Skip to content

Fix stale tokens returned during concurrent refresh-token renewal#2186

Open
ThorstenKunz wants to merge 1 commit intodamienbod:mainfrom
ThorstenKunz:oidc-refresh-token-publication-race
Open

Fix stale tokens returned during concurrent refresh-token renewal#2186
ThorstenKunz wants to merge 1 commit intodamienbod:mainfrom
ThorstenKunz:oidc-refresh-token-publication-race

Conversation

@ThorstenKunz
Copy link
Copy Markdown

Summary

This PR fixes a race condition in the refresh-token flow where concurrent forceRefreshSession() calls could return stale tokens or observe inconsistent auth state.

Problem

In refresh-token mode, forceRefreshSession() built its LoginResponse from the current auth-state getters instead of preferring the fresh result from the completed refresh operation.

This became problematic when multiple refresh calls happened close together:

  • one call could trigger the actual refresh,
  • another call could see that renew was already running,
  • and callers could still receive stale tokens from storage instead of the newly refreshed values.

Reproduction

The issue can be reproduced with a client configured with useRefreshToken: true by triggering multiple manual refresh calls in parallel:

await Promise.all([
  firstValueFrom(oidcSecurityService.forceRefreshSession(undefined, configId)),
  firstValueFrom(oidcSecurityService.forceRefreshSession(undefined, configId)),
]);

Before the fix, returned tokens and immediately readable stored tokens could diverge depending on timing.

Fix

The refresh flow was changed in three key places:

  1. The refresh-token path now prefers the authResult of the completed refresh operation when creating the LoginResponse.
  2. If a renew process is already running, additional callers now wait for the published NewAuthenticationResult event before completing.
  3. The renew-running flag is set earlier, before the asynchronous well-known endpoint lookup, to close a timing window for concurrent refresh requests.

Test Coverage

This PR adds regression coverage for both unit and integration scenarios:

  • unit tests for stale token reads and concurrent refresh handling,
  • an integration test covering concurrent forceRefreshSession() calls with useRefreshToken: true,
  • test IDP updates to support refresh tokens and refresh-token rotation.

Result

After this change, concurrent manual refresh calls resolve consistently and return the freshly renewed token set instead of stale auth-state values.

Enhances the session refresh handling process by waiting for an ongoing renew process to complete before attempting a refresh, ensuring token consistency. Introduces new integration tests to verify the behavior and updates the IDP server to support refresh tokens. This change addresses potential race conditions where concurrent session refresh calls may lead to stale or inconsistent tokens.
@ThorstenKunz ThorstenKunz marked this pull request as ready for review March 12, 2026 13:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant