From e0d612af928f778e55a0c1e504e85d7b6daf3d78 Mon Sep 17 00:00:00 2001 From: DonOmalVindula Date: Fri, 10 Mar 2023 21:54:14 +0530 Subject: [PATCH 1/3] Add logic to gracefully retry 401 error --- lib/src/helpers/authentication-helper.ts | 38 ++++++++++++++++++++++-- lib/src/models/http-client.ts | 12 +++++++- 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/lib/src/helpers/authentication-helper.ts b/lib/src/helpers/authentication-helper.ts index f3b12d9c..c1dcd419 100644 --- a/lib/src/helpers/authentication-helper.ts +++ b/lib/src/helpers/authentication-helper.ts @@ -32,7 +32,7 @@ import { TokenResponse } from "@asgardeo/auth-js"; import { SPAHelper } from "./spa-helper"; -import { Message, SPAUtils, SessionManagementHelperInterface } from ".."; +import { HttpRequestInterface, Message, SPAUtils, SessionManagementHelperInterface } from ".."; import { ACCESS_TOKEN_INVALID, CHECK_SESSION_SIGNED_IN, @@ -61,6 +61,7 @@ export class AuthenticationHelper< protected _authenticationClient: AsgardeoAuthClient; protected _dataLayer: DataLayer; protected _spaHelper: SPAHelper; + protected _isTokenRefreshing: boolean; public constructor( authClient: AsgardeoAuthClient, @@ -69,6 +70,7 @@ export class AuthenticationHelper< this._authenticationClient = authClient; this._dataLayer = this._authenticationClient.getDataLayer(); this._spaHelper = spaHelper; + this._isTokenRefreshing = false; } public enableHttpHandler(httpClient: HttpClientInstance): void { @@ -133,7 +135,7 @@ export class AuthenticationHelper< enableRetrievingSignOutURLFromSession(config); } - if (config.returnsSession) { + if (config.returnsSession) { this._spaHelper.refreshAccessTokenAutomatically(this); return this._authenticationClient.getBasicUserInfo(); @@ -189,6 +191,22 @@ export class AuthenticationHelper< } } + retryFailedRequests = async (failedRequest: HttpRequestInterface): Promise => { + if (this._isTokenRefreshing) { + return new Promise(() => setTimeout(() => { + return this.retryFailedRequests(failedRequest); + }, 500)); + } else { + return this.httpRequest(failedRequest.httpClient, + failedRequest.requestConfig, + failedRequest.isHttpHandlerEnabled, + failedRequest.httpErrorCallback, + failedRequest.httpFinishCallback, + failedRequest.enableRetrievingSignOutURLFromSession + ); + } + } + public async httpRequest( httpClient: HttpClientInstance, requestConfig: HttpRequestConfig, @@ -214,18 +232,32 @@ export class AuthenticationHelper< if (matches) { return httpClient .request(requestConfig) - .then((response: HttpResponse) => { + .then((response: HttpResponse) => { return Promise.resolve(response); }) .catch(async (error: HttpError) => { if (error?.response?.status === 401 || !error?.response) { + if (this._isTokenRefreshing) { + return this.retryFailedRequests({ + enableRetrievingSignOutURLFromSession, + httpClient, + httpErrorCallback, + httpFinishCallback, + isHttpHandlerEnabled, + requestConfig + }); + } + this._isTokenRefreshing = true; // Try to refresh the token let refreshAccessTokenResponse: BasicUserInfo; + try { refreshAccessTokenResponse = await this.refreshAccessToken( enableRetrievingSignOutURLFromSession ); + this._isTokenRefreshing = false; } catch (refreshError: any) { + this._isTokenRefreshing = false; if (isHttpHandlerEnabled) { if (typeof httpErrorCallback === "function") { await httpErrorCallback({ diff --git a/lib/src/models/http-client.ts b/lib/src/models/http-client.ts index 521542c9..4b546217 100755 --- a/lib/src/models/http-client.ts +++ b/lib/src/models/http-client.ts @@ -17,7 +17,8 @@ */ import { AxiosRequestConfig } from "axios"; -import { HttpError, HttpResponse } from "."; +import { HttpClientInstance, HttpError, HttpResponse } from "."; +import { SPACustomGrantConfig } from ".."; export interface HttpClient { requestStartCallback: () => void; @@ -33,6 +34,15 @@ export interface HttpRequestConfig extends AxiosRequestConfig { startTimeInMs?: number } +export interface HttpRequestInterface { + httpClient: HttpClientInstance, + requestConfig: HttpRequestConfig, + isHttpHandlerEnabled?: boolean, + httpErrorCallback?: (error: HttpError) => void | Promise, + httpFinishCallback?: () => void, + enableRetrievingSignOutURLFromSession?: (config: SPACustomGrantConfig) => void +} + export { AxiosResponse as HttpResponse, Method as HttpMethod, From 04737253c287aab336fd68679098e9eb02a3cdb5 Mon Sep 17 00:00:00 2001 From: DonOmalVindula Date: Fri, 10 Mar 2023 21:58:37 +0530 Subject: [PATCH 2/3] Remove whitespace --- lib/src/helpers/authentication-helper.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/src/helpers/authentication-helper.ts b/lib/src/helpers/authentication-helper.ts index c1dcd419..c7b04e8f 100644 --- a/lib/src/helpers/authentication-helper.ts +++ b/lib/src/helpers/authentication-helper.ts @@ -135,7 +135,7 @@ export class AuthenticationHelper< enableRetrievingSignOutURLFromSession(config); } - if (config.returnsSession) { + if (config.returnsSession) { this._spaHelper.refreshAccessTokenAutomatically(this); return this._authenticationClient.getBasicUserInfo(); @@ -232,7 +232,7 @@ export class AuthenticationHelper< if (matches) { return httpClient .request(requestConfig) - .then((response: HttpResponse) => { + .then((response: HttpResponse) => { return Promise.resolve(response); }) .catch(async (error: HttpError) => { @@ -250,7 +250,6 @@ export class AuthenticationHelper< this._isTokenRefreshing = true; // Try to refresh the token let refreshAccessTokenResponse: BasicUserInfo; - try { refreshAccessTokenResponse = await this.refreshAccessToken( enableRetrievingSignOutURLFromSession From cdf5c88db78912c615361b85ffd88cf9f11ce8c9 Mon Sep 17 00:00:00 2001 From: DonOmalVindula Date: Fri, 10 Mar 2023 22:08:23 +0530 Subject: [PATCH 3/3] Change function signature --- lib/src/helpers/authentication-helper.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/helpers/authentication-helper.ts b/lib/src/helpers/authentication-helper.ts index c7b04e8f..2cb96461 100644 --- a/lib/src/helpers/authentication-helper.ts +++ b/lib/src/helpers/authentication-helper.ts @@ -191,7 +191,7 @@ export class AuthenticationHelper< } } - retryFailedRequests = async (failedRequest: HttpRequestInterface): Promise => { + protected async retryFailedRequests (failedRequest: HttpRequestInterface): Promise { if (this._isTokenRefreshing) { return new Promise(() => setTimeout(() => { return this.retryFailedRequests(failedRequest);