Skip to content

Add responseType code and password with token refresh interval #147

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
Open
Prev Previous commit
Next Next commit
response_type password in progress
  • Loading branch information
babelouest committed Feb 13, 2017
commit 71312a98cfe53eb2a6839a99a6c78f4196d024e3
59 changes: 43 additions & 16 deletions app/scripts/directives/oauth.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,13 @@ directives.directive('oauth', [
authorizePath: '@', // (optional) authorization url
tokenPath: '@', // (optional) token url
state: '@', // (optional) An arbitrary unique string created by your app to guard against Cross-site Request Forgery
storage: '@', // (optional) Store token in 'sessionStorage' or 'localStorage', defaults to 'sessionStorage'
nonce: '@', // (optional) Send nonce on auth request
// OpenID Connect extras, more details in id-token.js:
issuer: '@', // (optional for OpenID Connect) issuer of the id_token, should match the 'iss' claim in id_token payload
subject: '@', // (optional for OpenID Connect) subject of the id_token, should match the 'sub' claim in id_token payload
pubKey: '@', // (optional for OpenID Connect) the public key(RSA public key or X509 certificate in PEM format) to verify the signature
wellKnown: '@', // (optional for OpenID Connect) whether to load public key according to .well-known/openid-configuration endpoint
storage: '@', // (optional) Store token in 'sessionStorage' or 'localStorage', defaults to 'sessionStorage'
nonce: '@', // (optional) Send nonce on auth request
// OpenID Connect extras, more details in id-token.js:
issuer: '@', // (optional for OpenID Connect) issuer of the id_token, should match the 'iss' claim in id_token payload
subject: '@', // (optional for OpenID Connect) subject of the id_token, should match the 'sub' claim in id_token payload
pubKey: '@', // (optional for OpenID Connect) the public key(RSA public key or X509 certificate in PEM format) to verify the signature
wellKnown: '@', // (optional for OpenID Connect) whether to load public key according to .well-known/openid-configuration endpoint
logoutPath: '@', // (optional) A url to go to at logout
sessionPath: '@' // (optional) A url to use to check the validity of the current token.
}
Expand Down Expand Up @@ -70,14 +70,17 @@ directives.directive('oauth', [
};

var initAttributes = function() {
scope.authorizePath = scope.authorizePath || '/oauth/authorize';
scope.tokenPath = scope.tokenPath || '/oauth/token';
scope.template = scope.template || 'views/templates/default.html';
scope.responseType = scope.responseType || 'token';
scope.text = scope.text || 'Sign In';
scope.state = scope.state || undefined;
scope.scope = scope.scope || undefined;
scope.storage = scope.storage || 'sessionStorage';
scope.authorizePath = scope.authorizePath || '/oauth/authorize';
scope.tokenPath = scope.tokenPath || '/oauth/token';
scope.template = scope.template || 'views/templates/default.html';
scope.responseType = scope.responseType || 'token';
scope.text = scope.text || 'Sign In';
scope.state = scope.state || undefined;
scope.scope = scope.scope || undefined;
scope.storage = scope.storage || 'sessionStorage';
scope.typedLogin = "";
scope.typedPassword = "";
scope.typedKeepConnection = false;
};

var compile = function() {
Expand All @@ -100,7 +103,7 @@ directives.directive('oauth', [
var initView = function () {
var token = AccessToken.get();

if (!token) {
if (!token && scope.responseType !== "password") {
return scope.login();
} // without access token it's logged out, so we attempt to log in
if (AccessToken.expired()) {
Expand All @@ -123,6 +126,28 @@ directives.directive('oauth', [
$rootScope.$broadcast('oauth:loggedOut');
scope.show = 'logged-out';
};

scope.checkPassword = function () {
$http({
method: "POST",
url: scope.site + scope.tokenPath,
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
transformRequest: function(obj) {
var str = [];
for(var p in obj)
str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
return str.join("&");
},
data: {grant_type: "password", username: scope.typedLogin, password: scope.typedPassword, scope: scope.scope}
}).then(function (result) {
AccessToken.set(result.data, scope.typedLogin, scope.typedPassword, scope.scope).then(function () { // sets the access token object (if existing, from fragment or session)
});
$rootScope.$broadcast('oauth:login', result.data);
scope.show = "logged-in";
}, function () {
$rootScope.$broadcast('oauth:denied');
});
};

scope.$on('oauth:expired',expired);

Expand Down Expand Up @@ -169,6 +194,8 @@ directives.directive('oauth', [
scope.$on('$stateChangeSuccess', function () {
$timeout(refreshDirective);
});


};

return definition;
Expand Down
44 changes: 40 additions & 4 deletions app/scripts/services/access-token.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ var accessTokenService = angular.module('oauth.accessToken', []);
accessTokenService.factory('AccessToken', ['Storage', '$rootScope', '$http', '$q', '$location', '$interval', '$timeout', 'IdToken', function(Storage, $rootScope, $http, $q, $location, $interval, $timeout, IdToken){

var service = {
token: null
token: null,
typedLogin: "",
typedPassword: "",
scope: ""
},
hashFragmentKeys = [
//Oauth2 keys per http://tools.ietf.org/html/rfc6749#section-4.2.2
Expand All @@ -29,7 +32,12 @@ accessTokenService.factory('AccessToken', ['Storage', '$rootScope', '$http', '$q
* - takes the token from the fragment URI
* - takes the token from the sessionStorage
*/
service.set = function(scope) {
service.set = function(scope, typedLogin, typedPassword, oauthScope) {
if (typedLogin && typedPassword) {
service.typedLogin = typedLogin;
service.typedPassword = typedPassword;
service.scope = oauthScope;
}
refreshTokenUri = scope.site + scope.tokenPath;
if ($location.search().code) {
return this.setTokenFromCode($location.search(), scope);
Expand Down Expand Up @@ -129,11 +137,15 @@ accessTokenService.factory('AccessToken', ['Storage', '$rootScope', '$http', '$q
if (!params.refresh_token) {
var deferred = $q.defer();
deferred.resolve(params);
$rootScope.$broadcast('oauth:login', token);
$rootScope.$broadcast('oauth:login', params);
return deferred.promise;
} else {
return refreshToken();
}
} else {
var deferred = $q.defer();
deferred.reject();
return deferred.promise;
}
};

Expand All @@ -153,7 +165,31 @@ accessTokenService.factory('AccessToken', ['Storage', '$rootScope', '$http', '$q
setToken(result.data);
$rootScope.$broadcast('oauth:login', service.token);
}, function () {
$rootScope.$broadcast('oauth:expired', service.token);
if (service.typedLogin && service.typedPassword) {
return reconnect();
} else {
$rootScope.$broadcast('oauth:expired', service.token);
}
});
};

var reconnect = function () {
return $http({
method: "POST",
url: refreshTokenUri,
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
transformRequest: function(obj) {
var str = [];
for(var p in obj)
str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
return str.join("&");
},
data: {grant_type: "password", username: service.typedLogin, password: service.typedPassword, scope: service.scope}
}).then(function (result) {
setToken(result.data);
$rootScope.$broadcast('oauth:login', service.token);
}, function () {
$rootScope.$broadcast('oauth:denied');
});
};

Expand Down
26 changes: 23 additions & 3 deletions app/views/templates/button.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,25 @@
<span class="oauth">
<a href="#" class="logged-out" ng-show="show=='logged-out'" ng-click="login()">Login Button</a>
<a href="#" class="logged-in" ng-show="show=='logged-in'" ng-click="logout()">Logout {{profile.email}}</a>
<a href="#" class="denied" ng-show="show=='denied'" ng-click="login()">Access denied.</a>
<a href="#" class="logged-out" data-ng-show="show==='logged-out'" data-ng-click="login()">Login Button</a>
<a href="#" class="logged-in" data-ng-show="responseType !== 'password' && show==='logged-in'" data-ng-click="logout()">Logout {{profile.email}}</a>
<a href="#" class="denied" data-ng-show="responseType !== 'password' && show==='denied'" data-ng-click="login()">Access denied.</a>
</span>

<!-- Login/password form for response_type 'password' -->

<div data-ng-show="responseType === 'password' && show !== 'logged-in'">
<form>
<div>
<label>Login</label>
<input type="text" data-ng-model="typedLogin" placeholder="login" >
</div>
<div>
<label>Password</label>
<input type="password" data-ng-model="typedPassword" placeholder="password" >
</div>
<div>
<label>Keep connection</label>
<input type="checkbox" data-ng-model="typedKeepConnection" >
</div>
<input type="submit" value="Submit" data-ng-click="checkPassword()">
</form>
</div>
26 changes: 23 additions & 3 deletions app/views/templates/default.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,25 @@
<a class="oauth">
<span href="#" class="logged-out" ng-show="show=='logged-out'" ng-click="login()" >{{text}}</span>
<span href="#" class="logged-in" ng-show="show=='logged-in'" ng-click="logout()">Logout {{profile.email}}</span>
<span href="#" class="denied" ng-show="show=='denied'" ng-click="login()">Access denied. Try again.</span>
<span href="#" class="logged-out" data-ng-show="show==='logged-out'" data-ng-click="login()" >{{text}}</span>
<span href="#" class="logged-in" data-ng-show="responseType !== 'password' && show==='logged-in'" data-ng-click="logout()">Logout {{profile.email}}</span>
<span href="#" class="denied" data-ng-show="responseType !== 'password' && show==='denied'" data-ng-click="login()">Access denied. Try again.</span>
</a>

<!-- Login/password form for response_type 'password' -->

<div data-ng-show="responseType === 'password' && show !== 'logged-in'">
<form>
<div>
<label>Login</label>
<input type="text" data-ng-model="typedLogin" placeholder="login" >
</div>
<div>
<label>Password</label>
<input type="password" data-ng-model="typedPassword" placeholder="password" >
</div>
<div>
<label>Keep connection</label>
<input type="checkbox" data-ng-model="typedKeepConnection" >
</div>
<input type="submit" value="Submit" data-ng-click="checkPassword()">
</form>
</div>
Loading