Skip to content

Commit

Permalink
* feat(jwt-token-manager): expose the saveTokenInfo to subclasses
Browse files Browse the repository at this point in the history
additionally, change `saveTokenInfo` to accept the entire response and extract
the body within the method. that way, there can be subclass implementations for
authentication mechanisms that include the token somewhere other than the body,
like the headers.
  • Loading branch information
dpopp07 authored Apr 9, 2020
1 parent 41f4dab commit f3f7ad9
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 47 deletions.
85 changes: 43 additions & 42 deletions auth/token-managers/jwt-token-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ export class JwtTokenManager {
// If refresh needed, kick one off
if (this.tokenNeedsRefresh()) {
this.requestToken().then(tokenResponse => {
this.saveTokenInfo(tokenResponse.result);
this.saveTokenInfo(tokenResponse);
});
}
// 2. use valid, managed token
Expand Down Expand Up @@ -161,7 +161,7 @@ export class JwtTokenManager {
} else {
this.requestTime = currentTime;
return this.requestToken().then(tokenResponse => {
this.saveTokenInfo(tokenResponse.result);
this.saveTokenInfo(tokenResponse);
this.pendingRequests.forEach(({resolve}) => {
resolve();
});
Expand All @@ -188,6 +188,47 @@ export class JwtTokenManager {
return Promise.reject(err);
}

/**
* Save the JWT service response and the calculated expiration time to the object's state.
*
* @param tokenResponse - Response object from JWT service request
* @protected
* @returns {void}
*/
protected saveTokenInfo(tokenResponse): void {
const responseBody = tokenResponse.result || {};
const accessToken = responseBody[this.tokenName];

if (!accessToken) {
const err = 'Access token not present in response';
logger.error(err);
throw new Error(err);
}

// the time of expiration is found by decoding the JWT access token
// exp is the time of expire and iat is the time of token retrieval
const decodedResponse = jwt.decode(accessToken);
if (!decodedResponse) {
const err = 'Access token recieved is not a valid JWT'
logger.error(err);
throw new Error(err);
}

const { exp, iat } = decodedResponse;
// There are no required claims in JWT
if (!exp || !iat) {
this.expireTime = 0;
this.refreshTime = 0;
} else {
const fractionOfTtl = 0.8;
const timeToLive = exp - iat;
this.expireTime = exp;
this.refreshTime = exp - (timeToLive * (1.0 - fractionOfTtl));
}

this.tokenInfo = extend({}, responseBody);
}

/**
* Check if currently stored token is expired
*
Expand Down Expand Up @@ -225,44 +266,4 @@ export class JwtTokenManager {

return true;
}

/**
* Save the JWT service response and the calculated expiration time to the object's state.
*
* @param tokenResponse - Response object from JWT service request
* @private
* @returns {void}
*/
private saveTokenInfo(tokenResponse): void {
const accessToken = tokenResponse[this.tokenName];

if (!accessToken) {
const err = 'Access token not present in response';
logger.error(err);
throw new Error(err);
}

// the time of expiration is found by decoding the JWT access token
// exp is the time of expire and iat is the time of token retrieval
const decodedResponse = jwt.decode(accessToken);
if (!decodedResponse) {
const err = 'Access token recieved is not a valid JWT'
logger.error(err);
throw new Error(err);
}

const { exp, iat } = decodedResponse;
// There are no required claims in JWT
if (!exp || !iat) {
this.expireTime = 0;
this.refreshTime = 0;
} else {
const fractionOfTtl = 0.8;
const timeToLive = exp - iat;
this.expireTime = exp;
this.refreshTime = exp - (timeToLive * (1.0 - fractionOfTtl));
}

this.tokenInfo = extend({}, tokenResponse);
}
}
10 changes: 5 additions & 5 deletions test/unit/jwt-token-manager.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -268,11 +268,11 @@ describe('JWT Token Manager', () => {
.spyOn(jwt, 'decode')
.mockImplementation(token => ({ iat: 10, exp: 100 }));

const tokenResponse = { access_token: ACCESS_TOKEN };
const tokenResponse = { result: { access_token: ACCESS_TOKEN } };

instance.saveTokenInfo(tokenResponse);
expect(instance.expireTime).toBe(100);
expect(instance.tokenInfo).toEqual(tokenResponse);
expect(instance.tokenInfo).toEqual(tokenResponse.result);
decodeSpy.mockRestore();
});

Expand All @@ -291,7 +291,7 @@ describe('JWT Token Manager', () => {
.spyOn(jwt, 'decode')
.mockImplementation(token => ({ iat: 100, exp: 200 }));

const tokenResponse = { access_token: ACCESS_TOKEN };
const tokenResponse = { result: { access_token: ACCESS_TOKEN } };

instance.saveTokenInfo(tokenResponse);
expect(instance.refreshTime).toBe(180);
Expand All @@ -309,11 +309,11 @@ describe('JWT Token Manager', () => {
.spyOn(jwt, 'decode')
.mockImplementation(token => ({ foo: 0, bar: 100 }));

const tokenResponse = { access_token: ACCESS_TOKEN };
const tokenResponse = { result: { access_token: ACCESS_TOKEN } };

instance.saveTokenInfo(tokenResponse);
expect(instance.expireTime).toBe(0);
expect(instance.tokenInfo).toEqual(tokenResponse);
expect(instance.tokenInfo).toEqual(tokenResponse.result);
decodeSpy.mockRestore();
});
});
Expand Down

0 comments on commit f3f7ad9

Please sign in to comment.