From 741cc72543e76038654f7a04e7a126174c5cb05c Mon Sep 17 00:00:00 2001 From: kavindadewmith Date: Thu, 22 Jan 2026 14:45:58 +0530 Subject: [PATCH 1/7] Add utility to check instance-specific state in URL and refactor instance ID handling --- packages/browser/src/index.ts | 1 + .../utils/hasCalledForThisInstanceInUrl.ts | 32 +++++++++++++++++++ packages/javascript/src/__legacy__/client.ts | 18 ++++++----- .../contexts/Asgardeo/AsgardeoProvider.tsx | 4 +-- packages/react/src/hooks/useBrowserUrl.ts | 16 ++++++++-- 5 files changed, 59 insertions(+), 12 deletions(-) create mode 100644 packages/browser/src/utils/hasCalledForThisInstanceInUrl.ts diff --git a/packages/browser/src/index.ts b/packages/browser/src/index.ts index 4f3b8e45a..efbcdedf3 100644 --- a/packages/browser/src/index.ts +++ b/packages/browser/src/index.ts @@ -49,6 +49,7 @@ export * from './__legacy__/worker/worker-receiver'; export {AsgardeoBrowserConfig} from './models/config'; export {default as hasAuthParamsInUrl} from './utils/hasAuthParamsInUrl'; +export {default as hasCalledForThisInstanceInUrl} from './utils/hasCalledForThisInstanceInUrl'; export {default as navigate} from './utils/navigate'; export {default as AsgardeoBrowserClient} from './AsgardeoBrowserClient'; diff --git a/packages/browser/src/utils/hasCalledForThisInstanceInUrl.ts b/packages/browser/src/utils/hasCalledForThisInstanceInUrl.ts new file mode 100644 index 000000000..3960e3286 --- /dev/null +++ b/packages/browser/src/utils/hasCalledForThisInstanceInUrl.ts @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/** + * Utility to check if `state` is available in the URL as a search param and matches the provided instance. + * + * @param params - The URL search params to check. Defaults to `window.location.search`. + * @param instanceId - The instance ID to match against the `state` param. + * @return `true` if the URL contains a matching `state` search param, otherwise `false`. + */ +const hasCalledForThisInstanceInUrl = (instanceId: number, params: string = window.location.search): boolean => { + const MATCHER: RegExp = new RegExp(`[?&]state=instance_${instanceId}-[^&]+`); + + return MATCHER.test(params); +}; + +export default hasCalledForThisInstanceInUrl; \ No newline at end of file diff --git a/packages/javascript/src/__legacy__/client.ts b/packages/javascript/src/__legacy__/client.ts index d18daf981..9e414948b 100644 --- a/packages/javascript/src/__legacy__/client.ts +++ b/packages/javascript/src/__legacy__/client.ts @@ -69,7 +69,7 @@ export class AsgardeoAuthClient { private cryptoHelper: IsomorphicCrypto; - private static instanceIdValue: number; + private instanceIdValue: number; // FIXME: Validate this. // Ref: https://github.com/asgardeo/asgardeo-auth-js-core/pull/205 @@ -121,20 +121,20 @@ export class AsgardeoAuthClient { ): Promise { const {clientId} = config; - if (!AsgardeoAuthClient.instanceIdValue) { - AsgardeoAuthClient.instanceIdValue = 0; + if (!this.instanceIdValue) { + this.instanceIdValue = 0; } else { - AsgardeoAuthClient.instanceIdValue += 1; + this.instanceIdValue += 1; } if (instanceID) { - AsgardeoAuthClient.instanceIdValue = instanceID; + this.instanceIdValue = instanceID; } if (!clientId) { - this.storageManager = new StorageManager(`instance_${AsgardeoAuthClient.instanceIdValue}`, store); + this.storageManager = new StorageManager(`instance_${this.instanceIdValue}`, store); } else { - this.storageManager = new StorageManager(`instance_${AsgardeoAuthClient.instanceIdValue}-${clientId}`, store); + this.storageManager = new StorageManager(`instance_${this.instanceIdValue}-${clientId}`, store); } this.cryptoUtils = inputCryptoUtils; @@ -187,7 +187,7 @@ export class AsgardeoAuthClient { */ // eslint-disable-next-line class-methods-use-this public getInstanceId(): number { - return AsgardeoAuthClient.instanceIdValue; + return this.instanceIdValue; } /** @@ -250,6 +250,8 @@ export class AsgardeoAuthClient { authRequestConfig['client_secret'] = configData.clientSecret; } + authRequestConfig['state'] = 'instance_' + this.getInstanceId() + '-' + configData.clientId; + const authorizeRequestParams: Map = getAuthorizeRequestUrlParams( { clientId: configData.clientId, diff --git a/packages/react/src/contexts/Asgardeo/AsgardeoProvider.tsx b/packages/react/src/contexts/Asgardeo/AsgardeoProvider.tsx index 8fda0b4ad..ec4c289db 100644 --- a/packages/react/src/contexts/Asgardeo/AsgardeoProvider.tsx +++ b/packages/react/src/contexts/Asgardeo/AsgardeoProvider.tsx @@ -70,7 +70,7 @@ const AsgardeoProvider: FC> = ({ }: PropsWithChildren): ReactElement => { const reRenderCheckRef: RefObject = useRef(false); const asgardeo: AsgardeoReactClient = useMemo(() => new AsgardeoReactClient(instanceId), [instanceId]); - const {hasAuthParams} = useBrowserUrl(); + const {hasAuthParams, hasCalledForThisInstance} = useBrowserUrl(); const [user, setUser] = useState(null); const [currentOrganization, setCurrentOrganization] = useState(null); @@ -266,7 +266,7 @@ const AsgardeoProvider: FC> = ({ } const currentUrl: URL = new URL(window.location.href); - const hasAuthParamsResult: boolean = hasAuthParams(currentUrl, afterSignInUrl); + const hasAuthParamsResult: boolean = hasAuthParams(currentUrl, afterSignInUrl) && hasCalledForThisInstance(currentUrl, instanceId ?? 1); const isV2Platform: boolean = config.platform === Platform.AsgardeoV2; diff --git a/packages/react/src/hooks/useBrowserUrl.ts b/packages/react/src/hooks/useBrowserUrl.ts index fd4bbd30c..8a3a8a8cc 100644 --- a/packages/react/src/hooks/useBrowserUrl.ts +++ b/packages/react/src/hooks/useBrowserUrl.ts @@ -16,7 +16,7 @@ * under the License. */ -import {hasAuthParamsInUrl} from '@asgardeo/browser'; +import {hasAuthParamsInUrl, hasCalledForThisInstanceInUrl} from '@asgardeo/browser'; /** * Interface for the useBrowserUrl hook return value. @@ -30,6 +30,15 @@ export interface UseBrowserUrl { * @returns True if the URL contains authentication parameters and matches the afterSignInUrl, or if it contains an error parameter */ hasAuthParams: (url: URL, afterSignInUrl: string) => boolean; + + /** + * Checks if the URL indicates that the authentication flow has been called for this instance. + * + * @param url - The URL object to check + * @param instanceId - The instance ID to check against + * @returns True if the URL indicates the flow has been called for this instance + */ + hasCalledForThisInstance: (url: URL, instanceId: number) => boolean; } /** @@ -53,7 +62,10 @@ const useBrowserUrl = (): UseBrowserUrl => { // authParams?.authorizationCode || // FIXME: These are sent externally. Need to see what we can do about this. url.searchParams.get('error') !== null; - return {hasAuthParams}; + const hasCalledForThisInstance = (url: URL, instanceId: number): boolean => + (hasCalledForThisInstanceInUrl(instanceId, url.search)); + + return {hasAuthParams, hasCalledForThisInstance}; }; export default useBrowserUrl; From 77be3ebf1b5e620427acda7fa92cc31731ebfafd Mon Sep 17 00:00:00 2001 From: kavindadewmith Date: Tue, 3 Feb 2026 11:37:18 +0530 Subject: [PATCH 2/7] Fix missing newline at end of file in hasCalledForThisInstanceInUrl utility --- packages/browser/src/utils/hasCalledForThisInstanceInUrl.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/browser/src/utils/hasCalledForThisInstanceInUrl.ts b/packages/browser/src/utils/hasCalledForThisInstanceInUrl.ts index 3960e3286..2e3e0913b 100644 --- a/packages/browser/src/utils/hasCalledForThisInstanceInUrl.ts +++ b/packages/browser/src/utils/hasCalledForThisInstanceInUrl.ts @@ -29,4 +29,4 @@ const hasCalledForThisInstanceInUrl = (instanceId: number, params: string = wind return MATCHER.test(params); }; -export default hasCalledForThisInstanceInUrl; \ No newline at end of file +export default hasCalledForThisInstanceInUrl; From 7faa052ecc9f4c4c7c15e76415fb010a0abca777 Mon Sep 17 00:00:00 2001 From: kavindadewmith Date: Thu, 5 Feb 2026 13:44:47 +0530 Subject: [PATCH 3/7] Fix instance ID default value in hasAuthParamsResult calculation --- packages/react/src/contexts/Asgardeo/AsgardeoProvider.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react/src/contexts/Asgardeo/AsgardeoProvider.tsx b/packages/react/src/contexts/Asgardeo/AsgardeoProvider.tsx index ec4c289db..4494e4b7e 100644 --- a/packages/react/src/contexts/Asgardeo/AsgardeoProvider.tsx +++ b/packages/react/src/contexts/Asgardeo/AsgardeoProvider.tsx @@ -266,7 +266,7 @@ const AsgardeoProvider: FC> = ({ } const currentUrl: URL = new URL(window.location.href); - const hasAuthParamsResult: boolean = hasAuthParams(currentUrl, afterSignInUrl) && hasCalledForThisInstance(currentUrl, instanceId ?? 1); + const hasAuthParamsResult: boolean = hasAuthParams(currentUrl, afterSignInUrl) && hasCalledForThisInstance(currentUrl, instanceId ?? 0); const isV2Platform: boolean = config.platform === Platform.AsgardeoV2; From 10ca5ed481d748b019c8e10fcd8b5650e42e36a9 Mon Sep 17 00:00:00 2001 From: kavindadewmith Date: Tue, 10 Feb 2026 10:49:28 +0530 Subject: [PATCH 4/7] Refactor instance ID handling in authorization flow and update state parameter format --- packages/browser/src/utils/hasCalledForThisInstanceInUrl.ts | 2 +- packages/javascript/src/__legacy__/client.ts | 3 +-- .../javascript/src/utils/getAuthorizeRequestUrlParams.ts | 6 ++++++ 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/browser/src/utils/hasCalledForThisInstanceInUrl.ts b/packages/browser/src/utils/hasCalledForThisInstanceInUrl.ts index 2e3e0913b..3897221a6 100644 --- a/packages/browser/src/utils/hasCalledForThisInstanceInUrl.ts +++ b/packages/browser/src/utils/hasCalledForThisInstanceInUrl.ts @@ -24,7 +24,7 @@ * @return `true` if the URL contains a matching `state` search param, otherwise `false`. */ const hasCalledForThisInstanceInUrl = (instanceId: number, params: string = window.location.search): boolean => { - const MATCHER: RegExp = new RegExp(`[?&]state=instance_${instanceId}-[^&]+`); + const MATCHER: RegExp = new RegExp(`[?&]state=instance_${instanceId}_[^&]+`); return MATCHER.test(params); }; diff --git a/packages/javascript/src/__legacy__/client.ts b/packages/javascript/src/__legacy__/client.ts index 9e414948b..d0cdbe2eb 100644 --- a/packages/javascript/src/__legacy__/client.ts +++ b/packages/javascript/src/__legacy__/client.ts @@ -250,8 +250,6 @@ export class AsgardeoAuthClient { authRequestConfig['client_secret'] = configData.clientSecret; } - authRequestConfig['state'] = 'instance_' + this.getInstanceId() + '-' + configData.clientId; - const authorizeRequestParams: Map = getAuthorizeRequestUrlParams( { clientId: configData.clientId, @@ -261,6 +259,7 @@ export class AsgardeoAuthClient { redirectUri: configData.afterSignInUrl, responseMode: configData.responseMode, scopes: processOpenIDScopes(configData.scopes), + instanceId: this.getInstanceId().toString(), }, {key: pkceKey}, authRequestConfig, diff --git a/packages/javascript/src/utils/getAuthorizeRequestUrlParams.ts b/packages/javascript/src/utils/getAuthorizeRequestUrlParams.ts index daae24901..455cdb67b 100644 --- a/packages/javascript/src/utils/getAuthorizeRequestUrlParams.ts +++ b/packages/javascript/src/utils/getAuthorizeRequestUrlParams.ts @@ -59,6 +59,7 @@ const getAuthorizeRequestUrlParams = ( redirectUri: string; responseMode?: string; scopes?: string; + instanceId?: string; } & ExtendedAuthorizeRequestUrlParams, pkceOptions: {key: string}, customParams: Record, @@ -105,6 +106,11 @@ const getAuthorizeRequestUrlParams = ( }); } + if (options.instanceId) { + const AUTH_INSTANCE_PREFIX = "instance_"; + customParams[OIDCRequestConstants.Params.STATE] = AUTH_INSTANCE_PREFIX + options.instanceId; + } + authorizeRequestParams.set( OIDCRequestConstants.Params.STATE, generateStateParamForRequestCorrelation( From 2b1f8decba8825b78f3ca7691e33d6ef11d355e4 Mon Sep 17 00:00:00 2001 From: kavindadewmith Date: Mon, 16 Feb 2026 14:09:21 +0530 Subject: [PATCH 5/7] Refactor instance ID handling in authorization flow and update related utility functions --- packages/javascript/src/__legacy__/client.ts | 2 +- .../src/utils/getAuthorizeRequestUrlParams.ts | 15 ++++++--------- .../src/contexts/Asgardeo/AsgardeoProvider.tsx | 3 ++- packages/react/src/hooks/useBrowserUrl.ts | 4 ++-- 4 files changed, 11 insertions(+), 13 deletions(-) diff --git a/packages/javascript/src/__legacy__/client.ts b/packages/javascript/src/__legacy__/client.ts index d0cdbe2eb..ee254db8e 100644 --- a/packages/javascript/src/__legacy__/client.ts +++ b/packages/javascript/src/__legacy__/client.ts @@ -255,11 +255,11 @@ export class AsgardeoAuthClient { clientId: configData.clientId, codeChallenge, codeChallengeMethod: PKCEConstants.DEFAULT_CODE_CHALLENGE_METHOD, + instanceId: this.getInstanceId().toString(), prompt: configData.prompt, redirectUri: configData.afterSignInUrl, responseMode: configData.responseMode, scopes: processOpenIDScopes(configData.scopes), - instanceId: this.getInstanceId().toString(), }, {key: pkceKey}, authRequestConfig, diff --git a/packages/javascript/src/utils/getAuthorizeRequestUrlParams.ts b/packages/javascript/src/utils/getAuthorizeRequestUrlParams.ts index 455cdb67b..960a88154 100644 --- a/packages/javascript/src/utils/getAuthorizeRequestUrlParams.ts +++ b/packages/javascript/src/utils/getAuthorizeRequestUrlParams.ts @@ -55,11 +55,11 @@ const getAuthorizeRequestUrlParams = ( clientId: string; codeChallenge?: string; codeChallengeMethod?: string; + instanceId?: string; prompt?: string; redirectUri: string; responseMode?: string; scopes?: string; - instanceId?: string; } & ExtendedAuthorizeRequestUrlParams, pkceOptions: {key: string}, customParams: Record, @@ -106,17 +106,14 @@ const getAuthorizeRequestUrlParams = ( }); } - if (options.instanceId) { - const AUTH_INSTANCE_PREFIX = "instance_"; - customParams[OIDCRequestConstants.Params.STATE] = AUTH_INSTANCE_PREFIX + options.instanceId; - } + const AUTH_INSTANCE_PREFIX: string = 'instance_'; + const customStateValue: string = options.instanceId + ? AUTH_INSTANCE_PREFIX + options.instanceId + : customParams ? customParams[OIDCRequestConstants.Params.STATE]?.toString() : ''; authorizeRequestParams.set( OIDCRequestConstants.Params.STATE, - generateStateParamForRequestCorrelation( - pkceKey, - customParams ? customParams[OIDCRequestConstants.Params.STATE]?.toString() : '', - ), + generateStateParamForRequestCorrelation(pkceKey, customStateValue), ); return authorizeRequestParams; diff --git a/packages/react/src/contexts/Asgardeo/AsgardeoProvider.tsx b/packages/react/src/contexts/Asgardeo/AsgardeoProvider.tsx index 4494e4b7e..4131ea646 100644 --- a/packages/react/src/contexts/Asgardeo/AsgardeoProvider.tsx +++ b/packages/react/src/contexts/Asgardeo/AsgardeoProvider.tsx @@ -266,7 +266,8 @@ const AsgardeoProvider: FC> = ({ } const currentUrl: URL = new URL(window.location.href); - const hasAuthParamsResult: boolean = hasAuthParams(currentUrl, afterSignInUrl) && hasCalledForThisInstance(currentUrl, instanceId ?? 0); + const hasAuthParamsResult: boolean = + hasAuthParams(currentUrl, afterSignInUrl) && hasCalledForThisInstance(currentUrl, instanceId ?? 0); const isV2Platform: boolean = config.platform === Platform.AsgardeoV2; diff --git a/packages/react/src/hooks/useBrowserUrl.ts b/packages/react/src/hooks/useBrowserUrl.ts index 8a3a8a8cc..9b0708a05 100644 --- a/packages/react/src/hooks/useBrowserUrl.ts +++ b/packages/react/src/hooks/useBrowserUrl.ts @@ -62,8 +62,8 @@ const useBrowserUrl = (): UseBrowserUrl => { // authParams?.authorizationCode || // FIXME: These are sent externally. Need to see what we can do about this. url.searchParams.get('error') !== null; - const hasCalledForThisInstance = (url: URL, instanceId: number): boolean => - (hasCalledForThisInstanceInUrl(instanceId, url.search)); + const hasCalledForThisInstance = (url: URL, instanceId: number): boolean => + hasCalledForThisInstanceInUrl(instanceId, url.search); return {hasAuthParams, hasCalledForThisInstance}; }; From a134915f3d7d6f3cac662d5bcd240a27c0a8e208 Mon Sep 17 00:00:00 2001 From: kavindadewmith Date: Mon, 16 Feb 2026 14:34:17 +0530 Subject: [PATCH 6/7] Refactor custom state value assignment logic in getAuthorizeRequestUrlParams utility --- .../src/utils/getAuthorizeRequestUrlParams.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/javascript/src/utils/getAuthorizeRequestUrlParams.ts b/packages/javascript/src/utils/getAuthorizeRequestUrlParams.ts index 960a88154..cf846655a 100644 --- a/packages/javascript/src/utils/getAuthorizeRequestUrlParams.ts +++ b/packages/javascript/src/utils/getAuthorizeRequestUrlParams.ts @@ -107,9 +107,13 @@ const getAuthorizeRequestUrlParams = ( } const AUTH_INSTANCE_PREFIX: string = 'instance_'; - const customStateValue: string = options.instanceId - ? AUTH_INSTANCE_PREFIX + options.instanceId - : customParams ? customParams[OIDCRequestConstants.Params.STATE]?.toString() : ''; + let customStateValue: string = ''; + + if (options.instanceId) { + customStateValue = AUTH_INSTANCE_PREFIX + options.instanceId; + } else if (customParams) { + customStateValue = customParams[OIDCRequestConstants.Params.STATE]?.toString() ?? ''; + } authorizeRequestParams.set( OIDCRequestConstants.Params.STATE, From 87058278fd7f53552418af9223da7d72b22c5b3f Mon Sep 17 00:00:00 2001 From: kavindadewmith Date: Mon, 16 Feb 2026 15:07:52 +0530 Subject: [PATCH 7/7] =?UTF-8?q?Add=20changeset=20=F0=9F=A6=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .changeset/brown-paths-report.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .changeset/brown-paths-report.md diff --git a/.changeset/brown-paths-report.md b/.changeset/brown-paths-report.md new file mode 100644 index 000000000..f178f6806 --- /dev/null +++ b/.changeset/brown-paths-report.md @@ -0,0 +1,7 @@ +--- +'@asgardeo/javascript': patch +'@asgardeo/browser': patch +'@asgardeo/react': patch +--- + +Fix login flow isolation when using multiple AuthProvider instances