diff --git a/CHANGELOG.md b/CHANGELOG.md index dcb9b56f7..66afc0b95 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - [#1495](https://github.com/okta/okta-auth-js/pull/1495) add: DPoP support - [#1507](https://github.com/okta/okta-auth-js/pull/1507) add: new method `getOrRenewAccessToken` - [#1505](https://github.com/okta/okta-auth-js/pull/1505) add: support of `revokeSessions` param for `OktaPassword` authenticator (can be used in `reset-authenticator` remediation) +- [#1508](https://github.com/okta/okta-auth-js/pull/1508) IDX: add condition to compare stateHandles when loading saved idxResponse only when useGenericRemediator option is false or undefined - [#1512](https://github.com/okta/okta-auth-js/pull/1512) add: new service `RenewOnTabActivation` ### Bug Fix diff --git a/lib/idx/IdxTransactionManager.ts b/lib/idx/IdxTransactionManager.ts index e092da747..ec911a4f4 100644 --- a/lib/idx/IdxTransactionManager.ts +++ b/lib/idx/IdxTransactionManager.ts @@ -52,7 +52,11 @@ export function createIdxTransactionManager } if (options) { - const { interactionHandle } = options; + const { stateHandle, interactionHandle } = options; + // only perform this check if NOT using generic remediator + if (!options.useGenericRemediator && stateHandle && storedValue.stateHandle !== stateHandle) { + return null; + } if (interactionHandle && storedValue.interactionHandle !== interactionHandle) { return null; } diff --git a/lib/idx/run.ts b/lib/idx/run.ts index 4c4082bbc..f4eae2aaa 100644 --- a/lib/idx/run.ts +++ b/lib/idx/run.ts @@ -127,13 +127,14 @@ async function getDataFromIntrospect(authClient: OktaAuthIdxInterface, data: Run maxAge, acrValues, nonce, + useGenericRemediator, } = options; let idxResponse; let meta = getSavedTransactionMeta(authClient, { state, recoveryToken, activationToken }); // may be undefined if (stateHandle) { - idxResponse = await introspect(authClient, { withCredentials, version, stateHandle }); + idxResponse = await introspect(authClient, { withCredentials, version, stateHandle, useGenericRemediator }); } else { let interactionHandle = meta?.interactionHandle; // may be undefined if (!interactionHandle) { @@ -154,7 +155,7 @@ async function getDataFromIntrospect(authClient: OktaAuthIdxInterface, data: Run } // Introspect to get idx response - idxResponse = await introspect(authClient, { withCredentials, version, interactionHandle }); + idxResponse = await introspect(authClient, { withCredentials, version, interactionHandle, useGenericRemediator }); } return { ...data, idxResponse, meta }; } diff --git a/lib/idx/types/options.ts b/lib/idx/types/options.ts index 1863d24c6..4d91f6e9f 100644 --- a/lib/idx/types/options.ts +++ b/lib/idx/types/options.ts @@ -59,6 +59,7 @@ export interface IntrospectOptions extends IdxOptions { interactionHandle?: string; stateHandle?: string; version?: string; + useGenericRemediator?: boolean; } export interface RemediateOptions extends IdxOptions { diff --git a/test/spec/idx/IdxTransactionManager.ts b/test/spec/idx/IdxTransactionManager.ts index d1ca9ad5c..628f74f6e 100644 --- a/test/spec/idx/IdxTransactionManager.ts +++ b/test/spec/idx/IdxTransactionManager.ts @@ -165,6 +165,20 @@ describe('IdxTransactionManager', () => { expect(res).toBeNull(); }); describe('with options.stateHandle', () => { + it('returns null if options.stateHandle does not match saved stateHandle and useGenericRemediator = undefined', () => { + const { transactionManager, idxResponseStorage, savedResponse } = testContext; + idxResponseStorage.getStorage.mockReturnValue(savedResponse); + const res = transactionManager.loadIdxResponse({ stateHandle: 'a' }); + expect(idxResponseStorage.getStorage).toHaveBeenCalled(); + expect(res).toBeNull(); + }); + it('returns savedResponse if options.stateHandle does not match saved stateHandle and useGenericRemediator = true', () => { + const { transactionManager, idxResponseStorage, savedResponse } = testContext; + idxResponseStorage.getStorage.mockReturnValue(savedResponse); + const res = transactionManager.loadIdxResponse({ stateHandle: 'a', useGenericRemediator: true }); + expect(idxResponseStorage.getStorage).toHaveBeenCalled(); + expect(res).toBe(savedResponse); + }); it('returns data if options.stateHandle matches saved stateHandle', () => { const { transactionManager, idxResponseStorage, savedResponse } = testContext; savedResponse.stateHandle = 'a';