Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions packages/journey-client/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,6 @@
*/

export * from './lib/client.store.js';

// Re-export types from internal packages that consumers need
export { callbackType } from '@forgerock/sdk-types';
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch

1 change: 0 additions & 1 deletion packages/journey-client/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ export type {
Callback,
CallbackType,
StepType,
callbackType,
GenericError,
PolicyRequirement,
FailedPolicyRequirement,
Expand Down
22 changes: 12 additions & 10 deletions packages/oidc-client/src/lib/client.store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -289,16 +289,18 @@ export async function oidc<ActionType extends ActionTypes = ActionTypes>({
options: storageOptions,
});
}),
Micro.tap(async (tokens) => {
await store.dispatch(
oidcApi.endpoints.revoke.initiate({
accessToken: tokens.accessToken,
clientId: config.clientId,
endpoint: wellknown.revocation_endpoint,
}),
);
await storageClient.remove();
await storageClient.set(tokens);
Micro.tap(async (newTokens) => {
if (tokens && 'accessToken' in tokens) {
await store.dispatch(
oidcApi.endpoints.revoke.initiate({
accessToken: tokens.accessToken,
clientId: config.clientId,
endpoint: wellknown.revocation_endpoint,
}),
);
await storageClient.remove();
}
await storageClient.set(newTokens);
}),
Comment on lines +292 to 304
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

These changes appear unrelated to the PR objectives.

The PR title indicates this is about fixing callbackType export in journey-client, but this file contains token renewal flow changes in the oidc-client package. Consider whether these changes should be in a separate PR for better traceability and review focus.

🤖 Prompt for AI Agents
In @packages/oidc-client/src/lib/client.store.ts around lines 292 - 304, The
changes to the token renewal flow (Micro.tap callback updating tokens) are
unrelated to the callbackType export fix; revert or remove the added logic
around Micro.tap, the calls to
store.dispatch(oidcApi.endpoints.revoke.initiate(...)), storageClient.remove(),
and storageClient.set(newTokens) from this PR and instead place them in a
separate focused PR or commit that clearly documents the token renewal behavior;
ensure the current PR only contains the callbackType export fix and no
modifications to the Micro.tap/token revocation flow or storageClient usage.

⚠️ Potential issue | 🔴 Critical

Consider error handling and atomic storage updates.

The token renewal logic has several concerns:

  1. Critical: Revocation failure blocks new token storage — If the old token revocation fails (e.g., token already expired/invalid), the tap will throw and prevent storing newTokens, leaving the user in a broken state. Consider using Micro.tapError or try-catch to ensure new tokens are stored even if revocation fails.

  2. Missing revocation endpoint check — Unlike the token.revoke method (line 331), this code doesn't verify wellknown.revocation_endpoint exists before using it (line 298). This could cause runtime errors if the endpoint is unavailable.

  3. Atomic gap in storage updates — Calling remove() followed by set() creates a brief window where no tokens exist in storage. Concurrent reads during this gap will fail. Consider directly calling storageClient.set(newTokens) without the remove, since set should overwrite existing values.

🔎 Proposed fix with better error handling
         Micro.tap(async (newTokens) => {
           if (tokens && 'accessToken' in tokens) {
-            await store.dispatch(
-              oidcApi.endpoints.revoke.initiate({
-                accessToken: tokens.accessToken,
-                clientId: config.clientId,
-                endpoint: wellknown.revocation_endpoint,
-              }),
-            );
-            await storageClient.remove();
+            if (wellknown.revocation_endpoint) {
+              try {
+                await store.dispatch(
+                  oidcApi.endpoints.revoke.initiate({
+                    accessToken: tokens.accessToken,
+                    clientId: config.clientId,
+                    endpoint: wellknown.revocation_endpoint,
+                  }),
+                );
+              } catch (error) {
+                log.warn('Failed to revoke old access token during renewal', error);
+              }
+            }
           }
           await storageClient.set(newTokens);
         }),
🤖 Prompt for AI Agents
In @packages/oidc-client/src/lib/client.store.ts around lines 292 - 304, The
Micro.tap handler currently calls
store.dispatch(oidcApi.endpoints.revoke.initiate(...)) without checking
wellknown.revocation_endpoint and then calls storageClient.remove() before
storageClient.set(newTokens), which can throw and create a storage gap; update
the Micro.tap callback to first check that wellknown.revocation_endpoint is
present before attempting revocation, wrap the revoke call in a try-catch (or
use Micro.tapError) so any revocation failure is logged/ignored and does not
prevent saving newTokens, and avoid calling storageClient.remove(); instead call
storageClient.set(newTokens) directly to atomically overwrite existing tokens
(keeping store.dispatch and oidcApi.endpoints.revoke.initiate as the revocation
call site).

);

Expand Down
Loading