From 5b9df4def4efa78672be6b77613404fa60863ed4 Mon Sep 17 00:00:00 2001 From: meriamBenSassi Date: Sun, 30 May 2021 21:43:15 +0200 Subject: [PATCH] fix(requestbuilder): regenerate access token if refresh fails --- src/RequestBuilder.ts | 73 ++++++++++++++++++--------------- test/request-builder.test.ts | 49 ++++++++++++++++++++++ test/utils/fake-server.utils.ts | 3 +- 3 files changed, 90 insertions(+), 35 deletions(-) diff --git a/src/RequestBuilder.ts b/src/RequestBuilder.ts index 68c4b47..2d9cb4b 100644 --- a/src/RequestBuilder.ts +++ b/src/RequestBuilder.ts @@ -93,46 +93,19 @@ export class RequestBuilder { } if (this.accessTokenInstance !== undefined && isAccessTokenExpired && !isRefreshTokenExpired) { - const token: AxiosResponse = await Axios.post( - `${this.baseUrl}/v${this.apiVersion}/oauth/token`, - stringify({ - refresh_token: this.accessTokenInstance.refresh_token, - grant_type: 'refresh_token', - client_id: this.credentials.clientId, - client_secret: this.credentials.clientSecret, - }), - { - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - }, - }, - ); + let token: AxiosResponse; + try { + token = await this.generateToken(true); + } catch (err) { + token = await this.generateToken(); + } this.accessTokenInstance = this.getAccessTokenInstance(token.data); return `Bearer ${this.accessTokenInstance.access_token}`; } - const grantType: string = - this.credentials.username !== undefined && this.credentials.password !== undefined - ? 'password' - : 'client_credentials'; - - const request: AxiosResponse = await Axios.post( - `${this.baseUrl}/v${this.apiVersion}/oauth/token`, - stringify({ - client_id: this.credentials.clientId, - client_secret: this.credentials.clientSecret, - username: this.credentials.username, - password: this.credentials.password, - grant_type: grantType, - }), - { - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - }, - }, - ); + const request: AxiosResponse = await this.generateToken(); this.accessTokenInstance = this.getAccessTokenInstance(request.data); @@ -195,6 +168,38 @@ export class RequestBuilder { // eslint-disable-next-line no-underscore-dangle return this._authorizationHeader; } + + /** + * Call the auth route to generate a token + * @param fromRefreshToken if set to true, use the grant type refresh_token + * @returns + */ + private generateToken(fromRefreshToken: boolean = false): Promise> { + const grantType: string = + this.credentials.username !== undefined && this.credentials.password !== undefined + ? 'password' + : 'client_credentials'; + + const body = fromRefreshToken + ? { + refresh_token: (this.accessTokenInstance as AccessToken).refresh_token, + grant_type: 'refresh_token', + client_id: this.credentials.clientId, + client_secret: this.credentials.clientSecret, + } + : { + client_id: this.credentials.clientId, + client_secret: this.credentials.clientSecret, + username: this.credentials.username, + password: this.credentials.password, + grant_type: grantType, + }; + return Axios.post(`${this.baseUrl}/v${this.apiVersion}/oauth/token`, stringify(body), { + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + }); + } } /** diff --git a/test/request-builder.test.ts b/test/request-builder.test.ts index ec552f6..8e7676b 100644 --- a/test/request-builder.test.ts +++ b/test/request-builder.test.ts @@ -473,4 +473,53 @@ describe('Tests related to the RequestBuilder class', () => { expect((requestBuilder as any).accessTokenInstance).toBeUndefined(); expect((requestBuilder as any)._authorizationHeader).toBeDefined(); }); + + it('RB012 - should call the token API twice - error on refresh', async () => { + const requestBuilder: RequestBuilder = new RequestBuilder(baseUrl, { + clientId: 'clientId', + clientSecret: 'clientSecret', + }); + const oAuthServer: nock.Scope = getOAuthServer({ + baseUrl, + isRefreshToken: false, + isUserPassword: false, + nbOfCalls: 2, + expiresIn: 0.05, + refreshExpiresIn: 1000, + }); + const refreshTokenServer: nock.Scope = getOAuthServer({ + baseUrl, + isRefreshToken: true, + isUserPassword: false, + nbOfCalls: 1, + error: 'an error occured', + }); + const randomAlgoanServer: nock.Scope = getFakeAlgoanServer({ + baseUrl, + method: 'get', + path: '/', + response: [], + nbOfCalls: 2, + }); + + await requestBuilder.request({ + method: 'GET', + url: '/', + }); + + expect(oAuthServer.isDone()).toBeFalsy(); + expect(randomAlgoanServer.isDone()).toBeFalsy(); + expect((requestBuilder as any).accessTokenInstance).toBeDefined(); + expect(refreshTokenServer.isDone()).toBeFalsy(); + + await delay(500); + + await requestBuilder.request({ + method: 'GET', + url: '/', + }); + expect(refreshTokenServer.isDone()).toBeTruthy(); + expect(oAuthServer.isDone()).toBeTruthy(); + expect(randomAlgoanServer.isDone()).toBeTruthy(); + }); }); diff --git a/test/utils/fake-server.utils.ts b/test/utils/fake-server.utils.ts index b53d008..9f5f0bd 100644 --- a/test/utils/fake-server.utils.ts +++ b/test/utils/fake-server.utils.ts @@ -14,6 +14,7 @@ export const getOAuthServer = ( expiresIn?: number; refreshExpiresIn?: number; version?: number; + error?: string; } = { baseUrl: 'http://localhost:3000', isRefreshToken: false, @@ -46,7 +47,7 @@ export const getOAuthServer = ( nockInstance = nockInstance.times(params.nbOfCalls); } - return nockInstance.reply(200, responseBody); + return params.error ? nockInstance.replyWithError(params.error) : nockInstance.reply(200, responseBody); }; /**