Skip to content

Commit

Permalink
Add tokenManager.refresh (#35)
Browse files Browse the repository at this point in the history
Resolves: OKTA-94715
  • Loading branch information
lboyette-okta authored Jul 28, 2016
1 parent cde3065 commit be508d2
Show file tree
Hide file tree
Showing 10 changed files with 366 additions and 49 deletions.
44 changes: 41 additions & 3 deletions lib/TokenManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ var util = require('./util');
var AuthSdkError = require('./errors/AuthSdkError');
var cookies = require('./cookies');
var tokenStorageBuilder = require('./tokenStorageBuilder');
var Q = require('q');

// Provides webStorage-like interface for cookies
var cookieStorage = {
Expand All @@ -14,8 +15,11 @@ var cookieStorage = {

function add(storage, key, token) {
var tokenStorage = storage.getTokenStorage();
if (!util.isObject(token)) {
throw new AuthSdkError('Token must be an Object');
if (!util.isObject(token) ||
!token.scopes ||
(!token.expiresAt && token.expiresAt !== 0) ||
(!token.idToken && !token.accessToken)) {
throw new AuthSdkError('Token must be an Object with scopes, expiresAt, and an idToken or accessToken properties');
}
tokenStorage[key] = token;
storage.setTokenStorage(tokenStorage);
Expand All @@ -32,7 +36,40 @@ function remove(storage, key) {
storage.setTokenStorage(tokenStorage);
}

function TokenManager(options) {
function refresh(sdk, storage, key) {
try {
var token = get(storage, key);
if (!token) {
throw new AuthSdkError('The tokenManager has no token for the key: ' + key);
}
} catch (e) {
return Q.reject(e);
}

var responseType;
if (token.accessToken) {
responseType = 'token';
} else {
responseType = 'id_token';
}

return sdk.token.getWithoutPrompt({
responseType: responseType,
scopes: token.scopes
})
.then(function(freshToken) {
add(storage, key, freshToken);
return freshToken;
})
.fail(function(err) {
if (err.name === 'OAuthError') {
remove(storage, key);
}
throw err;
});
}

function TokenManager(sdk, options) {
options = options || {};
options.storage = options.storage || 'localStorage';

Expand All @@ -54,6 +91,7 @@ function TokenManager(options) {
this.add = util.bind(add, this, storage);
this.get = util.bind(get, this, storage);
this.remove = util.bind(remove, this, storage);
this.refresh = util.bind(refresh, this, sdk, storage);
}

module.exports = TokenManager;
2 changes: 1 addition & 1 deletion lib/clientBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ function OktaAuthBuilder(args) {
return window.location.hash;
};

sdk.tokenManager = new TokenManager(args.tokenManager);
sdk.tokenManager = new TokenManager(sdk, args.tokenManager);
}

var proto = OktaAuthBuilder.prototype;
Expand Down
19 changes: 13 additions & 6 deletions lib/token.js
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ function handleOAuthResponse(sdk, oauthParams, res) {
}

var tokenTypes = oauthParams.responseType;
var scopes = util.clone(oauthParams.scope);
var scopes = util.clone(oauthParams.scopes);
var tokenDict = {};

if (res['id_token']) {
Expand Down Expand Up @@ -321,14 +321,21 @@ function handleOAuthResponse(sdk, oauthParams, res) {

function getDefaultOAuthParams(sdk, oauthOptions) {
oauthOptions = util.clone(oauthOptions) || {};

if (oauthOptions.scope) {
util.deprecate('The param "scope" is equivalent to "scopes". Use "scopes" instead.');
oauthOptions.scopes = oauthOptions.scope;
delete oauthOptions.scope;
}

var defaults = {
clientId: sdk.options.clientId,
redirectUri: sdk.options.redirectUri || window.location.href,
responseType: 'id_token',
responseMode: 'okta_post_message',
state: util.genRandomString(64),
nonce: util.genRandomString(64),
scope: ['openid', 'email']
scopes: ['openid', 'email']
};
util.extend(defaults, oauthOptions);
return defaults;
Expand Down Expand Up @@ -364,10 +371,10 @@ function convertOAuthParamsToQueryParams(oauthParams) {
}

if (oauthParams.responseType.indexOf('id_token') !== -1 &&
oauthParams.scope.indexOf('openid') === -1) {
throw new AuthSdkError('openid scope must be specified in the scope argument when requesting an id_token');
oauthParams.scopes.indexOf('openid') === -1) {
throw new AuthSdkError('openid scope must be specified in the scopes argument when requesting an id_token');
} else {
oauthQueryParams.scope = oauthParams.scope.join(' ');
oauthQueryParams.scope = oauthParams.scopes.join(' ');
}

return oauthQueryParams;
Expand Down Expand Up @@ -586,7 +593,7 @@ function getWithRedirect(sdk, oauthOptions, options) {
responseType: oauthParams.responseType,
state: oauthParams.state,
nonce: oauthParams.nonce,
scope: oauthParams.scope
scopes: oauthParams.scopes
}));

sdk.token.getWithRedirect._setLocation(requestUrl);
Expand Down
9 changes: 8 additions & 1 deletion lib/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,12 @@ function getLink(obj, linkName, altName) {
}
}

function deprecate(text) {
/* eslint-disable no-console */
console.log('[okta-auth-sdk] DEPRECATION: ' + text);
/* eslint-enable */
}

module.exports = {
base64UrlToBase64: base64UrlToBase64,
base64UrlToString: base64UrlToString,
Expand All @@ -207,5 +213,6 @@ module.exports = {
clone: clone,
omit: omit,
find: find,
getLink: getLink
getLink: getLink,
deprecate: deprecate
};
22 changes: 11 additions & 11 deletions test/spec/general.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ define(function(require) {

setupVerifyIdTokenTest({
title: 'verifies a valid idToken',
idToken: tokens.standardIdToken,
idToken: tokens.verifiableIdToken,
expectations: function (test, res) {
expect(res).toEqual(true);
}
Expand Down Expand Up @@ -120,7 +120,7 @@ define(function(require) {

setupVerifyIdTokenTest({
title: 'rejects an invalid idToken due to expiration',
idToken: tokens.standardIdToken,
idToken: tokens.verifiableIdToken,
execute: function(test, opts) {
util.warpToDistantPast();
return test.oa.idToken.verify(opts.idToken, opts.verifyOpts)
Expand All @@ -137,7 +137,7 @@ define(function(require) {
setupVerifyIdTokenTest({
title: 'verifies an idToken that would be invalid, except ' +
'we\'re using the expirationTime option',
idToken: tokens.standardIdToken,
idToken: tokens.verifiableIdToken,
verifyOpts: {
expirationTime: 9999999999
},
Expand All @@ -156,7 +156,7 @@ define(function(require) {

setupVerifyIdTokenTest({
title: 'verifies a valid idToken using single audience option',
idToken: tokens.standardIdToken,
idToken: tokens.verifiableIdToken,
verifyOpts: {
audience: 'NPSfOkH5eZrTy8PMDlvx'
},
Expand All @@ -167,7 +167,7 @@ define(function(require) {

setupVerifyIdTokenTest({
title: 'rejects an invalid idToken using single audience option',
idToken: tokens.standardIdToken,
idToken: tokens.verifiableIdToken,
verifyOpts: {
audience: 'invalid'
},
Expand All @@ -178,7 +178,7 @@ define(function(require) {

setupVerifyIdTokenTest({
title: 'verifies a valid idToken using multiple audience option (all valid)',
idToken: tokens.standardIdToken,
idToken: tokens.verifiableIdToken,
verifyOpts: {
audience: ['NPSfOkH5eZrTy8PMDlvx', 'NPSfOkH5eZrTy8PMDlvx']
},
Expand All @@ -189,7 +189,7 @@ define(function(require) {

setupVerifyIdTokenTest({
title: 'verifies a valid idToken using multiple audience option (valid and invalid)',
idToken: tokens.standardIdToken,
idToken: tokens.verifiableIdToken,
verifyOpts: {
audience: ['NPSfOkH5eZrTy8PMDlvx', 'invalid2']
},
Expand All @@ -200,7 +200,7 @@ define(function(require) {

setupVerifyIdTokenTest({
title: 'rejects an invalid idToken using multiple audience option (all invalid)',
idToken: tokens.standardIdToken,
idToken: tokens.verifiableIdToken,
verifyOpts: {
audience: ['invalid1', 'invalid2']
},
Expand All @@ -211,9 +211,9 @@ define(function(require) {

setupVerifyIdTokenTest({
title: 'verifies a valid idToken using issuer option',
idToken: tokens.standardIdToken,
idToken: tokens.verifiableIdToken,
verifyOpts: {
issuer: 'https://auth-js-test.okta.com'
issuer: 'https://lboyette.trexcloud.com'
},
expectations: function (test, res) {
expect(res).toEqual(true);
Expand All @@ -222,7 +222,7 @@ define(function(require) {

setupVerifyIdTokenTest({
title: 'rejects an invalid idToken using issuer option',
idToken: tokens.standardIdToken,
idToken: tokens.verifiableIdToken,
verifyOpts: {
issuer: 'invalid'
},
Expand Down
8 changes: 4 additions & 4 deletions test/spec/oauth.js
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ define(function(require) {
authorizeArgs: {
clientId: 'NPSfOkH5eZrTy8PMDlvx',
redirectUri: 'https://auth-js-test.okta.com/redirect',
scope: ['openid', 'testscope'],
scopes: ['openid', 'testscope'],
sessionToken: 'testToken'
},
postMessageSrc: {
Expand Down Expand Up @@ -237,15 +237,15 @@ define(function(require) {
authorizeArgs: {
clientId: 'NPSfOkH5eZrTy8PMDlvx',
redirectUri: 'https://auth-js-test.okta.com/redirect',
scope: ['notopenid'],
scopes: ['notopenid'],
sessionToken: 'testToken'
}
},
{
name: 'AuthSdkError',
message: 'openid scope must be specified in the scope argument when requesting an id_token',
message: 'openid scope must be specified in the scopes argument when requesting an id_token',
errorCode: 'INTERNAL',
errorSummary: 'openid scope must be specified in the scope argument when requesting an id_token',
errorSummary: 'openid scope must be specified in the scopes argument when requesting an id_token',
errorLink: 'INTERNAL',
errorId: 'INTERNAL',
errorCauses: []
Expand Down
22 changes: 11 additions & 11 deletions test/spec/token.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ define(function(require) {
responseMode: 'okta_post_message',
state: 'bbbbbb',
nonce: 'cccccc',
scope: ['openid', 'custom'],
scopes: ['openid', 'custom'],
maxAge: 1469481630,
display: 'page' // will be forced to undefined
},
Expand Down Expand Up @@ -500,7 +500,7 @@ define(function(require) {
responseType: 'id_token',
state: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
nonce: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
scope: ['openid', 'email']
scopes: ['openid', 'email']
}) + ';',
expectedRedirectUrl: 'https://auth-js-test.okta.com/oauth2/v1/authorize?' +
'client_id=NPSfOkH5eZrTy8PMDlvx&' +
Expand All @@ -518,14 +518,14 @@ define(function(require) {
oauthUtil.setupRedirect({
getWithRedirectArgs: {
responseType: 'token',
scope: ['email'],
scopes: ['email'],
sessionToken: 'testToken'
},
expectedCookie: 'okta-oauth-redirect-params=' + JSON.stringify({
responseType: 'token',
state: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
nonce: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
scope: ['email']
scopes: ['email']
}) + ';',
expectedRedirectUrl: 'https://auth-js-test.okta.com/oauth2/v1/authorize?' +
'client_id=NPSfOkH5eZrTy8PMDlvx&' +
Expand All @@ -549,7 +549,7 @@ define(function(require) {
responseType: ['token', 'id_token'],
state: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
nonce: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
scope: ['openid', 'email']
scopes: ['openid', 'email']
}) + ';',
expectedRedirectUrl: 'https://auth-js-test.okta.com/oauth2/v1/authorize?' +
'client_id=NPSfOkH5eZrTy8PMDlvx&' +
Expand All @@ -573,7 +573,7 @@ define(function(require) {
responseType: 'id_token',
state: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
nonce: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
scope: ['openid', 'email']
scopes: ['openid', 'email']
}) + ';',
expectedResp: {
idToken: tokens.standardIdToken,
Expand All @@ -598,7 +598,7 @@ define(function(require) {
responseType: 'token',
state: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
nonce: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
scope: ['openid', 'email']
scopes: ['openid', 'email']
}) + ';',
expectedResp: {
accessToken: tokens.standardAccessToken,
Expand All @@ -624,7 +624,7 @@ define(function(require) {
responseType: ['id_token', 'token'],
state: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
nonce: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
scope: ['openid', 'email']
scopes: ['openid', 'email']
}) + ';',
expectedResp: [{
idToken: tokens.standardIdToken,
Expand All @@ -651,7 +651,7 @@ define(function(require) {
responseType: ['id_token', 'token'],
state: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
nonce: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
scope: ['openid', 'email']
scopes: ['openid', 'email']
}) + ';'
},
{
Expand Down Expand Up @@ -698,7 +698,7 @@ define(function(require) {
responseType: ['id_token', 'token'],
state: 'mismatchedState',
nonce: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
scope: ['openid', 'email']
scopes: ['openid', 'email']
}) + ';'
},
{
Expand All @@ -724,7 +724,7 @@ define(function(require) {
responseType: ['id_token', 'token'],
state: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
nonce: 'mismatchedNonce',
scope: ['openid', 'email']
scopes: ['openid', 'email']
}) + ';'
},
{
Expand Down
Loading

0 comments on commit be508d2

Please sign in to comment.