From f3f7ad9ad778009dabe232a1f5c53cc3bb33df88 Mon Sep 17 00:00:00 2001 From: Dustin Popp Date: Thu, 9 Apr 2020 10:33:17 -0500 Subject: [PATCH] * feat(jwt-token-manager): expose the `saveTokenInfo` to subclasses 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. --- auth/token-managers/jwt-token-manager.ts | 85 ++++++++++++------------ test/unit/jwt-token-manager.test.js | 10 +-- 2 files changed, 48 insertions(+), 47 deletions(-) diff --git a/auth/token-managers/jwt-token-manager.ts b/auth/token-managers/jwt-token-manager.ts index e7a839639..0e93a0b79 100644 --- a/auth/token-managers/jwt-token-manager.ts +++ b/auth/token-managers/jwt-token-manager.ts @@ -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 @@ -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(); }); @@ -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 * @@ -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); - } } diff --git a/test/unit/jwt-token-manager.test.js b/test/unit/jwt-token-manager.test.js index 3785ada9d..2891dd3f6 100644 --- a/test/unit/jwt-token-manager.test.js +++ b/test/unit/jwt-token-manager.test.js @@ -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(); }); @@ -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); @@ -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(); }); });