Skip to content
This repository was archived by the owner on Nov 25, 2020. It is now read-only.

Commit

Permalink
feat(project): store the complete response in storage. AuthService.ge…
Browse files Browse the repository at this point in the history
…tTimeLeft() added

BREAKING: accessTokenStorage renamed to storageKey and used as storage key for the complete response
BREAKING: internal works of Authentication refactored for performance. Some methods might be renamed or be removed
  • Loading branch information
doktordirk committed Apr 12, 2016
1 parent 479e338 commit b98d839
Show file tree
Hide file tree
Showing 6 changed files with 315 additions and 241 deletions.
38 changes: 24 additions & 14 deletions src/authService.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ export class AuthService {
*
*/
getAccessToken() {
return this.authentication.accessToken;
return this.authentication.getAccessToken();
}

getCurrentToken() {
Expand All @@ -85,7 +85,7 @@ export class AuthService {
*
*/
getRefreshToken() {
return this.authentication.refreshToken;
return this.authentication.getRefreshToken();
}

/**
Expand All @@ -101,8 +101,8 @@ export class AuthService {
// auto-update token?
if (!authenticated
&& this.config.autoUpdateToken
&& this.authentication.accessToken
&& this.authentication.refreshToken) {
&& this.authentication.getAccessToken()
&& this.authentication.getRefreshToken()) {
authenticated = this.updateToken();
}

Expand All @@ -119,10 +119,20 @@ export class AuthService {
return authenticated;
}

/**
* Gets remaining time in seconds
*
* @returns {Number} remaing time for JWT tokens, NaN for all other tokesn
*
*/
getTimeLeft() {
return this.authentication.getTimeLeft();
}

/**
* Gets exp from token payload and compares to current time
*
* @returns {Boolean | undefined} undefined: Non-JWT payload, true: unexpired JWT tokens, false: else
* @returns {Boolean} getTimeLeft>0 time for JWT tokens, undefined other tokesn
*
*/
isTokenExpired() {
Expand All @@ -136,7 +146,7 @@ export class AuthService {
*
*/
getTokenPayload() {
return this.authentication.getTokenPayload();
return this.authentication.getPayload();
}

/**
Expand All @@ -146,24 +156,24 @@ export class AuthService {
*
*/
updateToken() {
if (!this.authentication.refreshToken) {
if (!this.authentication.getRefreshToken()) {
return Promise.reject(new Error('refreshToken not set'));
}

if (this.authentication.updateTokenCallstack.length === 0) {
const content = {
grant_type: 'refresh_token',
refresh_token: this.authentication.refreshToken,
refresh_token: this.authentication.getRefreshToken(),
client_id: this.config.clientId ? this.config.clientId : undefined
};

this.client.post(this.config.withBase(this.config.loginUrl), content)
.then(response => {
this.authentication.setTokensFromResponse(response);
this.authentication.responseObject = response;
this.authentication.resolveUpdateTokenCallstack(response);
})
.catch(err => {
this.authentication.removeTokens();
this.authentication.responseObject = null;
this.authentication.resolveUpdateTokenCallstack(Promise.reject(err));
});
}
Expand Down Expand Up @@ -201,7 +211,7 @@ export class AuthService {
return this.client.post(this.config.withBase(this.config.signupUrl), data)
.then(response => {
if (this.config.loginOnSignup) {
this.authentication.setTokensFromResponse(response);
this.authentication.responseObject = response;
}
this.authentication.redirect(redirectUri, this.config.signupRedirect);

Expand Down Expand Up @@ -237,7 +247,7 @@ export class AuthService {

return this.client.post(this.config.withBase(this.config.loginUrl), data)
.then(response => {
this.authentication.setTokensFromResponse(response);
this.authentication.responseObject = response;

this.authentication.redirect(redirectUri, this.config.loginRedirect);

Expand All @@ -255,7 +265,7 @@ export class AuthService {
*/
logout(redirectUri) {
return new Promise(resolve => {
this.authentication.removeTokens();
this.authentication.responseObject = null;

this.authentication.redirect(redirectUri, this.config.logoutRedirect);

Expand Down Expand Up @@ -283,7 +293,7 @@ export class AuthService {
authenticate(name, redirectUri, userData = {}) {
return this.authentication.authenticate(name, userData)
.then(response => {
this.authentication.setTokensFromResponse(response);
this.authentication.responseObject = response;

this.authentication.redirect(redirectUri, this.config.loginRedirect);

Expand Down
176 changes: 94 additions & 82 deletions src/authentication.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,16 @@ export class Authentication {
this.oAuth1 = oAuth1;
this.oAuth2 = oAuth2;
this.updateTokenCallstack = [];
this.accessToken = null;
this.refreshToken = null;
this.payload = null;
this.exp = null;
this.hasDataStored = false;
}


/* deprecated methods */

getLoginRoute() {
console.warn('DEPRECATED: Authentication.getLoginRoute. Use baseConfig.loginRoute instead.');
return this.config.loginRoute;
Expand All @@ -41,88 +49,119 @@ export class Authentication {
}

getToken() {
console.warn('DEPRECATED: Authentication.getToken. Use .accessToken instead.');
console.warn('DEPRECATED: Authentication.getToken. Use .getAccessToken() instead.');
return this.getAccessToken();
}

/* getters/setters for responseObject */

get responseObject() {
return JSON.parse(this.storage.get(this.config.storageKey));
}

set responseObject(response) {
if (response) {
this.getDataFromResponse(response);
return this.storage.set(this.config.storageKey, JSON.stringify(response));
}
this.deleteData();
return this.storage.remove(this.config.storageKey);
}


/* get data, update if needed first */

getAccessToken() {
if (!this.hasDataStored) this.getDataFromResponse(this.responseObject);
return this.accessToken;
}

getRefreshToken() {
console.warn('DEPRECATED: Authentication.getRefreshToken. Use .refreshToken instead.');
if (!this.hasDataStored) this.getDataFromResponse(this.responseObject);
return this.refreshToken;
}
/* getters/setters for tokens */

get accessToken() {
return this.storage.get(this.config.accessTokenStorage);
getPayload() {
if (!this.hasDataStored) this.getDataFromResponse(this.responseObject);
return this.payload;
}

set accessToken(newToken) {
if (newToken) {
return this.storage.set(this.config.accessTokenStorage, newToken);
}
return this.storage.remove(this.config.accessTokenStorage);
getExp() {
if (!this.hasDataStored) this.getDataFromResponse(this.responseObject);
return this.exp;
}

get refreshToken() {
return this.storage.get(this.config.refreshTokenStorage);

/* get status from data */

getTimeLeft() {
const exp = this.getExp();
return Number.isNaN(exp) ? NaN : exp - Math.round(new Date().getTime() / 1000);
}

set refreshToken(newToken) {
if (newToken) {
return this.storage.set(this.config.refreshTokenStorage, newToken);
}
return this.storage.remove(this.config.refreshTokenStorage);
isTokenExpired() {
const timeLeft = this.getTimeLeft();
return Number.isNaN(timeLeft) ? undefined : timeLeft < 0;
}

isAuthenticated() {
const isTokenExpired = this.isTokenExpired();
if (isTokenExpired === undefined ) return this.accessToken ? true : false;
return !isTokenExpired;
}

/* work with the token */

getPayload() {
console.warn('DEPRECATED: Authentication.getPayload(). Use .getTokenPayload() instead.');
return this.getTokenPayload();
}
/* get and set from response */

getDataFromResponse(response) {
const config = this.config;

this.accessToken = this.getTokenFromResponse(response, config.accessTokenProp, config.accessTokenName, config.accessTokenRoot);

getTokenPayload() {
const accessToken = this.accessToken;
if (accessToken && accessToken.split('.').length === 3) {
this.refreshToken = null;
if (config.useRefreshToken) {
try {
const base64Url = this.accessToken.split('.')[1];
const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
return JSON.parse(decodeURIComponent(escape(window.atob(base64))));
this.refreshToken = this.getTokenFromResponse(response, config.refreshTokenProp, config.refreshTokenName, config.refreshTokenRoot);
} catch (e) {
return null;
this.refreshToken = null;
}
}
return null;
}

isTokenExpired() {
const payload = this.getTokenPayload();
const exp = payload && payload.exp;
if (exp) {
return Math.round(new Date().getTime() / 1000) > exp;
}
return undefined;
}
let payload = null;

isAuthenticated() {
// FAIL: There's no token, so user is not authenticated.
if (!this.accessToken) {
return false;
}
// PASS: There is a token, but in a different format
if (this.accessToken.split('.').length !== 3) {
return true;
if (this.accessToken && this.accessToken.split('.').length === 3) {
try {
const base64 = this.accessToken.split('.')[1].replace(/-/g, '+').replace(/_/g, '/');
payload = JSON.parse(decodeURIComponent(escape(window.atob(base64))));
} catch (e) {
payload = null;
}
}
// PASS: Non-JWT token that looks like JWT (isTokenExpired === undefined)
// PASS or FAIL: test isTokenExpired.
return this.isTokenExpired() !== true;

this.payload = payload;
this.exp = payload ? parseInt(payload.exp, 10) : NaN;

this.hasDataStored = true;

return {
accessToken: this.accessToken,
refreshToken: this.refreshToken,
payload: this.payload,
exp: this.exp
};
}

deleteData() {
this.accessToken = null;
this.refreshToken = null;
this.payload = null;
this.exp = null;

/* get and set token from response */
this.hasDataStored = false;
}

getTokenFromResponse(response, tokenProp, tokenName, tokenRoot) {
if (!response) return null;
if (!response) return undefined;

const responseTokenProp = response[tokenProp];

Expand All @@ -135,38 +174,11 @@ export class Authentication {
return tokenRootData ? tokenRootData[tokenName] : responseTokenProp[tokenName];
}

return response[tokenName] === undefined ? null : response[tokenName];
}
const token = response[tokenName] === undefined ? null : response[tokenName];

setAccessTokenFromResponse(response) {
const config = this.config;
const newToken = this.getTokenFromResponse(response, config.accessTokenProp, config.accessTokenName, config.accessTokenRoot);
if (!token) throw new Error('Token not found in response');

if (!newToken) throw new Error('Token not found in response');

this.accessToken = newToken;
}

setRefreshTokenFromResponse(response) {
const config = this.config;
const newToken = this.getTokenFromResponse(response, config.refreshTokenProp, config.refreshTokenName, config.refreshTokenRoot);

if (!newToken) throw new Error('Token not found in response');

this.refreshToken = newToken;
}

setTokensFromResponse(response) {
this.setAccessTokenFromResponse(response);

if (this.config.useRefreshToken) {
this.setRefreshTokenFromResponse(response);
}
}

removeTokens() {
this.accessToken = null;
this.refreshToken = null;
return token;
}


Expand Down
11 changes: 4 additions & 7 deletions src/baseConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,10 +122,7 @@ export class BaseConfig {
// Determines the `window` property name upon which aurelia-authentication data is stored (Default: `window.localStorage`)
storage = 'localStorage';
// The property name used when storing the access token locally
accessTokenStorage = 'aurelia_access_token';
// The property name used when storing the refresh token locally
refreshTokenStorage = 'aurelia_refresh_token';

storageKey = 'aurelia_authentication';

//OAuth provider specific related configuration
// ============================================
Expand Down Expand Up @@ -296,17 +293,17 @@ export class BaseConfig {
console.warn('BaseConfig.tokenName is deprecated. Use BaseConfig.accessTokenName instead.');
this._tokenName = tokenName;
this.accessTokenName = tokenName;
this.accessTokenStorage = this.tokenPrefix ? this.tokenPrefix + '_' + this.tokenName : this.tokenName;
this.storageKey = this.tokenPrefix ? this.tokenPrefix + '_' + this.tokenName : this.tokenName;
return tokenName;
}
get tokenName() {
return this._tokenName;
}

set tokenPrefix(tokenPrefix) {
console.warn('BaseConfig.tokenPrefix is deprecated. Use BaseConfig.accessTokenStorage instead.');
console.warn('BaseConfig.tokenPrefix is deprecated. Use BaseConfig.storageKey instead.');
this._tokenPrefix = tokenPrefix;
this.accessTokenStorage = this.tokenPrefix ? this.tokenPrefix + '_' + this.tokenName : this.tokenName;
this.storageKey = this.tokenPrefix ? this.tokenPrefix + '_' + this.tokenName : this.tokenName;
return tokenPrefix;
}
get tokenPrefix() {
Expand Down
Loading

0 comments on commit b98d839

Please sign in to comment.