diff --git a/.changeset/light-queens-rest.md b/.changeset/light-queens-rest.md new file mode 100644 index 000000000..6cdfdf1e4 --- /dev/null +++ b/.changeset/light-queens-rest.md @@ -0,0 +1,5 @@ +--- +'@asgardeo/browser': minor +--- + +Fix failure of calling authenticated APIs from secondary AsgardeoProvider Instances in Multi Provider scenarios diff --git a/packages/browser/src/__legacy__/clients/main-thread-client.ts b/packages/browser/src/__legacy__/clients/main-thread-client.ts index cd0d9908d..79900c12b 100755 --- a/packages/browser/src/__legacy__/clients/main-thread-client.ts +++ b/packages/browser/src/__legacy__/clients/main-thread-client.ts @@ -85,7 +85,7 @@ export const MainThreadClient = async ( let _getSignOutURLFromSessionStorage: boolean = false; - const _httpClient: HttpClientInstance = HttpClient.getInstance(); + const _httpClient: HttpClientInstance = HttpClient.getInstance(instanceID); let _isHttpHandlerEnabled: boolean = true; let _httpErrorCallback: (error: HttpError) => void | Promise; let _httpFinishCallback: () => void; diff --git a/packages/browser/src/__legacy__/clients/web-worker-client.ts b/packages/browser/src/__legacy__/clients/web-worker-client.ts index 615295409..0ce4518d1 100755 --- a/packages/browser/src/__legacy__/clients/web-worker-client.ts +++ b/packages/browser/src/__legacy__/clients/web-worker-client.ts @@ -365,8 +365,8 @@ export const WebWorkerClient = async ( } }; - const message: Message> = { - data: config, + const message: Message & {instanceID: number}> = { + data: {...config, instanceID}, type: INIT, }; diff --git a/packages/browser/src/__legacy__/http-client/clients/axios-http-client.ts b/packages/browser/src/__legacy__/http-client/clients/axios-http-client.ts index d6f56f86e..29ce5cb11 100755 --- a/packages/browser/src/__legacy__/http-client/clients/axios-http-client.ts +++ b/packages/browser/src/__legacy__/http-client/clients/axios-http-client.ts @@ -41,9 +41,9 @@ import {HttpClientInstance, HttpClientInterface, HttpClientStatic} from '../mode */ @staticDecorator>() export class HttpClient implements HttpClientInterface { - private static axiosInstance: HttpClientInstance; - private static clientInstance: HttpClient; - private static isHandlerEnabled: boolean; + private static instances: Map = new Map(); + private static clientInstances: Map = new Map(); + private isHandlerEnabled: boolean = true; private attachToken: (request: HttpRequestConfig) => Promise = () => Promise.resolve(); private requestStartCallback: (request: HttpRequestConfig) => void = () => null; private requestSuccessCallback: (response: HttpResponse) => void = () => null; @@ -59,6 +59,9 @@ export class HttpClient implements HttpClientInterface await this.clientInstance.requestHandler(request as HttpRequestConfig) as InternalAxiosRequestConfig); + axiosInstance.interceptors.request.use(async (request: InternalAxiosRequestConfig) => await clientInstance.requestHandler(request as HttpRequestConfig) as InternalAxiosRequestConfig); // Register response interceptor - this.axiosInstance.interceptors.response.use( - response => this.clientInstance.successHandler(response), - error => this.clientInstance.errorHandler(error), + axiosInstance.interceptors.response.use( + response => clientInstance.successHandler(response), + error => clientInstance.errorHandler(error), ); // Add the missing helper methods from axios - this.axiosInstance.all = axios.all; - this.axiosInstance.spread = axios.spread; + axiosInstance.all = axios.all; + axiosInstance.spread = axios.spread; // Add the init method from the `HttpClient` instance. - this.axiosInstance.init = this.clientInstance.init; + axiosInstance.init = clientInstance.init; // Add the handler enabling & disabling methods to the instance. - this.axiosInstance.enableHandler = this.clientInstance.enableHandler; - this.axiosInstance.disableHandler = this.clientInstance.disableHandler; - this.axiosInstance.disableHandlerWithTimeout = this.clientInstance.disableHandlerWithTimeout; - this.axiosInstance.setHttpRequestStartCallback = this.clientInstance.setHttpRequestStartCallback; - this.axiosInstance.setHttpRequestSuccessCallback = this.clientInstance.setHttpRequestSuccessCallback; - this.axiosInstance.setHttpRequestErrorCallback = this.clientInstance.setHttpRequestErrorCallback; - this.axiosInstance.setHttpRequestFinishCallback = this.clientInstance.setHttpRequestFinishCallback; - return this.axiosInstance; + axiosInstance.enableHandler = clientInstance.enableHandler; + axiosInstance.disableHandler = clientInstance.disableHandler; + axiosInstance.disableHandlerWithTimeout = clientInstance.disableHandlerWithTimeout; + axiosInstance.setHttpRequestStartCallback = clientInstance.setHttpRequestStartCallback; + axiosInstance.setHttpRequestSuccessCallback = clientInstance.setHttpRequestSuccessCallback; + axiosInstance.setHttpRequestErrorCallback = clientInstance.setHttpRequestErrorCallback; + axiosInstance.setHttpRequestFinishCallback = clientInstance.setHttpRequestFinishCallback; + + this.instances.set(instanceId, axiosInstance); + return axiosInstance; + } + + /** + * Destroys and cleans up an HttpClient instance. + * This should be called during provider teardown to prevent memory leaks. + * + * @param instanceId - The instance ID to destroy. Defaults to 0. + */ + public static destroyInstance(instanceId: number = 0): void { + const axiosInstance = this.instances.get(instanceId); + if (axiosInstance) { + // Eject interceptors to prevent memory leaks + axiosInstance.interceptors.request.clear(); + axiosInstance.interceptors.response.clear(); + } + this.instances.delete(instanceId); + this.clientInstances.delete(instanceId); } /** @@ -134,7 +157,7 @@ export class HttpClient implements HttpClientInterface Promise, ): Promise { - HttpClient.isHandlerEnabled = isHandlerEnabled; + this.isHandlerEnabled = isHandlerEnabled; this.attachToken = attachToken; } @@ -203,14 +226,14 @@ export class HttpClient implements HttpClientInterface { - HttpClient.isHandlerEnabled = true; + this.isHandlerEnabled = true; }, timeout); } diff --git a/packages/browser/src/__legacy__/http-client/models/http-client.ts b/packages/browser/src/__legacy__/http-client/models/http-client.ts index d6e612fed..9c0783c6b 100644 --- a/packages/browser/src/__legacy__/http-client/models/http-client.ts +++ b/packages/browser/src/__legacy__/http-client/models/http-client.ts @@ -23,7 +23,7 @@ import { HttpError, HttpResponse } from "../../models"; * Http client interface with static functions. */ export interface HttpClientStatic { - getInstance(): S; + getInstance(instanceId?: number): S; } /** diff --git a/packages/browser/src/__legacy__/worker/worker-core.ts b/packages/browser/src/__legacy__/worker/worker-core.ts index d5714d90e..6fc518a46 100755 --- a/packages/browser/src/__legacy__/worker/worker-core.ts +++ b/packages/browser/src/__legacy__/worker/worker-core.ts @@ -42,6 +42,7 @@ import {MemoryStore} from '../stores'; import {SPACryptoUtils} from '../utils/crypto-utils'; export const WebWorkerCore = async ( + instanceID: number, config: AuthClientConfig, getAuthHelper: ( authClient: AsgardeoAuthClient, @@ -51,7 +52,7 @@ export const WebWorkerCore = async ( const _store: Storage = new MemoryStore(); const _cryptoUtils: SPACryptoUtils = new SPACryptoUtils(); const _authenticationClient = new AsgardeoAuthClient(); - await _authenticationClient.initialize(config, _store, _cryptoUtils); + await _authenticationClient.initialize(config, _store, _cryptoUtils, instanceID); const _spaHelper = new SPAHelper(_authenticationClient); @@ -62,7 +63,7 @@ export const WebWorkerCore = async ( const _dataLayer = _authenticationClient.getStorageManager(); - const _httpClient: HttpClientInstance = HttpClient.getInstance(); + const _httpClient: HttpClientInstance = HttpClient.getInstance(instanceID); const attachToken = async (request: HttpRequestConfig): Promise => { await _authenticationHelper.attachTokenToRequestConfig(request); diff --git a/packages/browser/src/__legacy__/worker/worker-receiver.ts b/packages/browser/src/__legacy__/worker/worker-receiver.ts index ee7c8deff..ac75e7e78 100644 --- a/packages/browser/src/__legacy__/worker/worker-receiver.ts +++ b/packages/browser/src/__legacy__/worker/worker-receiver.ts @@ -84,8 +84,9 @@ export const workerReceiver = ( switch (data.type) { case INIT: try { - const config: AuthClientConfig = {...data.data}; - webWorker = await WebWorkerCore(config, getAuthHelper); + const {instanceID = 0, ...configData} = data.data; + const config: AuthClientConfig = {...configData}; + webWorker = await WebWorkerCore(instanceID, config, getAuthHelper); webWorker.setHttpRequestFinishCallback(onRequestFinishCallback); webWorker.setHttpRequestStartCallback(onRequestStartCallback); webWorker.setHttpRequestSuccessCallback(onRequestSuccessCallback);