Socket
Socket
Sign inDemoInstall

@ndustrial/contxt-sdk

Package Overview
Dependencies
Maintainers
14
Versions
123
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@ndustrial/contxt-sdk - npm Package Compare versions

Comparing version 1.0.0-beta.6 to 1.0.0-beta.7

3

CHANGELOG.md

@@ -15,3 +15,6 @@ ## [v1.0.0](http://github.com/ndustrialio/contxt-sdk-js/tree/v1.0.0) (2019-xx-xx)

- Changed back to using UUIDv4s as IDs for Message Bus subscriptions and publications.
- Refactored Auth0WebAuth to better handle access tokens provided by contxt-auth. Each API/audience now gets its own token instead of getting one big token that contained every possible API/audience combination.
- If using `Auth#getCurrentApiToken` (especially if not passing in the audience name/API name), pay extra attention to this update. The output has the same format, but the information that should be expected in the token is slightly different (there will be less information).
## [v0.0.49](http://github.com/ndustrialio/contxt-sdk-js/tree/v0.0.49) (2019-04-22)

@@ -18,0 +21,0 @@

26

docs/Auth0WebAuth.md

@@ -23,4 +23,3 @@ <a name="Auth0WebAuth"></a>

* [new Auth0WebAuth(sdk)](#new_Auth0WebAuth_new)
* [.getCurrentAccessToken()](#Auth0WebAuth+getCurrentAccessToken) ⇒ <code>Promise</code>
* [.getCurrentApiToken()](#Auth0WebAuth+getCurrentApiToken) ⇒ <code>Promise</code>
* [.getCurrentApiToken(audienceName)](#Auth0WebAuth+getCurrentApiToken) ⇒ <code>Promise</code>
* [.getProfile()](#Auth0WebAuth+getProfile) ⇒ <code>Promise</code>

@@ -63,16 +62,14 @@ * [.handleAuthentication()](#Auth0WebAuth+handleAuthentication) ⇒ <code>Promise</code>

```
<a name="Auth0WebAuth+getCurrentAccessToken"></a>
<a name="Auth0WebAuth+getCurrentApiToken"></a>
### contxtSdk.auth.getCurrentAccessToken() ⇒ <code>Promise</code>
Gets the current access token (used to communicate with Auth0 & Contxt Auth)
### contxtSdk.auth.getCurrentApiToken(audienceName) ⇒ <code>Promise</code>
Requests an access token from Contxt Auth for the correct audience
**Kind**: instance method of [<code>Auth0WebAuth</code>](#Auth0WebAuth)
**Fulfills**: <code>string</code> accessToken
<a name="Auth0WebAuth+getCurrentApiToken"></a>
**Fulfills**: <code>string</code> apiToken
### contxtSdk.auth.getCurrentApiToken() ⇒ <code>Promise</code>
Gets the current API token (used to communicate with other Contxt APIs)
| Param |
| --- |
| audienceName |
**Kind**: instance method of [<code>Auth0WebAuth</code>](#Auth0WebAuth)
**Fulfills**: <code>string</code> apiToken
<a name="Auth0WebAuth+getProfile"></a>

@@ -89,3 +86,4 @@

### contxtSdk.auth.handleAuthentication() ⇒ <code>Promise</code>
Routine that takes unparsed information from Auth0, uses it to get a valid API token, and then
Routine that takes unparsed information from Auth0, stores it in a way that
can be used for getting access tokens, schedules its future renewal, and
redirects to the correct page in the application.

@@ -111,5 +109,5 @@

### contxtSdk.auth.logOut()
Logs the user out by removing any stored session info, clearing any token renewal, and
redirecting to the root
Logs the user out by removing any stored session info, clearing any token
renewal, and redirecting to the root
**Kind**: instance method of [<code>Auth0WebAuth</code>](#Auth0WebAuth)

@@ -205,3 +205,2 @@ <a name="Asset"></a>

| accessToken | <code>string</code> |
| apiToken | <code>string</code> |
| expiresAt | <code>number</code> |

@@ -208,0 +207,0 @@

@@ -1,3 +0,1 @@

var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };

@@ -25,3 +23,2 @@

* @property {string} accessToken
* @property {string} apiToken
* @property {number} expiresAt

@@ -90,3 +87,5 @@ */

this._onRedirect = this._sdk.config.auth.onRedirect || this._defaultOnRedirect;
this._sessionInfo = this._loadSession();
this._sessionInfo = this._getStoredSession();
this._sessionRenewalTimeout = null;
this._tokenPromises = {};

@@ -106,3 +105,3 @@ var currentUrl = new URL(window.location);

if (this.isAuthenticated()) {
this._scheduleTokenRenewal();
this._scheduleSessionRefresh();
}

@@ -112,6 +111,8 @@ }

/**
* Gets the current access token (used to communicate with Auth0 & Contxt Auth)
* Requests an access token from Contxt Auth for the correct audience
*
* @param audienceName
*
* @returns {Promise}
* @fulfills {string} accessToken
* @fulfills {string} apiToken
*/

@@ -121,18 +122,29 @@

_createClass(Auth0WebAuth, [{
key: 'getCurrentAccessToken',
value: function getCurrentAccessToken() {
return this._getCurrentTokenByType('access');
}
key: 'getCurrentApiToken',
value: function getCurrentApiToken(audienceName) {
if (!this.isAuthenticated()) {
return Promise.reject(this._generateUnauthorizedError());
}
/**
* Gets the current API token (used to communicate with other Contxt APIs)
*
* @returns {Promise}
* @fulfills {string} apiToken
*/
var audience = this._sdk.config.audiences[audienceName];
}, {
key: 'getCurrentApiToken',
value: function getCurrentApiToken() {
return this._getCurrentTokenByType('api');
if (!(audience && audience.clientId)) {
return Promise.reject(new Error('No valid audience found'));
}
if (!this._tokenPromises[audienceName]) {
this._tokenPromises[audienceName] = axios.post(this._sdk.config.audiences.contxtAuth.host + '/v1/token', {
audiences: [audience.clientId],
nonce: 'nonce'
}, {
headers: {
Authorization: 'Bearer ' + this._sessionInfo.accessToken
}
}).then(function (_ref) {
var data = _ref.data;
return data.access_token;
});
}
return this._tokenPromises[audienceName];
}

@@ -153,16 +165,14 @@

return this.getCurrentAccessToken().then(function (accessToken) {
return new Promise(function (resolve, reject) {
_this._auth0.client.userInfo(accessToken, function (err, profile) {
if (err) {
reject(err);
}
return new Promise(function (resolve, reject) {
_this._auth0.client.userInfo(_this._sessionInfo.accessToken, function (err, profile) {
if (err) {
reject(err);
}
var formattedProfile = _extends({}, profile, {
updatedAt: profile.updated_at
});
delete formattedProfile.updated_at;
var formattedProfile = _extends({}, profile, {
updatedAt: profile.updated_at
});
delete formattedProfile.updated_at;
resolve(formattedProfile);
});
resolve(formattedProfile);
});

@@ -173,3 +183,4 @@ });

/**
* Routine that takes unparsed information from Auth0, uses it to get a valid API token, and then
* Routine that takes unparsed information from Auth0, stores it in a way that
* can be used for getting access tokens, schedules its future renewal, and
* redirects to the correct page in the application.

@@ -187,10 +198,8 @@ *

return this._parseWebAuthHash().then(function (hash) {
return _this2._handleNewSessionInfo(hash);
}).then(function (sessionInfo) {
return this._parseHash().then(function (authResult) {
_this2._storeSession(authResult);
_this2._scheduleSessionRefresh();
var redirectPathname = _this2._getRedirectPathname();
_this2._onRedirect(redirectPathname);
return sessionInfo;
}).catch(function (err) {

@@ -214,5 +223,3 @@ console.log('Error while handling authentication: ' + err);

value: function isAuthenticated() {
var hasTokens = !!(this._sessionInfo && this._sessionInfo.accessToken && this._sessionInfo.apiToken && this._sessionInfo.expiresAt);
return hasTokens;
return !!(this._sessionInfo && this._sessionInfo.accessToken && this._sessionInfo.expiresAt > Date.now());
}

@@ -231,4 +238,4 @@

/**
* Logs the user out by removing any stored session info, clearing any token renewal, and
* redirecting to the root
* Logs the user out by removing any stored session info, clearing any token
* renewal, and redirecting to the root
*/

@@ -240,15 +247,15 @@

this._sessionInfo = {};
this._tokenPromises = {};
localStorage.removeItem('access_token');
localStorage.removeItem('api_token');
localStorage.removeItem('expires_at');
clearTimeout(this._tokenRenewalTimeout);
clearTimeout(this._sessionRenewalTimeout);
this._onRedirect('/');
this._auth0.logout({ returnTo: new URL(window.location).origin });
}
/**
* Wraps Auth0's `checkSession` method. Will check if the current Auth0 session is valid and get
* updated information if needed
* Wraps Auth0's `checkSession` method. Will check if the current Auth0
* session is valid and get updated information if needed
*

@@ -280,4 +287,4 @@ * @fulfill {Object} sessionResponse Information returned from Auth0

/**
* Default method used for redirecting around the web application. Overridden by `onRedirect` in
* the auth config
* Default method used for redirecting around the web application. Overridden
* by `onRedirect` in the auth config
*

@@ -294,7 +301,5 @@ * @private

/**
* Requests an access token from Contxt Auth with the correct audiences.
* Grabs a stored redirect pathname that may have been stored in another part
* of the web application
*
* @returns {Promise}
* @fulfill {string} accessToken
*
* @private

@@ -304,31 +309,17 @@ */

}, {
key: '_getApiToken',
value: function _getApiToken(accessToken) {
var _this4 = this;
key: '_getRedirectPathname',
value: function _getRedirectPathname() {
var redirectPathname = localStorage.getItem('redirect_pathname');
localStorage.removeItem('redirect_pathname');
return axios.post(this._sdk.config.audiences.contxtAuth.host + '/v1/token', {
audiences: Object.keys(this._sdk.config.audiences).map(function (audienceName) {
return _this4._sdk.config.audiences[audienceName].clientId;
}).filter(function (clientId) {
return clientId && clientId !== _this4._sdk.config.audiences.contxtAuth.clientId;
}),
nonce: 'nonce'
}, {
headers: { Authorization: 'Bearer ' + accessToken }
}).then(function (_ref) {
var data = _ref.data;
return data.access_token;
});
return redirectPathname || '/';
}
/**
* Returns the type of token requested if it exists. Will get an updated token if the existing
* tokens have expired.
* Loads a saved session from local storage
*
* @param {string} type The type of token requested. Only valid types are `access` and `api`
* @returns {Object} session
* @returns {string} session.accessToken
* @returns {number} session.expiresAt
*
* @returns {Promise}
* @fulfills {string} token
* @rejects {Error}
*
* @private

@@ -338,39 +329,11 @@ */

}, {
key: '_getCurrentTokenByType',
value: function _getCurrentTokenByType(type) {
var propertyName = type + 'Token';
var tokenExpiresAtBufferMs = this._sdk.config.auth.tokenExpiresAtBufferMs || 0;
var bufferedExpiresAt = this._sessionInfo.expiresAt - tokenExpiresAtBufferMs;
var needsNewToken = Date.now() > bufferedExpiresAt;
if (!needsNewToken && this._sessionInfo[propertyName]) {
return Promise.resolve(this._sessionInfo[propertyName]);
}
return this._getUpdatedSessionInfo().then(function (sessionInfo) {
if (!sessionInfo[propertyName]) {
throw new Error('No ' + type + ' token found');
}
return sessionInfo[propertyName];
});
key: '_getStoredSession',
value: function _getStoredSession() {
return {
accessToken: localStorage.getItem('access_token'),
expiresAt: JSON.parse(localStorage.getItem('expires_at'))
};
}
/**
* Grabs a stored redirect pathname that may have been stored in another part of the
* web application
*
* @private
*/
}, {
key: '_getRedirectPathname',
value: function _getRedirectPathname() {
var redirectPathname = localStorage.getItem('redirect_pathname');
localStorage.removeItem('redirect_pathname');
return redirectPathname || '/';
}
/**
* Gets up to date session info. Will get an updated session/tokens if the

@@ -381,3 +344,2 @@ * previous session has already expired. Will log the user out if an error

* @returns {Promise}
* @fulfills {Auth0WebAuthSessionInfo} sessionInfo
* @rejects {Error}

@@ -389,97 +351,51 @@ *

}, {
key: '_getUpdatedSessionInfo',
value: function _getUpdatedSessionInfo() {
var _this5 = this;
key: '_getUpdatedSession',
value: function _getUpdatedSession() {
var _this4 = this;
if (!this._updatedSessionInfoPromise) {
this._updatedSessionInfoPromise = this._checkSession().then(function (sessionInfo) {
return _this5._handleNewSessionInfo(sessionInfo);
}).then(function (sessionInfo) {
_this5._updatedSessionInfoPromise = null;
return this._checkSession().then(function (sessionInfo) {
_this4._storeSession(sessionInfo);
return sessionInfo;
}).catch(function (err) {
var errorToThrow = err;
_this4._tokenPromises = {};
if (err.error && ['consent_required', 'interaction_required', 'login_required'].indexOf(err.error) > -1) {
errorToThrow = new Error('Unauthorized');
errorToThrow.response = {
data: _extends({}, err, {
code: 401
}),
status: 401
};
_this4._scheduleSessionRefresh();
}).catch(function (err) {
var errorToThrow = err;
_this5.logOut();
} else if (!(err.response && err.response.status)) {
errorToThrow = new Error('There was a problem getting new session info. Please check your configuration settings.');
errorToThrow.fromSdk = true;
errorToThrow.originalError = err;
}
if (err.error && ['consent_required', 'interaction_required', 'login_required'].indexOf(err.error) > -1) {
errorToThrow = _this4._generateUnauthorizedError(err);
throw errorToThrow;
});
}
_this4.logOut();
} else if (!(err.response && err.response.status)) {
errorToThrow = new Error('There was a problem getting new session info. Please check your configuration settings.');
errorToThrow.fromSdk = true;
errorToThrow.originalError = err;
}
return this._updatedSessionInfoPromise;
throw errorToThrow;
});
}
/**
* Takes Auth0 Session Info, retrieves a Contxt API token, packages them up together, saves the
* tokens, and schedules a renewal.
*
* @returns {Promise}
* @fulfills {Auth0WebAuthSessionInfo} sessionInfo
*
* @private
*/
}, {
key: '_handleNewSessionInfo',
value: function _handleNewSessionInfo(sessionResponse) {
var _this6 = this;
key: '_generateUnauthorizedError',
value: function _generateUnauthorizedError(err) {
var error = new Error('Unauthorized');
return Promise.all([{
accessToken: sessionResponse.accessToken,
expiresAt: sessionResponse.expiresIn * 1000 + Date.now()
}, this._getApiToken(sessionResponse.accessToken)]).then(function (_ref2) {
var _ref3 = _slicedToArray(_ref2, 2),
partialSessionInfo = _ref3[0],
apiToken = _ref3[1];
if (!err) {
error.fromSdk = true;
}
var sessionInfo = _extends({}, partialSessionInfo, {
apiToken: apiToken
});
error.response = {
data: _extends({}, err, {
code: 401
}),
status: 401
};
_this6._saveSession(sessionInfo);
_this6._scheduleTokenRenewal();
return sessionInfo;
});
return error;
}
/**
* Loads a saved session from local storage
* Wraps Auth0's method for parsing hash information after a successful
* authentication.
*
* @returns {Object} session
* @returns {string} session.accessToken
* @returns {string} session.apiToken
* @returns {number} session.expiresAt
*
* @private
*/
}, {
key: '_loadSession',
value: function _loadSession() {
return {
accessToken: localStorage.getItem('access_token'),
apiToken: localStorage.getItem('api_token'),
expiresAt: JSON.parse(localStorage.getItem('expires_at'))
};
}
/**
* Wraps Auth0's method for parsing hash information after a successful authentication.
*
* @returns {Promise}

@@ -493,13 +409,13 @@ * @fulfill {Object} hashResponse Information returned from Auth0

}, {
key: '_parseWebAuthHash',
value: function _parseWebAuthHash() {
var _this7 = this;
key: '_parseHash',
value: function _parseHash() {
var _this5 = this;
return new Promise(function (resolve, reject) {
_this7._auth0.parseHash(function (err, hashResponse) {
if (err || !hashResponse) {
_this5._auth0.parseHash(function (err, authResult) {
if (err || !authResult) {
return reject(err || new Error('No valid tokens returned from auth0'));
}
return resolve(hashResponse);
return resolve(authResult);
});

@@ -510,9 +426,4 @@ });

/**
* Saves a session in local storage for future use
* Schedules the Access token to renew before they expire
*
* @param {Object} sessionInfo
* @param {string} sessionInfo.accessToken
* @param {string} sessionInfo.apiToken
* @param {number} sessionInfo.expiresAt
*
* @private

@@ -522,14 +433,26 @@ */

}, {
key: '_saveSession',
value: function _saveSession(sessionInfo) {
this._sessionInfo = sessionInfo;
key: '_scheduleSessionRefresh',
value: function _scheduleSessionRefresh() {
var _this6 = this;
localStorage.setItem('access_token', sessionInfo.accessToken);
localStorage.setItem('api_token', sessionInfo.apiToken);
localStorage.setItem('expires_at', JSON.stringify(sessionInfo.expiresAt));
var tokenExpiresAtBufferMs = this._sdk.config.auth.tokenExpiresAtBufferMs || 0;
var bufferedExpiresAt = this._sessionInfo.expiresAt - tokenExpiresAtBufferMs;
var delay = bufferedExpiresAt - Date.now();
if (this._sessionRenewalTimeout) {
clearTimeout(this._sessionRenewalTimeout);
}
this._sessionRenewalTimeout = setTimeout(function () {
_this6._getUpdatedSession();
}, delay);
}
/**
* Schedules the Access and API tokens to renew before they expire
* Saves a session in local storage for future use
*
* @param {Object} sessionInfo
* @param {string} sessionInfo.accessToken
* @param {number} sessionInfo.expiresAt
*
* @private

@@ -539,17 +462,14 @@ */

}, {
key: '_scheduleTokenRenewal',
value: function _scheduleTokenRenewal() {
var _this8 = this;
key: '_storeSession',
value: function _storeSession(_ref2) {
var accessToken = _ref2.accessToken,
expiresIn = _ref2.expiresIn;
var tokenExpiresAtBufferMs = this._sdk.config.auth.tokenExpiresAtBufferMs || 0;
var bufferedExpiresAt = this._sessionInfo.expiresAt - tokenExpiresAtBufferMs;
var delay = bufferedExpiresAt - Date.now();
var expiresAt = expiresIn * 1000 + Date.now();
if (this._tokenRenewalTimeout) {
clearTimeout(this._tokenRenewalTimeout);
}
localStorage.setItem('access_token', accessToken);
localStorage.setItem('expires_at', JSON.stringify(expiresAt));
this._tokenRenewalTimeout = setTimeout(function () {
_this8._getUpdatedSessionInfo();
}, delay);
this._sessionInfo.accessToken = accessToken;
this._sessionInfo.expiresAt = expiresAt;
}

@@ -556,0 +476,0 @@ }]);

@@ -8,4 +8,2 @@ 'use strict';

var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };

@@ -43,3 +41,2 @@

* @property {string} accessToken
* @property {string} apiToken
* @property {number} expiresAt

@@ -107,3 +104,5 @@ */

this._onRedirect = this._sdk.config.auth.onRedirect || this._defaultOnRedirect;
this._sessionInfo = this._loadSession();
this._sessionInfo = this._getStoredSession();
this._sessionRenewalTimeout = null;
this._tokenPromises = {};

@@ -123,3 +122,3 @@ var currentUrl = new _urlParse2.default(window.location);

if (this.isAuthenticated()) {
this._scheduleTokenRenewal();
this._scheduleSessionRefresh();
}

@@ -129,6 +128,8 @@ }

/**
* Gets the current access token (used to communicate with Auth0 & Contxt Auth)
* Requests an access token from Contxt Auth for the correct audience
*
* @param audienceName
*
* @returns {Promise}
* @fulfills {string} accessToken
* @fulfills {string} apiToken
*/

@@ -138,18 +139,29 @@

_createClass(Auth0WebAuth, [{
key: 'getCurrentAccessToken',
value: function getCurrentAccessToken() {
return this._getCurrentTokenByType('access');
}
key: 'getCurrentApiToken',
value: function getCurrentApiToken(audienceName) {
if (!this.isAuthenticated()) {
return Promise.reject(this._generateUnauthorizedError());
}
/**
* Gets the current API token (used to communicate with other Contxt APIs)
*
* @returns {Promise}
* @fulfills {string} apiToken
*/
var audience = this._sdk.config.audiences[audienceName];
}, {
key: 'getCurrentApiToken',
value: function getCurrentApiToken() {
return this._getCurrentTokenByType('api');
if (!(audience && audience.clientId)) {
return Promise.reject(new Error('No valid audience found'));
}
if (!this._tokenPromises[audienceName]) {
this._tokenPromises[audienceName] = _axios2.default.post(this._sdk.config.audiences.contxtAuth.host + '/v1/token', {
audiences: [audience.clientId],
nonce: 'nonce'
}, {
headers: {
Authorization: 'Bearer ' + this._sessionInfo.accessToken
}
}).then(function (_ref) {
var data = _ref.data;
return data.access_token;
});
}
return this._tokenPromises[audienceName];
}

@@ -170,16 +182,14 @@

return this.getCurrentAccessToken().then(function (accessToken) {
return new Promise(function (resolve, reject) {
_this._auth0.client.userInfo(accessToken, function (err, profile) {
if (err) {
reject(err);
}
return new Promise(function (resolve, reject) {
_this._auth0.client.userInfo(_this._sessionInfo.accessToken, function (err, profile) {
if (err) {
reject(err);
}
var formattedProfile = _extends({}, profile, {
updatedAt: profile.updated_at
});
delete formattedProfile.updated_at;
var formattedProfile = _extends({}, profile, {
updatedAt: profile.updated_at
});
delete formattedProfile.updated_at;
resolve(formattedProfile);
});
resolve(formattedProfile);
});

@@ -190,3 +200,4 @@ });

/**
* Routine that takes unparsed information from Auth0, uses it to get a valid API token, and then
* Routine that takes unparsed information from Auth0, stores it in a way that
* can be used for getting access tokens, schedules its future renewal, and
* redirects to the correct page in the application.

@@ -204,10 +215,8 @@ *

return this._parseWebAuthHash().then(function (hash) {
return _this2._handleNewSessionInfo(hash);
}).then(function (sessionInfo) {
return this._parseHash().then(function (authResult) {
_this2._storeSession(authResult);
_this2._scheduleSessionRefresh();
var redirectPathname = _this2._getRedirectPathname();
_this2._onRedirect(redirectPathname);
return sessionInfo;
}).catch(function (err) {

@@ -231,5 +240,3 @@ console.log('Error while handling authentication: ' + err);

value: function isAuthenticated() {
var hasTokens = !!(this._sessionInfo && this._sessionInfo.accessToken && this._sessionInfo.apiToken && this._sessionInfo.expiresAt);
return hasTokens;
return !!(this._sessionInfo && this._sessionInfo.accessToken && this._sessionInfo.expiresAt > Date.now());
}

@@ -248,4 +255,4 @@

/**
* Logs the user out by removing any stored session info, clearing any token renewal, and
* redirecting to the root
* Logs the user out by removing any stored session info, clearing any token
* renewal, and redirecting to the root
*/

@@ -257,15 +264,15 @@

this._sessionInfo = {};
this._tokenPromises = {};
localStorage.removeItem('access_token');
localStorage.removeItem('api_token');
localStorage.removeItem('expires_at');
clearTimeout(this._tokenRenewalTimeout);
clearTimeout(this._sessionRenewalTimeout);
this._onRedirect('/');
this._auth0.logout({ returnTo: new _urlParse2.default(window.location).origin });
}
/**
* Wraps Auth0's `checkSession` method. Will check if the current Auth0 session is valid and get
* updated information if needed
* Wraps Auth0's `checkSession` method. Will check if the current Auth0
* session is valid and get updated information if needed
*

@@ -297,4 +304,4 @@ * @fulfill {Object} sessionResponse Information returned from Auth0

/**
* Default method used for redirecting around the web application. Overridden by `onRedirect` in
* the auth config
* Default method used for redirecting around the web application. Overridden
* by `onRedirect` in the auth config
*

@@ -311,7 +318,5 @@ * @private

/**
* Requests an access token from Contxt Auth with the correct audiences.
* Grabs a stored redirect pathname that may have been stored in another part
* of the web application
*
* @returns {Promise}
* @fulfill {string} accessToken
*
* @private

@@ -321,31 +326,17 @@ */

}, {
key: '_getApiToken',
value: function _getApiToken(accessToken) {
var _this4 = this;
key: '_getRedirectPathname',
value: function _getRedirectPathname() {
var redirectPathname = localStorage.getItem('redirect_pathname');
localStorage.removeItem('redirect_pathname');
return _axios2.default.post(this._sdk.config.audiences.contxtAuth.host + '/v1/token', {
audiences: Object.keys(this._sdk.config.audiences).map(function (audienceName) {
return _this4._sdk.config.audiences[audienceName].clientId;
}).filter(function (clientId) {
return clientId && clientId !== _this4._sdk.config.audiences.contxtAuth.clientId;
}),
nonce: 'nonce'
}, {
headers: { Authorization: 'Bearer ' + accessToken }
}).then(function (_ref) {
var data = _ref.data;
return data.access_token;
});
return redirectPathname || '/';
}
/**
* Returns the type of token requested if it exists. Will get an updated token if the existing
* tokens have expired.
* Loads a saved session from local storage
*
* @param {string} type The type of token requested. Only valid types are `access` and `api`
* @returns {Object} session
* @returns {string} session.accessToken
* @returns {number} session.expiresAt
*
* @returns {Promise}
* @fulfills {string} token
* @rejects {Error}
*
* @private

@@ -355,39 +346,11 @@ */

}, {
key: '_getCurrentTokenByType',
value: function _getCurrentTokenByType(type) {
var propertyName = type + 'Token';
var tokenExpiresAtBufferMs = this._sdk.config.auth.tokenExpiresAtBufferMs || 0;
var bufferedExpiresAt = this._sessionInfo.expiresAt - tokenExpiresAtBufferMs;
var needsNewToken = Date.now() > bufferedExpiresAt;
if (!needsNewToken && this._sessionInfo[propertyName]) {
return Promise.resolve(this._sessionInfo[propertyName]);
}
return this._getUpdatedSessionInfo().then(function (sessionInfo) {
if (!sessionInfo[propertyName]) {
throw new Error('No ' + type + ' token found');
}
return sessionInfo[propertyName];
});
key: '_getStoredSession',
value: function _getStoredSession() {
return {
accessToken: localStorage.getItem('access_token'),
expiresAt: JSON.parse(localStorage.getItem('expires_at'))
};
}
/**
* Grabs a stored redirect pathname that may have been stored in another part of the
* web application
*
* @private
*/
}, {
key: '_getRedirectPathname',
value: function _getRedirectPathname() {
var redirectPathname = localStorage.getItem('redirect_pathname');
localStorage.removeItem('redirect_pathname');
return redirectPathname || '/';
}
/**
* Gets up to date session info. Will get an updated session/tokens if the

@@ -398,3 +361,2 @@ * previous session has already expired. Will log the user out if an error

* @returns {Promise}
* @fulfills {Auth0WebAuthSessionInfo} sessionInfo
* @rejects {Error}

@@ -406,97 +368,51 @@ *

}, {
key: '_getUpdatedSessionInfo',
value: function _getUpdatedSessionInfo() {
var _this5 = this;
key: '_getUpdatedSession',
value: function _getUpdatedSession() {
var _this4 = this;
if (!this._updatedSessionInfoPromise) {
this._updatedSessionInfoPromise = this._checkSession().then(function (sessionInfo) {
return _this5._handleNewSessionInfo(sessionInfo);
}).then(function (sessionInfo) {
_this5._updatedSessionInfoPromise = null;
return this._checkSession().then(function (sessionInfo) {
_this4._storeSession(sessionInfo);
return sessionInfo;
}).catch(function (err) {
var errorToThrow = err;
_this4._tokenPromises = {};
if (err.error && ['consent_required', 'interaction_required', 'login_required'].indexOf(err.error) > -1) {
errorToThrow = new Error('Unauthorized');
errorToThrow.response = {
data: _extends({}, err, {
code: 401
}),
status: 401
};
_this4._scheduleSessionRefresh();
}).catch(function (err) {
var errorToThrow = err;
_this5.logOut();
} else if (!(err.response && err.response.status)) {
errorToThrow = new Error('There was a problem getting new session info. Please check your configuration settings.');
errorToThrow.fromSdk = true;
errorToThrow.originalError = err;
}
if (err.error && ['consent_required', 'interaction_required', 'login_required'].indexOf(err.error) > -1) {
errorToThrow = _this4._generateUnauthorizedError(err);
throw errorToThrow;
});
}
_this4.logOut();
} else if (!(err.response && err.response.status)) {
errorToThrow = new Error('There was a problem getting new session info. Please check your configuration settings.');
errorToThrow.fromSdk = true;
errorToThrow.originalError = err;
}
return this._updatedSessionInfoPromise;
throw errorToThrow;
});
}
/**
* Takes Auth0 Session Info, retrieves a Contxt API token, packages them up together, saves the
* tokens, and schedules a renewal.
*
* @returns {Promise}
* @fulfills {Auth0WebAuthSessionInfo} sessionInfo
*
* @private
*/
}, {
key: '_handleNewSessionInfo',
value: function _handleNewSessionInfo(sessionResponse) {
var _this6 = this;
key: '_generateUnauthorizedError',
value: function _generateUnauthorizedError(err) {
var error = new Error('Unauthorized');
return Promise.all([{
accessToken: sessionResponse.accessToken,
expiresAt: sessionResponse.expiresIn * 1000 + Date.now()
}, this._getApiToken(sessionResponse.accessToken)]).then(function (_ref2) {
var _ref3 = _slicedToArray(_ref2, 2),
partialSessionInfo = _ref3[0],
apiToken = _ref3[1];
if (!err) {
error.fromSdk = true;
}
var sessionInfo = _extends({}, partialSessionInfo, {
apiToken: apiToken
});
error.response = {
data: _extends({}, err, {
code: 401
}),
status: 401
};
_this6._saveSession(sessionInfo);
_this6._scheduleTokenRenewal();
return sessionInfo;
});
return error;
}
/**
* Loads a saved session from local storage
* Wraps Auth0's method for parsing hash information after a successful
* authentication.
*
* @returns {Object} session
* @returns {string} session.accessToken
* @returns {string} session.apiToken
* @returns {number} session.expiresAt
*
* @private
*/
}, {
key: '_loadSession',
value: function _loadSession() {
return {
accessToken: localStorage.getItem('access_token'),
apiToken: localStorage.getItem('api_token'),
expiresAt: JSON.parse(localStorage.getItem('expires_at'))
};
}
/**
* Wraps Auth0's method for parsing hash information after a successful authentication.
*
* @returns {Promise}

@@ -510,13 +426,13 @@ * @fulfill {Object} hashResponse Information returned from Auth0

}, {
key: '_parseWebAuthHash',
value: function _parseWebAuthHash() {
var _this7 = this;
key: '_parseHash',
value: function _parseHash() {
var _this5 = this;
return new Promise(function (resolve, reject) {
_this7._auth0.parseHash(function (err, hashResponse) {
if (err || !hashResponse) {
_this5._auth0.parseHash(function (err, authResult) {
if (err || !authResult) {
return reject(err || new Error('No valid tokens returned from auth0'));
}
return resolve(hashResponse);
return resolve(authResult);
});

@@ -527,9 +443,4 @@ });

/**
* Saves a session in local storage for future use
* Schedules the Access token to renew before they expire
*
* @param {Object} sessionInfo
* @param {string} sessionInfo.accessToken
* @param {string} sessionInfo.apiToken
* @param {number} sessionInfo.expiresAt
*
* @private

@@ -539,14 +450,26 @@ */

}, {
key: '_saveSession',
value: function _saveSession(sessionInfo) {
this._sessionInfo = sessionInfo;
key: '_scheduleSessionRefresh',
value: function _scheduleSessionRefresh() {
var _this6 = this;
localStorage.setItem('access_token', sessionInfo.accessToken);
localStorage.setItem('api_token', sessionInfo.apiToken);
localStorage.setItem('expires_at', JSON.stringify(sessionInfo.expiresAt));
var tokenExpiresAtBufferMs = this._sdk.config.auth.tokenExpiresAtBufferMs || 0;
var bufferedExpiresAt = this._sessionInfo.expiresAt - tokenExpiresAtBufferMs;
var delay = bufferedExpiresAt - Date.now();
if (this._sessionRenewalTimeout) {
clearTimeout(this._sessionRenewalTimeout);
}
this._sessionRenewalTimeout = setTimeout(function () {
_this6._getUpdatedSession();
}, delay);
}
/**
* Schedules the Access and API tokens to renew before they expire
* Saves a session in local storage for future use
*
* @param {Object} sessionInfo
* @param {string} sessionInfo.accessToken
* @param {number} sessionInfo.expiresAt
*
* @private

@@ -556,17 +479,14 @@ */

}, {
key: '_scheduleTokenRenewal',
value: function _scheduleTokenRenewal() {
var _this8 = this;
key: '_storeSession',
value: function _storeSession(_ref2) {
var accessToken = _ref2.accessToken,
expiresIn = _ref2.expiresIn;
var tokenExpiresAtBufferMs = this._sdk.config.auth.tokenExpiresAtBufferMs || 0;
var bufferedExpiresAt = this._sessionInfo.expiresAt - tokenExpiresAtBufferMs;
var delay = bufferedExpiresAt - Date.now();
var expiresAt = expiresIn * 1000 + Date.now();
if (this._tokenRenewalTimeout) {
clearTimeout(this._tokenRenewalTimeout);
}
localStorage.setItem('access_token', accessToken);
localStorage.setItem('expires_at', JSON.stringify(expiresAt));
this._tokenRenewalTimeout = setTimeout(function () {
_this8._getUpdatedSessionInfo();
}, delay);
this._sessionInfo.accessToken = accessToken;
this._sessionInfo.expiresAt = expiresAt;
}

@@ -573,0 +493,0 @@ }]);

{
"name": "@ndustrial/contxt-sdk",
"version": "1.0.0-beta.6",
"version": "1.0.0-beta.7",
"description": "",

@@ -5,0 +5,0 @@ "main": "lib/index.js",

@@ -17,3 +17,2 @@ import auth0 from 'auth0-js';

* @property {string} accessToken
* @property {string} apiToken
* @property {number} expiresAt

@@ -80,3 +79,5 @@ */

this._sdk.config.auth.onRedirect || this._defaultOnRedirect;
this._sessionInfo = this._loadSession();
this._sessionInfo = this._getStoredSession();
this._sessionRenewalTimeout = null;
this._tokenPromises = {};

@@ -96,3 +97,3 @@ const currentUrl = new URL(window.location);

if (this.isAuthenticated()) {
this._scheduleTokenRenewal();
this._scheduleSessionRefresh();
}

@@ -102,13 +103,5 @@ }

/**
* Gets the current access token (used to communicate with Auth0 & Contxt Auth)
* Requests an access token from Contxt Auth for the correct audience
*
* @returns {Promise}
* @fulfills {string} accessToken
*/
getCurrentAccessToken() {
return this._getCurrentTokenByType('access');
}
/**
* Gets the current API token (used to communicate with other Contxt APIs)
* @param audienceName
*

@@ -118,4 +111,31 @@ * @returns {Promise}

*/
getCurrentApiToken() {
return this._getCurrentTokenByType('api');
getCurrentApiToken(audienceName) {
if (!this.isAuthenticated()) {
return Promise.reject(this._generateUnauthorizedError());
}
const audience = this._sdk.config.audiences[audienceName];
if (!(audience && audience.clientId)) {
return Promise.reject(new Error('No valid audience found'));
}
if (!this._tokenPromises[audienceName]) {
this._tokenPromises[audienceName] = axios
.post(
`${this._sdk.config.audiences.contxtAuth.host}/v1/token`,
{
audiences: [audience.clientId],
nonce: 'nonce'
},
{
headers: {
Authorization: `Bearer ${this._sessionInfo.accessToken}`
}
}
)
.then(({ data }) => data.access_token);
}
return this._tokenPromises[audienceName];
}

@@ -131,5 +151,6 @@

getProfile() {
return this.getCurrentAccessToken().then((accessToken) => {
return new Promise((resolve, reject) => {
this._auth0.client.userInfo(accessToken, (err, profile) => {
return new Promise((resolve, reject) => {
this._auth0.client.userInfo(
this._sessionInfo.accessToken,
(err, profile) => {
if (err) {

@@ -146,4 +167,4 @@ reject(err);

resolve(formattedProfile);
});
});
}
);
});

@@ -153,3 +174,4 @@ }

/**
* Routine that takes unparsed information from Auth0, uses it to get a valid API token, and then
* Routine that takes unparsed information from Auth0, stores it in a way that
* can be used for getting access tokens, schedules its future renewal, and
* redirects to the correct page in the application.

@@ -162,10 +184,9 @@ *

handleAuthentication() {
return this._parseWebAuthHash()
.then((hash) => this._handleNewSessionInfo(hash))
.then((sessionInfo) => {
return this._parseHash()
.then((authResult) => {
this._storeSession(authResult);
this._scheduleSessionRefresh();
const redirectPathname = this._getRedirectPathname();
this._onRedirect(redirectPathname);
return sessionInfo;
})

@@ -187,10 +208,7 @@ .catch((err) => {

isAuthenticated() {
const hasTokens = !!(
return !!(
this._sessionInfo &&
this._sessionInfo.accessToken &&
this._sessionInfo.apiToken &&
this._sessionInfo.expiresAt
this._sessionInfo.expiresAt > Date.now()
);
return hasTokens;
}

@@ -206,20 +224,20 @@

/**
* Logs the user out by removing any stored session info, clearing any token renewal, and
* redirecting to the root
* Logs the user out by removing any stored session info, clearing any token
* renewal, and redirecting to the root
*/
logOut() {
this._sessionInfo = {};
this._tokenPromises = {};
localStorage.removeItem('access_token');
localStorage.removeItem('api_token');
localStorage.removeItem('expires_at');
clearTimeout(this._tokenRenewalTimeout);
clearTimeout(this._sessionRenewalTimeout);
this._onRedirect('/');
this._auth0.logout({ returnTo: new URL(window.location).origin });
}
/**
* Wraps Auth0's `checkSession` method. Will check if the current Auth0 session is valid and get
* updated information if needed
* Wraps Auth0's `checkSession` method. Will check if the current Auth0
* session is valid and get updated information if needed
*

@@ -246,4 +264,4 @@ * @fulfill {Object} sessionResponse Information returned from Auth0

/**
* Default method used for redirecting around the web application. Overridden by `onRedirect` in
* the auth config
* Default method used for redirecting around the web application. Overridden
* by `onRedirect` in the auth config
*

@@ -257,80 +275,31 @@ * @private

/**
* Requests an access token from Contxt Auth with the correct audiences.
* Grabs a stored redirect pathname that may have been stored in another part
* of the web application
*
* @returns {Promise}
* @fulfill {string} accessToken
*
* @private
*/
_getApiToken(accessToken) {
return axios
.post(
`${this._sdk.config.audiences.contxtAuth.host}/v1/token`,
{
audiences: Object.keys(this._sdk.config.audiences)
.map(
(audienceName) =>
this._sdk.config.audiences[audienceName].clientId
)
.filter(
(clientId) =>
clientId &&
clientId !== this._sdk.config.audiences.contxtAuth.clientId
),
nonce: 'nonce'
},
{
headers: { Authorization: `Bearer ${accessToken}` }
}
)
.then(({ data }) => data.access_token);
_getRedirectPathname() {
const redirectPathname = localStorage.getItem('redirect_pathname');
localStorage.removeItem('redirect_pathname');
return redirectPathname || '/';
}
/**
* Returns the type of token requested if it exists. Will get an updated token if the existing
* tokens have expired.
* Loads a saved session from local storage
*
* @param {string} type The type of token requested. Only valid types are `access` and `api`
* @returns {Object} session
* @returns {string} session.accessToken
* @returns {number} session.expiresAt
*
* @returns {Promise}
* @fulfills {string} token
* @rejects {Error}
*
* @private
*/
_getCurrentTokenByType(type) {
const propertyName = `${type}Token`;
const tokenExpiresAtBufferMs =
this._sdk.config.auth.tokenExpiresAtBufferMs || 0;
const bufferedExpiresAt =
this._sessionInfo.expiresAt - tokenExpiresAtBufferMs;
const needsNewToken = Date.now() > bufferedExpiresAt;
if (!needsNewToken && this._sessionInfo[propertyName]) {
return Promise.resolve(this._sessionInfo[propertyName]);
}
return this._getUpdatedSessionInfo().then((sessionInfo) => {
if (!sessionInfo[propertyName]) {
throw new Error(`No ${type} token found`);
}
return sessionInfo[propertyName];
});
_getStoredSession() {
return {
accessToken: localStorage.getItem('access_token'),
expiresAt: JSON.parse(localStorage.getItem('expires_at'))
};
}
/**
* Grabs a stored redirect pathname that may have been stored in another part of the
* web application
*
* @private
*/
_getRedirectPathname() {
const redirectPathname = localStorage.getItem('redirect_pathname');
localStorage.removeItem('redirect_pathname');
return redirectPathname || '/';
}
/**
* Gets up to date session info. Will get an updated session/tokens if the

@@ -341,3 +310,2 @@ * previous session has already expired. Will log the user out if an error

* @returns {Promise}
* @fulfills {Auth0WebAuthSessionInfo} sessionInfo
* @rejects {Error}

@@ -347,96 +315,58 @@ *

*/
_getUpdatedSessionInfo() {
if (!this._updatedSessionInfoPromise) {
this._updatedSessionInfoPromise = this._checkSession()
.then((sessionInfo) => this._handleNewSessionInfo(sessionInfo))
.then((sessionInfo) => {
this._updatedSessionInfoPromise = null;
_getUpdatedSession() {
return this._checkSession()
.then((sessionInfo) => {
this._storeSession(sessionInfo);
return sessionInfo;
})
.catch((err) => {
let errorToThrow = err;
this._tokenPromises = {};
if (
err.error &&
[
'consent_required',
'interaction_required',
'login_required'
].indexOf(err.error) > -1
) {
errorToThrow = new Error('Unauthorized');
errorToThrow.response = {
data: {
...err,
code: 401
},
status: 401
};
this._scheduleSessionRefresh();
})
.catch((err) => {
let errorToThrow = err;
this.logOut();
} else if (!(err.response && err.response.status)) {
errorToThrow = new Error(
'There was a problem getting new session info. Please check your configuration settings.'
);
errorToThrow.fromSdk = true;
errorToThrow.originalError = err;
}
if (
err.error &&
[
'consent_required',
'interaction_required',
'login_required'
].indexOf(err.error) > -1
) {
errorToThrow = this._generateUnauthorizedError(err);
throw errorToThrow;
});
}
this.logOut();
} else if (!(err.response && err.response.status)) {
errorToThrow = new Error(
'There was a problem getting new session info. Please check your configuration settings.'
);
errorToThrow.fromSdk = true;
errorToThrow.originalError = err;
}
return this._updatedSessionInfoPromise;
throw errorToThrow;
});
}
/**
* Takes Auth0 Session Info, retrieves a Contxt API token, packages them up together, saves the
* tokens, and schedules a renewal.
*
* @returns {Promise}
* @fulfills {Auth0WebAuthSessionInfo} sessionInfo
*
* @private
*/
_handleNewSessionInfo(sessionResponse) {
return Promise.all([
{
accessToken: sessionResponse.accessToken,
expiresAt: sessionResponse.expiresIn * 1000 + Date.now()
},
this._getApiToken(sessionResponse.accessToken)
]).then(([partialSessionInfo, apiToken]) => {
const sessionInfo = {
...partialSessionInfo,
apiToken
};
_generateUnauthorizedError(err) {
const error = new Error('Unauthorized');
this._saveSession(sessionInfo);
this._scheduleTokenRenewal();
if (!err) {
error.fromSdk = true;
}
return sessionInfo;
});
}
error.response = {
data: {
...err,
code: 401
},
status: 401
};
/**
* Loads a saved session from local storage
*
* @returns {Object} session
* @returns {string} session.accessToken
* @returns {string} session.apiToken
* @returns {number} session.expiresAt
*
* @private
*/
_loadSession() {
return {
accessToken: localStorage.getItem('access_token'),
apiToken: localStorage.getItem('api_token'),
expiresAt: JSON.parse(localStorage.getItem('expires_at'))
};
return error;
}
/**
* Wraps Auth0's method for parsing hash information after a successful authentication.
* Wraps Auth0's method for parsing hash information after a successful
* authentication.
*

@@ -449,6 +379,6 @@ * @returns {Promise}

*/
_parseWebAuthHash() {
_parseHash() {
return new Promise((resolve, reject) => {
this._auth0.parseHash((err, hashResponse) => {
if (err || !hashResponse) {
this._auth0.parseHash((err, authResult) => {
if (err || !authResult) {
return reject(

@@ -459,3 +389,3 @@ err || new Error('No valid tokens returned from auth0')

return resolve(hashResponse);
return resolve(authResult);
});

@@ -466,25 +396,7 @@ });

/**
* Saves a session in local storage for future use
* Schedules the Access token to renew before they expire
*
* @param {Object} sessionInfo
* @param {string} sessionInfo.accessToken
* @param {string} sessionInfo.apiToken
* @param {number} sessionInfo.expiresAt
*
* @private
*/
_saveSession(sessionInfo) {
this._sessionInfo = sessionInfo;
localStorage.setItem('access_token', sessionInfo.accessToken);
localStorage.setItem('api_token', sessionInfo.apiToken);
localStorage.setItem('expires_at', JSON.stringify(sessionInfo.expiresAt));
}
/**
* Schedules the Access and API tokens to renew before they expire
*
* @private
*/
_scheduleTokenRenewal() {
_scheduleSessionRefresh() {
const tokenExpiresAtBufferMs =

@@ -496,10 +408,29 @@ this._sdk.config.auth.tokenExpiresAtBufferMs || 0;

if (this._tokenRenewalTimeout) {
clearTimeout(this._tokenRenewalTimeout);
if (this._sessionRenewalTimeout) {
clearTimeout(this._sessionRenewalTimeout);
}
this._tokenRenewalTimeout = setTimeout(() => {
this._getUpdatedSessionInfo();
this._sessionRenewalTimeout = setTimeout(() => {
this._getUpdatedSession();
}, delay);
}
/**
* Saves a session in local storage for future use
*
* @param {Object} sessionInfo
* @param {string} sessionInfo.accessToken
* @param {number} sessionInfo.expiresAt
*
* @private
*/
_storeSession({ accessToken, expiresIn }) {
const expiresAt = expiresIn * 1000 + Date.now();
localStorage.setItem('access_token', accessToken);
localStorage.setItem('expires_at', JSON.stringify(expiresAt));
this._sessionInfo.accessToken = accessToken;
this._sessionInfo.expiresAt = expiresAt;
}
}

@@ -506,0 +437,0 @@

@@ -8,4 +8,7 @@ import auth0 from 'auth0-js';

describe('sessionTypes/Auth0WebAuth', function() {
let getStoredSession;
let isAuthenticated;
let originalWindow;
let scheduleSessionRefresh;
let sdk;
let originalWindow;
let webAuth;

@@ -31,3 +34,4 @@ let webAuthSession;

webAuthSession = {
authorize: this.sandbox.stub()
authorize: this.sandbox.stub(),
logout: this.sandbox.stub()
};

@@ -39,2 +43,12 @@ originalWindow = global.window;

getStoredSession = this.sandbox
.stub(Auth0WebAuth.prototype, '_getStoredSession')
.returns({});
isAuthenticated = this.sandbox
.stub(Auth0WebAuth.prototype, 'isAuthenticated')
.returns(true);
scheduleSessionRefresh = this.sandbox.stub(
Auth0WebAuth.prototype,
'_scheduleSessionRefresh'
);
webAuth = this.sandbox.stub(auth0, 'WebAuth').returns(webAuthSession);

@@ -52,5 +66,6 @@ });

let expectedSession;
let loadSession;
beforeEach(function() {
getStoredSession.restore();
expectedSession = {

@@ -62,7 +77,4 @@ accessToken: faker.internet.url(),

this.sandbox
.stub(Auth0WebAuth.prototype, 'isAuthenticated')
.returns(false);
loadSession = this.sandbox
.stub(Auth0WebAuth.prototype, '_loadSession')
getStoredSession = this.sandbox
.stub(Auth0WebAuth.prototype, '_getStoredSession')
.returns(expectedSession);

@@ -84,3 +96,3 @@

it('loads the session from memory', function() {
expect(loadSession.calledOnce).to.be.true;
expect(getStoredSession.calledOnce).to.be.true;
});

@@ -92,2 +104,7 @@

it('sets up the default data structures for tokens', function() {
expect(auth0WebAuth._sessionRenewalTimeout).to.be.null;
expect(auth0WebAuth._tokenPromises).to.deep.equal({});
});
it('creates an auth0 WebAuth instance with the default settings', function() {

@@ -110,2 +127,10 @@ expect(webAuth).to.be.calledWithNew;

});
it('checks if the user is currently authenticated', function() {
expect(isAuthenticated).to.be.calledOnce;
});
it('schedules a future token renewal', function() {
expect(scheduleSessionRefresh).to.be.calledOnce;
});
});

@@ -124,5 +149,2 @@

this.sandbox.stub(Auth0WebAuth.prototype, '_loadSession');
this.sandbox.stub(Auth0WebAuth.prototype, '_scheduleTokenRenewal');
auth0WebAuth = new Auth0WebAuth(sdk);

@@ -143,14 +165,8 @@ });

context('when currently authenticated', function() {
let scheduleTokenRenewal;
context('when the user is not authenticated', function() {
beforeEach(function() {
isAuthenticated.restore();
this.sandbox
.stub(Auth0WebAuth.prototype, 'isAuthenticated')
.returns(true);
this.sandbox.stub(Auth0WebAuth.prototype, '_loadSession');
scheduleTokenRenewal = this.sandbox.stub(
Auth0WebAuth.prototype,
'_scheduleTokenRenewal'
);
.returns(false);

@@ -160,4 +176,4 @@ const auth0WebAuth = new Auth0WebAuth(sdk); // eslint-disable-line no-unused-vars

it('schedules a future token renewal', function() {
expect(scheduleTokenRenewal).to.be.calledOnce;
it('does not schedule a future token renewal', function() {
expect(scheduleSessionRefresh).to.not.be.called;
});

@@ -167,6 +183,2 @@ });

context('without required config options', function() {
beforeEach(function() {
this.sandbox.stub(Auth0WebAuth.prototype, '_loadSession');
});
it('throws an error when no clientId is provided', function() {

@@ -181,30 +193,145 @@ delete sdk.config.auth.clientId;

['Access', 'Api'].forEach(function(tokenType) {
describe(`getCurrent${tokenType}Token`, function() {
let expectedToken;
let getCurrentTokenByType;
describe('getCurrentApiToken', function() {
let expectedAudienceName;
beforeEach(function() {
expectedAudienceName = faker.random.arrayElement(
Object.keys(sdk.config.audiences)
);
});
context(
"when there is no existing request for an audience's token",
function() {
let auth0WebAuth;
let expectedAccessToken;
let expectedApiToken;
let post;
let promise;
beforeEach(function() {
expectedAccessToken = faker.internet.password();
expectedApiToken = faker.internet.password();
post = this.sandbox
.stub(axios, 'post')
.resolves({ data: { access_token: expectedApiToken } });
auth0WebAuth = new Auth0WebAuth(sdk);
auth0WebAuth._sessionInfo.accessToken = expectedAccessToken;
promise = auth0WebAuth.getCurrentApiToken(expectedAudienceName);
});
it('requests a token from contxt-auth', function() {
expect(post).to.be.calledWith(
`${sdk.config.audiences.contxtAuth.host}/v1/token`,
{
audiences: [sdk.config.audiences[expectedAudienceName].clientId],
nonce: 'nonce'
},
{
headers: {
Authorization: `Bearer ${expectedAccessToken}`
}
}
);
});
it('resolves with the token', function() {
return expect(promise).to.be.fulfilled.and.to.eventually.equal(
expectedApiToken
);
});
}
);
context(
"when there is an existing request for an audience's token",
function() {
let auth0WebAuth;
let expectedPromise;
let promise;
beforeEach(function() {
expectedPromise = new Promise(function() {});
auth0WebAuth = new Auth0WebAuth(sdk);
auth0WebAuth._tokenPromises[expectedAudienceName] = expectedPromise;
promise = auth0WebAuth.getCurrentApiToken(expectedAudienceName);
});
it('returns the existing promise', function() {
expect(promise).to.equal(expectedPromise);
});
}
);
context('when the user is not authenticated', function() {
let expectedError;
let generateUnauthorizedError;
let promise;
beforeEach(function() {
expectedToken = faker.internet.password();
expectedError = new Error(faker.hacker.phrase());
getCurrentTokenByType = this.sandbox
.stub(Auth0WebAuth.prototype, '_getCurrentTokenByType')
.resolves(expectedToken);
this.sandbox.stub(Auth0WebAuth.prototype, '_loadSession');
generateUnauthorizedError = this.sandbox
.stub(Auth0WebAuth.prototype, '_generateUnauthorizedError')
.returns(expectedError);
const auth0WebAuth = new Auth0WebAuth(sdk);
promise = auth0WebAuth[`getCurrent${tokenType}Token`]();
isAuthenticated.restore();
isAuthenticated = this.sandbox
.stub(Auth0WebAuth.prototype, 'isAuthenticated')
.returns(false);
promise = auth0WebAuth.getCurrentApiToken(expectedAudienceName);
});
it('gets the current token', function() {
expect(getCurrentTokenByType).to.be.calledWith(tokenType.toLowerCase());
it('checks if the audience already has a valid token', function() {
return promise.then(expect.fail).catch(() => {
expect(isAuthenticated).to.be.calledOnce;
});
});
it('returns a promise with the token', function() {
return expect(promise).to.be.fulfilled.and.to.eventually.equal(
expectedToken
);
it('gets a generated `unauthorized` error', function() {
return promise.then(expect.fail).catch(() => {
expect(generateUnauthorizedError).to.be.calledOnce;
});
});
it('returns a rejected promise with an error', function() {
return expect(promise).to.be.rejectedWith(expectedError);
});
});
context('when a valid audience is not provided', function() {
it('returns a rejected promise with an error when there is no audience with that name', function() {
const auth0WebAuth = new Auth0WebAuth(sdk);
return expect(
auth0WebAuth.getCurrentApiToken(faker.hacker.noun())
).to.be.rejectedWith('No valid audience found');
});
it('returns a rejected promise with an error when there is no client ID for the chosen audenice', function() {
const invalidAudience = faker.lorem.word();
const auth0WebAuth = new Auth0WebAuth({
...sdk,
config: {
...sdk.config,
audiences: {
...sdk.config.audiences,
[invalidAudience]: {}
}
}
});
return expect(
auth0WebAuth.getCurrentApiToken(invalidAudience)
).to.be.rejectedWith('No valid audience found');
});
});
});

@@ -217,3 +344,2 @@

let expectedProfile;
let getCurrentAccessToken;
let profile;

@@ -229,6 +355,2 @@ let promise;

getCurrentAccessToken = this.sandbox
.stub(Auth0WebAuth.prototype, 'getCurrentAccessToken')
.resolves(expectedAccessToken);
this.sandbox.stub(Auth0WebAuth.prototype, '_loadSession');
webAuthSession.client = {

@@ -241,9 +363,7 @@ userInfo: this.sandbox.stub().callsFake((accessToken, cb) => {

auth0WebAuth = new Auth0WebAuth(sdk);
auth0WebAuth._sessionInfo.accessToken = expectedAccessToken;
promise = auth0WebAuth.getProfile();
});
it("gets the user's access token", function() {
expect(getCurrentAccessToken).to.be.calledOnce;
});
it("gets the user's profile", function() {

@@ -270,7 +390,2 @@ return promise.then(() => {

expectedError = new Error(faker.hacker.phrase());
this.sandbox
.stub(Auth0WebAuth.prototype, 'getCurrentAccessToken')
.resolves(faker.internet.password());
this.sandbox.stub(Auth0WebAuth.prototype, '_loadSession');
webAuthSession.client = {

@@ -283,3 +398,4 @@ userInfo: this.sandbox.stub().callsFake((accessToken, cb) => {

const auth0WebAuth = new Auth0WebAuth(sdk);
auth0WebAuth._sessionInfo = { accessToken: faker.internet.password() };
auth0WebAuth._sessionInfo.accessToken = faker.internet.password();
promise = auth0WebAuth.getProfile();

@@ -295,56 +411,54 @@ });

describe('handleAuthentication', function() {
context('successfully getting a new api token', function() {
let clock;
context('when successfully authenticating', function() {
let expectedHash;
let expectedRedirectPathname;
let expectedSessionInfo;
let getRedirectPathname;
let handleNewSessionInfo;
let onRedirect;
let parseWebAuthHash;
let parseHash;
let promise;
let storeSession;
beforeEach(function() {
const currentDate = new Date();
expectedRedirectPathname = `/${faker.hacker.adjective()}/${faker.hacker.adjective()}`;
expectedSessionInfo = {
expectedHash = {
accessToken: faker.internet.password(),
apiToken: faker.internet.password(),
expiresAt: faker.date.future().getTime()
};
expectedHash = {
accessToken: expectedSessionInfo.accessToken,
expiresIn:
(expectedSessionInfo.expiresAt - currentDate.getTime()) / 1000
(faker.date.future().getTime() - currentDate.getTime()) / 1000
};
expectedRedirectPathname = `/${faker.hacker.adjective()}/${faker.hacker.adjective()}`;
clock = sinon.useFakeTimers(currentDate);
getRedirectPathname = this.sandbox
.stub(Auth0WebAuth.prototype, '_getRedirectPathname')
.returns(expectedRedirectPathname);
handleNewSessionInfo = this.sandbox
.stub(Auth0WebAuth.prototype, '_handleNewSessionInfo')
.resolves(expectedSessionInfo);
this.sandbox.stub(Auth0WebAuth.prototype, '_loadSession');
parseHash = this.sandbox
.stub(Auth0WebAuth.prototype, '_parseHash')
.resolves(expectedHash);
onRedirect = this.sandbox.stub();
parseWebAuthHash = this.sandbox
.stub(Auth0WebAuth.prototype, '_parseWebAuthHash')
.resolves(expectedHash);
storeSession = this.sandbox.stub(
Auth0WebAuth.prototype,
'_storeSession'
);
const auth0WebAuth = new Auth0WebAuth(sdk);
auth0WebAuth._onRedirect = onRedirect;
scheduleSessionRefresh.reset();
promise = auth0WebAuth.handleAuthentication();
});
afterEach(function() {
clock.restore();
it('parses the previously retrieved web auth hash', function() {
return promise.then(() => {
expect(parseHash).to.be.calledOnce;
});
});
it('parses the previously retrieved web auth hash', function() {
expect(parseWebAuthHash).to.be.calledOnce;
it('stores the session', function() {
return promise.then(() => {
expect(storeSession).to.be.calledWith(expectedHash);
});
});
it('handles getting any other needed new session info', function() {
it('schedules the session to refresh', function() {
return promise.then(() => {
expect(handleNewSessionInfo).to.be.calledWith(expectedHash);
expect(scheduleSessionRefresh).to.be.calledOnce;
});

@@ -365,10 +479,8 @@ });

it('returns a promise that is fulfilled with the web auth info and contxt api token', function() {
return expect(promise).to.be.fulfilled.and.to.eventually.deep.equal(
expectedSessionInfo
);
it('returns a resolved promise', function() {
return expect(promise).to.be.fulfilled;
});
});
context('unsuccessfully getting an api token', function() {
context('when there is a problem parsing the hash', function() {
let expectedError;

@@ -379,21 +491,12 @@ let onRedirect;

beforeEach(function() {
expectedError = faker.hacker.phrase();
expectedError = new Error(faker.hacker.phrase());
this.sandbox.stub(Auth0WebAuth.prototype, '_loadSession');
this.sandbox
.stub(Auth0WebAuth.prototype, '_parseHash')
.rejects(expectedError);
onRedirect = this.sandbox.stub();
this.sandbox
.stub(Auth0WebAuth.prototype, '_parseWebAuthHash')
.rejects(new Error(expectedError));
global.window = {
_location: `${faker.internet.url()}/${faker.hacker.adjective()}`,
get location() {
return this._location;
},
set location(newLocation) {
this._location = newLocation;
}
};
const auth0WebAuth = new Auth0WebAuth(sdk);
auth0WebAuth._onRedirect = onRedirect;
promise = auth0WebAuth.handleAuthentication();

@@ -408,3 +511,3 @@ });

it('returns with a rejected promise', function() {
it('returns a rejected promise', function() {
return expect(promise).to.be.rejectedWith(expectedError);

@@ -419,8 +522,20 @@ });

beforeEach(function() {
this.sandbox.stub(Auth0WebAuth.prototype, '_loadSession');
auth0WebAuth = new Auth0WebAuth(sdk);
isAuthenticated.restore();
});
it('returns true if the user is authenticated', function() {
auth0WebAuth._sessionInfo = {
accessToken: faker.internet.url(),
expiresAt: faker.date.future().getTime()
};
const isAuthenticated = auth0WebAuth.isAuthenticated();
expect(isAuthenticated).to.be.true;
});
it('returns false when there is no stored session info', function() {
auth0WebAuth._sessionInfo = undefined;
const isAuthenticated = auth0WebAuth.isAuthenticated();

@@ -432,6 +547,3 @@

it('returns false when there is no stored access token', function() {
auth0WebAuth._sessionInfo = {
apiToken: faker.internet.url(),
expiresAt: faker.date.past().getTime()
};
auth0WebAuth._sessionInfo = {};

@@ -443,6 +555,5 @@ const isAuthenticated = auth0WebAuth.isAuthenticated();

it('returns false when there is no stored api token', function() {
it('returns false when there is no stored `expiresAt` value', function() {
auth0WebAuth._sessionInfo = {
accessToken: faker.internet.url(),
expiresAt: faker.date.past().getTime()
accessToken: faker.internet.url()
};

@@ -455,6 +566,6 @@

it('returns false when there is no stored expires at value', function() {
it('returns false when the stored `expiresAt` value is in the past', function() {
auth0WebAuth._sessionInfo = {
accessToken: faker.internet.url(),
apiToken: faker.internet.url()
expiresAt: faker.date.past().getTime()
};

@@ -472,4 +583,2 @@

beforeEach(function() {
this.sandbox.stub(Auth0WebAuth.prototype, '_loadSession');
auth0WebAuth = new Auth0WebAuth(sdk);

@@ -488,3 +597,2 @@ auth0WebAuth.logIn();

let expectedTokenRenewalTimeout;
let onRedirect;
let localStorage;

@@ -496,3 +604,2 @@

clearTimeout = this.sandbox.stub(global, 'clearTimeout');
this.sandbox.stub(Auth0WebAuth.prototype, '_loadSession');
localStorage = {

@@ -502,7 +609,4 @@ removeItem: this.sandbox.stub()

global.localStorage = localStorage;
onRedirect = this.sandbox.stub();
this.sandbox.stub(Auth0WebAuth.prototype, '_scheduleTokenRenewal');
auth0WebAuth = new Auth0WebAuth(sdk);
auth0WebAuth._onRedirect = onRedirect;
auth0WebAuth._sessionInfo = {

@@ -513,32 +617,34 @@ accessToken: faker.internet.password(),

};
auth0WebAuth._tokenRenewalTimeout = expectedTokenRenewalTimeout;
auth0WebAuth._tokenPromises = {
[faker.internet.password()]: Promise.resolve()
};
auth0WebAuth._sessionRenewalTimeout = expectedTokenRenewalTimeout;
auth0WebAuth.logOut();
});
afterEach(function() {
delete global.localStorage;
it('resets the session info stored in the auth module instance', function() {
expect(auth0WebAuth._sessionInfo).to.deep.equal({});
});
it('resets the session info in the auth module instance', function() {
expect(auth0WebAuth._sessionInfo).to.deep.equal({});
it('resets any stored API tokens', function() {
expect(auth0WebAuth._tokenPromises).to.deep.equal({});
});
it('deletes the access token from local storage', function() {
it('deletes the `access_token` from local storage', function() {
expect(localStorage.removeItem).to.be.calledWith('access_token');
});
it('deletes the api access token from local storage', function() {
expect(localStorage.removeItem).to.be.calledWith('api_token');
});
it('deletes the expires at information from local storage', function() {
it('deletes the `expires_at` from local storage', function() {
expect(localStorage.removeItem).to.be.calledWith('expires_at');
});
it('clears the existing token renewal timeout', function() {
it('clears the token renewal timeout', function() {
expect(clearTimeout).to.be.calledWith(expectedTokenRenewalTimeout);
});
it("redirects to the browser's location", function() {
expect(onRedirect).to.be.calledWith('/');
it('logs the user out from Auth0 and redirects to the project root', function() {
expect(webAuthSession.logout).to.be.calledWith({
returnTo: global.window.location
});
});

@@ -557,3 +663,2 @@ });

accessToken: faker.internet.password(),
apiToken: faker.internet.password(),
expiresAt: faker.date.future().getTime()

@@ -567,3 +672,2 @@ };

});
this.sandbox.stub(Auth0WebAuth.prototype, '_loadSession');

@@ -597,3 +701,2 @@ const auth0WebAuth = new Auth0WebAuth(sdk);

});
this.sandbox.stub(Auth0WebAuth.prototype, '_loadSession');

@@ -616,12 +719,2 @@ const auth0WebAuth = new Auth0WebAuth(sdk);

global.window = {
_location: faker.internet.url(),
get location() {
return this._location;
},
set location(newLocation) {
this._location = newLocation;
}
};
Auth0WebAuth.prototype._defaultOnRedirect(expectedPathname);

@@ -635,198 +728,3 @@ });

describe('_getApiToken', function() {
context('when handling audiences with a client id', function() {
let accessToken;
let expectedApiToken;
let post;
let promise;
beforeEach(function() {
accessToken = faker.internet.password();
expectedApiToken = faker.internet.password();
post = this.sandbox.stub(axios, 'post').callsFake(() => {
return Promise.resolve({ data: { access_token: expectedApiToken } });
});
this.sandbox.stub(Auth0WebAuth.prototype, '_loadSession');
const auth0WebAuth = new Auth0WebAuth(sdk);
promise = auth0WebAuth._getApiToken(accessToken);
});
it('POSTs to the contxt api to get a token', function() {
expect(post).to.be.calledWith(
`${sdk.config.audiences.contxtAuth.host}/v1/token`,
{
audiences: [sdk.config.audiences.facilities.clientId],
nonce: 'nonce'
},
{ headers: { Authorization: `Bearer ${accessToken}` } }
);
});
it('returns a promise that fulfills with the api access token', function() {
return expect(promise).to.be.fulfilled.and.to.eventually.equal(
expectedApiToken
);
});
});
context('when handing a null audience', function() {
let accessToken;
let post;
beforeEach(function() {
accessToken = faker.internet.password();
post = this.sandbox.stub(axios, 'post').resolves({ data: {} });
this.sandbox.stub(Auth0WebAuth.prototype, '_loadSession');
const auth0WebAuth = new Auth0WebAuth({
...sdk,
config: {
...sdk.config,
audiences: {
...sdk.config.audiences,
[faker.hacker.noun()]: {
clientId: null,
host: faker.internet.url(),
module: function() {}
}
}
}
});
auth0WebAuth._getApiToken(accessToken);
});
it('does not include null values when getting a token from the contxt api', function() {
const { audiences } = post.firstCall.args[1];
expect(audiences).to.not.include(null);
});
});
});
describe('_getCurrentTokenByType', function() {
context('when there are no existing tokens', function() {
context('when successfully getting the requested token', function() {
let expectedSessionInfo;
let getUpdatedSessionInfo;
let promise;
let tokenType;
beforeEach(function() {
expectedSessionInfo = {
accessToken: faker.internet.password(),
apiToken: faker.internet.password()
};
tokenType = faker.random.arrayElement(
Object.keys(expectedSessionInfo)
);
getUpdatedSessionInfo = this.sandbox
.stub(Auth0WebAuth.prototype, '_getUpdatedSessionInfo')
.resolves(expectedSessionInfo);
this.sandbox.stub(Auth0WebAuth.prototype, '_loadSession');
const auth0WebAuth = new Auth0WebAuth(sdk);
auth0WebAuth._sessionInfo = {
expiresAt: faker.date.past().getTime()
};
promise = auth0WebAuth._getCurrentTokenByType(
tokenType.replace(/Token$/, '')
);
});
it('gets updated session info', function() {
expect(getUpdatedSessionInfo).to.be.calledOnce;
});
it('resolves with the requested token info', function() {
return expect(promise).to.be.fulfilled.and.to.eventually.equal(
expectedSessionInfo[tokenType]
);
});
});
context(
'when requesting a token type that does not exist in the session info',
function() {
let promise;
let tokenType;
beforeEach(function() {
tokenType = faker.hacker.adjective();
this.sandbox
.stub(Auth0WebAuth.prototype, '_getUpdatedSessionInfo')
.resolves({
accessToken: faker.internet.password(),
apiToken: faker.internet.password()
});
this.sandbox.stub(Auth0WebAuth.prototype, '_loadSession');
const auth0WebAuth = new Auth0WebAuth(sdk);
auth0WebAuth._sessionInfo = {
expiresAt: faker.date.past().getTime()
};
promise = auth0WebAuth._getCurrentTokenByType(tokenType);
});
it('returns a rejected promise', function() {
return expect(promise).to.be.rejectedWith(
`No ${tokenType} token found`
);
});
}
);
});
context('when there is an existing, non-expired token', function() {
let expectedSessionInfo;
let getUpdatedSessionInfo;
let promise;
let tokenType;
beforeEach(function() {
expectedSessionInfo = {
accessToken: faker.internet.password(),
apiToken: faker.internet.password(),
// Add buffer to ensure date is in the future
expiresAt:
faker.date.future().getTime() +
sdk.config.auth.tokenExpiresAtBufferMs
};
tokenType = faker.random.arrayElement(
Object.keys(omit(expectedSessionInfo, ['expiresAt']))
);
getUpdatedSessionInfo = this.sandbox
.stub(Auth0WebAuth.prototype, '_getUpdatedSessionInfo')
.resolves({});
this.sandbox.stub(Auth0WebAuth.prototype, '_loadSession');
const auth0WebAuth = new Auth0WebAuth(sdk);
auth0WebAuth._sessionInfo = expectedSessionInfo;
promise = auth0WebAuth._getCurrentTokenByType(
tokenType.replace(/Token$/, '')
);
});
it('does not get updated session info', function() {
expect(getUpdatedSessionInfo).to.not.be.called;
});
it('resolves with the requested token info', function() {
return expect(promise).to.be.fulfilled.and.to.eventually.equal(
expectedSessionInfo[tokenType]
);
});
});
});
describe('_getRedirectPathname', function() {
beforeEach(function() {
global.localStorage = {
removeItem: this.sandbox.stub()
};
});
afterEach(function() {

@@ -843,6 +741,6 @@ delete global.localStorage;

this.sandbox.stub(Auth0WebAuth.prototype, '_loadSession');
global.localStorage.getItem = this.sandbox
.stub()
.returns(expectedPathname);
global.localStorage = {
getItem: this.sandbox.stub().returns(expectedPathname),
removeItem: this.sandbox.stub()
};

@@ -874,4 +772,6 @@ const auth0WebAuth = new Auth0WebAuth(sdk);

beforeEach(function() {
this.sandbox.stub(Auth0WebAuth.prototype, '_loadSession');
global.localStorage.getItem = this.sandbox.stub();
global.localStorage = {
getItem: this.sandbox.stub(),
removeItem: this.sandbox.stub()
};

@@ -888,293 +788,234 @@ const auth0WebAuth = new Auth0WebAuth(sdk);

describe('_getUpdatedSessionInfo', function() {
context('when there is no existing request for session info', function() {
context('when successfully updating session info', function() {
let auth0WebAuth;
let checkSession;
let expectedSessionInfo;
let handleNewSessionInfo;
let promise;
describe('_getStoredSession', function() {
let auth0WebAuth;
let expectedSessionInfo;
let localStorage;
let session;
beforeEach(function() {
expectedSessionInfo = {
accessToken: faker.internet.password(),
apiToken: faker.internet.password(),
expiresAt: faker.date.future().getTime()
};
beforeEach(function() {
expectedSessionInfo = {
accessToken: faker.internet.password(),
expiresAt: faker.date.future().getTime()
};
checkSession = this.sandbox
.stub(Auth0WebAuth.prototype, '_checkSession')
.resolves(expectedSessionInfo);
handleNewSessionInfo = this.sandbox
.stub(Auth0WebAuth.prototype, '_handleNewSessionInfo')
.resolves(expectedSessionInfo);
this.sandbox.stub(Auth0WebAuth.prototype, '_loadSession');
localStorage = {
getItem: this.sandbox.stub().callsFake((key) => {
switch (key) {
case 'access_token':
return expectedSessionInfo.accessToken;
case 'expires_at':
return `${expectedSessionInfo.expiresAt}`;
}
})
};
global.localStorage = localStorage;
auth0WebAuth = new Auth0WebAuth(sdk);
promise = auth0WebAuth._getUpdatedSessionInfo();
});
auth0WebAuth = new Auth0WebAuth(sdk);
getStoredSession.restore();
it('sets the stored promise in the class instance', function() {
expect(auth0WebAuth._updatedSessionInfoPromise).to.be.a('promise');
});
session = auth0WebAuth._getStoredSession();
});
it('checks the existing session for updated session info', function() {
expect(checkSession).to.be.calledOnce;
});
afterEach(function() {
delete global.localStorage;
});
it('handles the new session info', function() {
return promise.then(() => {
expect(handleNewSessionInfo).to.be.calledWith(expectedSessionInfo);
});
});
it('gets the access token out of local storage', function() {
expect(localStorage.getItem).to.be.calledWith('access_token');
});
it('resets the stored promise in the class instance', function() {
return promise.then(() => {
expect(auth0WebAuth._updatedSessionInfoPromise).to.be.null;
});
});
it('gets the expires at information out of local storage (and coreces it to be a number)', function() {
expect(localStorage.getItem).to.be.calledWith('expires_at');
});
it('resolves with the new session info', function() {
return expect(promise).to.be.fulfilled.and.to.eventually.equal(
expectedSessionInfo
);
});
});
it('returns an object with the session info', function() {
expect(session).to.deep.equal(expectedSessionInfo);
});
});
context('when there is an error getting the session info', function() {
let logOut;
describe('_getUpdatedSession', function() {
context('when successfully updating the session', function() {
let auth0WebAuth;
let checkSession;
let expectedSessionInfo;
let storeSession;
let promise;
beforeEach(function() {
this.sandbox.stub(Auth0WebAuth.prototype, '_loadSession');
logOut = this.sandbox.stub(Auth0WebAuth.prototype, 'logOut');
});
beforeEach(function() {
expectedSessionInfo = {
accessToken: faker.internet.password(),
expiresAt: faker.date.future().getTime()
};
it('throws a 401 and logs the user out if Auth0 requires the session to be re-authenticated', function() {
const errorType = faker.random.arrayElement([
'consent_required',
'interaction_required',
'login_required'
]);
const originalError = {
error: errorType,
error_description: {
consent_required: 'Consent required',
interaction_required: 'Interaction required',
login_required: 'Login required'
}[errorType]
};
const expectedError = new Error('Unauthorized');
expectedError.response = {
data: {
code: 401,
error: originalError.error,
error_description: originalError.error_description
},
status: 401
};
checkSession = this.sandbox
.stub(Auth0WebAuth.prototype, '_checkSession')
.resolves(expectedSessionInfo);
storeSession = this.sandbox.stub(
Auth0WebAuth.prototype,
'_storeSession'
);
this.sandbox
.stub(Auth0WebAuth.prototype, '_checkSession')
.rejects(originalError);
auth0WebAuth = new Auth0WebAuth(sdk);
auth0WebAuth._tokenPromises = {
[faker.internet.password()]: Promise.resolve()
};
scheduleSessionRefresh.reset();
const auth0WebAuth = new Auth0WebAuth(sdk);
const promise = auth0WebAuth._getUpdatedSessionInfo();
promise = auth0WebAuth._getUpdatedSession();
});
return promise.then(expect.fail).catch((error) => {
expect(error.message).to.equal('Unauthorized');
expect(error.response).to.deep.equal(expectedError.response);
it('checks the existing session for updated session info', function() {
return promise.then(() => {
expect(checkSession).to.be.calledOnce;
});
});
expect(logOut).to.be.calledOnce;
});
it('stores the new session info', function() {
return promise.then(() => {
expect(storeSession).to.be.calledWith(expectedSessionInfo);
});
});
it('throws a human readable error when unable to reach the server', function() {
const originalError = new Error(faker.hacker.phrase());
const expectedError = new Error(
'There was a problem getting new session info. Please check your configuration settings.'
);
expectedError.fromSdk = true;
expectedError.originalError = originalError;
it('resets any stored access tokens', function() {
return promise.then(() => {
expect(auth0WebAuth._tokenPromises).to.deep.equal({});
});
});
this.sandbox
.stub(Auth0WebAuth.prototype, '_checkSession')
.rejects(originalError);
const auth0WebAuth = new Auth0WebAuth(sdk);
const promise = auth0WebAuth._getUpdatedSessionInfo();
return promise.then(expect.fail).catch((err) => {
expect(err.message).to.equal(expectedError.message);
expect(err.fromSdk).to.equal(expectedError.fromSdk);
expect(err.originalError).to.equal(expectedError.originalError);
});
it('schedules the session to refresh in the future', function() {
return promise.then(() => {
expect(scheduleSessionRefresh).to.be.calledOnce;
});
});
});
it('throws the original error if it includes a status code', function() {
const expectedError = new Error();
expectedError.response = { status: faker.random.number() };
context('when there is a failure whileupdating the session', function() {
let generateUnauthorizedError;
let logOut;
this.sandbox
.stub(Auth0WebAuth.prototype, '_checkSession')
.rejects(expectedError);
beforeEach(function() {
logOut = this.sandbox.stub(Auth0WebAuth.prototype, 'logOut');
});
const auth0WebAuth = new Auth0WebAuth(sdk);
const promise = auth0WebAuth._getUpdatedSessionInfo();
it('throws a 401 and logs the user out if Auth0 requires the session to be re-authenticated', function() {
const errorType = faker.random.arrayElement([
'consent_required',
'interaction_required',
'login_required'
]);
const originalError = {
error: errorType,
error_description: {
consent_required: 'Consent required',
interaction_required: 'Interaction required',
login_required: 'Login required'
}[errorType]
};
const expectedError = new Error('Unauthorized');
expectedError.response = {
data: {
code: 401,
error: originalError.error,
error_description: originalError.error_description
},
status: 401
};
return expect(promise).to.be.rejectedWith(expectedError);
});
});
});
this.sandbox
.stub(Auth0WebAuth.prototype, '_checkSession')
.rejects(originalError);
generateUnauthorizedError = this.sandbox
.stub(Auth0WebAuth.prototype, '_generateUnauthorizedError')
.returns(expectedError);
context(
'when there is an existing request for new session info',
function() {
let expectedPromise;
let promise;
const auth0WebAuth = new Auth0WebAuth(sdk);
const promise = auth0WebAuth._getUpdatedSession();
beforeEach(function() {
expectedPromise = new Promise(() => {});
this.sandbox.stub(Auth0WebAuth.prototype, '_loadSession');
return promise.then(expect.fail).catch((error) => {
expect(generateUnauthorizedError).to.be.calledWith(originalError);
const auth0WebAuth = new Auth0WebAuth(sdk);
auth0WebAuth._updatedSessionInfoPromise = expectedPromise;
promise = auth0WebAuth._getUpdatedSessionInfo();
});
expect(error.message).to.equal('Unauthorized');
expect(error.response).to.deep.equal(expectedError.response);
it('returns the existing promise', function() {
expect(promise).to.equal(expectedPromise);
expect(logOut).to.be.calledOnce;
});
}
);
});
});
describe('_handleNewSessionInfo', function() {
let clock;
let expectedSessionInfo;
let getApiToken;
let promise;
let saveSession;
let scheduleTokenRenewal;
it('throws a human readable error when unable to reach the server', function() {
const originalError = new Error(faker.hacker.phrase());
const expectedError = new Error(
'There was a problem getting new session info. Please check your configuration settings.'
);
expectedError.fromSdk = true;
expectedError.originalError = originalError;
beforeEach(function() {
const currentDate = new Date();
expectedSessionInfo = {
accessToken: faker.internet.password(),
apiToken: faker.internet.password(),
expiresAt: faker.date.future().getTime()
};
this.sandbox
.stub(Auth0WebAuth.prototype, '_checkSession')
.rejects(originalError);
clock = sinon.useFakeTimers(currentDate);
getApiToken = this.sandbox
.stub(Auth0WebAuth.prototype, '_getApiToken')
.resolves(expectedSessionInfo.apiToken);
this.sandbox.stub(Auth0WebAuth.prototype, '_loadSession');
saveSession = this.sandbox.stub(Auth0WebAuth.prototype, '_saveSession');
scheduleTokenRenewal = this.sandbox.stub(
Auth0WebAuth.prototype,
'_scheduleTokenRenewal'
);
const auth0WebAuth = new Auth0WebAuth(sdk);
const promise = auth0WebAuth._getUpdatedSession();
const auth0WebAuth = new Auth0WebAuth(sdk);
promise = auth0WebAuth._handleNewSessionInfo({
accessToken: expectedSessionInfo.accessToken,
expiresIn:
(expectedSessionInfo.expiresAt - currentDate.getTime()) / 1000
return promise.then(expect.fail).catch((err) => {
expect(err.message).to.equal(expectedError.message);
expect(err.fromSdk).to.equal(expectedError.fromSdk);
expect(err.originalError).to.equal(expectedError.originalError);
});
});
});
afterEach(function() {
clock.restore();
});
it('throws the original error if it includes a status code', function() {
const expectedError = new Error();
expectedError.response = { status: faker.random.number() };
it('gets a contxt api token using the web auth access token', function() {
return promise.then(() => {
expect(getApiToken).to.be.calledWith(expectedSessionInfo.accessToken);
});
});
this.sandbox
.stub(Auth0WebAuth.prototype, '_checkSession')
.rejects(expectedError);
it('saves the updated session info', function() {
return promise.then(() => {
expect(saveSession).to.be.calledWith(expectedSessionInfo);
});
});
const auth0WebAuth = new Auth0WebAuth(sdk);
const promise = auth0WebAuth._getUpdatedSession();
it('schedules the next token renewal', function() {
return promise.then(() => {
expect(scheduleTokenRenewal).to.be.calledOnce;
return expect(promise).to.be.rejectedWith(expectedError);
});
});
it('resolves with new session info', function() {
return expect(promise).to.be.fulfilled.and.to.eventually.deep.equal(
expectedSessionInfo
);
});
});
describe('_loadSession', function() {
describe('_generateUnauthorizedError', function() {
let auth0WebAuth;
let expectedSessionInfo;
let localStorage;
let session;
beforeEach(function() {
expectedSessionInfo = {
accessToken: faker.internet.password(),
apiToken: faker.internet.password(),
expiresAt: faker.date.future().getTime()
};
this.sandbox
.stub(Auth0WebAuth.prototype, 'isAuthenticated')
.returns(false);
localStorage = {
getItem: this.sandbox.stub().callsFake((key) => {
switch (key) {
case 'access_token':
return expectedSessionInfo.accessToken;
case 'api_token':
return expectedSessionInfo.apiToken;
case 'expires_at':
return `${expectedSessionInfo.expiresAt}`;
}
})
};
global.localStorage = localStorage;
auth0WebAuth = new Auth0WebAuth(sdk);
session = auth0WebAuth._loadSession();
});
afterEach(function() {
delete global.localStorage;
});
it('returns an error with a 401 status code', function() {
const error = auth0WebAuth._generateUnauthorizedError();
it('gets the access token out of local storage', function() {
expect(localStorage.getItem).to.be.calledWith('access_token');
expect(error).to.be.an('error');
expect(error).to.deep.include({
message: 'Unauthorized',
response: {
data: {
code: 401
},
status: 401
}
});
});
it('gets the api token out of local storage', function() {
expect(localStorage.getItem).to.be.calledWith('api_token');
});
it("includes the original error's content if one is provided", function() {
const expectedError = new Error(faker.hacker.phrase());
expectedError[faker.hacker.noun()] = faker.helpers.createTransaction();
it('gets the expires at information out of local storage (and coreces it to be a number)', function() {
expect(localStorage.getItem).to.be.calledWith('expires_at');
});
const error = auth0WebAuth._generateUnauthorizedError(expectedError);
it('returns an object with the session info', function() {
expect(session).to.deep.equal(expectedSessionInfo);
expect(error.response.data).to.include(expectedError);
});
});
describe('_parseWebAuthHash', function() {
let auth0WebAuth;
it('indicates the SDK originated the error if no original error is provided', function() {
const error = auth0WebAuth._generateUnauthorizedError();
beforeEach(function() {
this.sandbox.stub(Auth0WebAuth.prototype, '_loadSession');
auth0WebAuth = new Auth0WebAuth(sdk);
auth0WebAuth.logIn();
expect(error.fromSdk).to.be.true;
});
});
describe('_parseHash', function() {
context('successfully parsing the hash', function() {

@@ -1191,3 +1032,3 @@ let expectedHash;

const auth0WebAuth = new Auth0WebAuth(sdk);
promise = auth0WebAuth._parseWebAuthHash();
promise = auth0WebAuth._parseHash();
});

@@ -1218,3 +1059,3 @@

it('returns with a rejected promise', function() {
return expect(auth0WebAuth._parseWebAuthHash()).to.be.rejectedWith(
return expect(auth0WebAuth._parseHash()).to.be.rejectedWith(
expectedError

@@ -1237,3 +1078,3 @@ );

it('returns with a rejected promise', function() {
return expect(auth0WebAuth._parseWebAuthHash()).to.be.rejectedWith(
return expect(auth0WebAuth._parseHash()).to.be.rejectedWith(
'No valid tokens returned from auth0'

@@ -1245,54 +1086,2 @@ );

describe('_saveSession', function() {
let auth0WebAuth;
let expectedSessionInfo;
let localStorage;
beforeEach(function() {
expectedSessionInfo = {
accessToken: faker.internet.password(),
apiToken: faker.internet.password(),
expiresAt: faker.date.future().getTime()
};
this.sandbox.stub(Auth0WebAuth.prototype, '_loadSession');
localStorage = {
setItem: this.sandbox.stub()
};
global.localStorage = localStorage;
auth0WebAuth = new Auth0WebAuth(sdk);
auth0WebAuth._saveSession(expectedSessionInfo);
});
afterEach(function() {
delete global.localStorage;
});
it('saves the session info in the auth module instance', function() {
expect(auth0WebAuth._sessionInfo).to.equal(expectedSessionInfo);
});
it('saves the access token to local storage', function() {
expect(localStorage.setItem).to.be.calledWith(
'access_token',
expectedSessionInfo.accessToken
);
});
it('saves the api access token to local storage', function() {
expect(localStorage.setItem).to.be.calledWith(
'api_token',
expectedSessionInfo.apiToken
);
});
it('saves the expires at information to local storage (as a string)', function() {
expect(localStorage.setItem).to.be.calledWith(
'expires_at',
`${expectedSessionInfo.expiresAt}`
);
});
});
describe('_scheduleTokenRenewal', function() {

@@ -1302,3 +1091,3 @@ let auth0WebAuth;

let expectedDelay;
let getUpdatedSessionInfo;
let getUpdatedSession;
let initialTimeout;

@@ -1318,6 +1107,5 @@

this.sandbox.spy(clock, 'clearTimeout');
this.sandbox.stub(Auth0WebAuth.prototype, '_loadSession');
getUpdatedSessionInfo = this.sandbox.stub(
getUpdatedSession = this.sandbox.stub(
Auth0WebAuth.prototype,
'_getUpdatedSessionInfo'
'_getUpdatedSession'
);

@@ -1328,4 +1116,6 @@ this.sandbox.spy(clock, 'setTimeout');

auth0WebAuth._sessionInfo = { expiresAt };
auth0WebAuth._tokenRenewalTimeout = initialTimeout;
auth0WebAuth._scheduleTokenRenewal();
auth0WebAuth._sessionRenewalTimeout = initialTimeout;
scheduleSessionRefresh.restore();
auth0WebAuth._scheduleSessionRefresh();
});

@@ -1342,5 +1132,5 @@

it('sets up a renewal timeout', function() {
expect(auth0WebAuth._tokenRenewalTimeout).to.not.equal(initialTimeout);
expect(auth0WebAuth._tokenRenewalTimeout).to.be.an('object');
expect(auth0WebAuth._tokenRenewalTimeout.id).to.be.a('number');
expect(auth0WebAuth._sessionRenewalTimeout).to.not.equal(initialTimeout);
expect(auth0WebAuth._sessionRenewalTimeout).to.be.an('object');
expect(auth0WebAuth._sessionRenewalTimeout.id).to.be.a('number');

@@ -1356,5 +1146,60 @@ expect(clock.setTimeout).to.be.calledOnce;

expect(getUpdatedSessionInfo).to.be.calledOnce;
expect(getUpdatedSession).to.be.calledOnce;
});
});
describe('_storeSession', function() {
let auth0WebAuth;
let expectedSessionInfo;
let localStorage;
beforeEach(function() {
expectedSessionInfo = {
accessToken: faker.internet.password(),
expiresAt: faker.date.future().getTime()
};
localStorage = {
setItem: this.sandbox.stub()
};
global.localStorage = localStorage;
auth0WebAuth = new Auth0WebAuth(sdk);
auth0WebAuth._storeSession({
accessToken: expectedSessionInfo.accessToken,
expiresIn: (expectedSessionInfo.expiresAt - Date.now()) / 1000
});
});
afterEach(function() {
delete global.localStorage;
});
it('saves the access token to local storage', function() {
expect(localStorage.setItem).to.be.calledWith(
'access_token',
expectedSessionInfo.accessToken
);
});
it('saves the `expiresAt` information to local storage (as a string)', function() {
expect(localStorage.setItem).to.be.calledWith(
'expires_at',
`${expectedSessionInfo.expiresAt}`
);
});
it('saves the access token in the auth module instance', function() {
expect(auth0WebAuth._sessionInfo.accessToken).to.equal(
expectedSessionInfo.accessToken
);
});
it('saves the `expiresAt` information in the auth module instance', function() {
expect(auth0WebAuth._sessionInfo.expiresAt).to.equal(
expectedSessionInfo.expiresAt
);
});
});
});

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc