Skip to content

fix: keep the auto-login redirect route per browser tab - #2272

Open
RhisiartK wants to merge 3 commits into
damienbod:mainfrom
RhisiartK:fix/multi-tab-redirect-route-race
Open

fix: keep the auto-login redirect route per browser tab#2272
RhisiartK wants to merge 3 commits into
damienbod:mainfrom
RhisiartK:fix/multi-tab-redirect-route-race

Conversation

@RhisiartK

@RhisiartK RhisiartK commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Summary

The auto-login guards save the pending redirect route in the configured storage, which is shared across tabs (e.g. a localStorage AbstractSecurityStorage) and keyed by the deterministic configId — so concurrent logins in multiple tabs overwrite each other's saved route. The route is now stored per browser tab (sessionStorage).

The bug

With a shared storage and two tabs logging in concurrently (same configId, one redirect slot):

  • Tab A's guard saves /orders/123; tab B's guard overwrites it with /orders/456 before the login completes.
  • Whichever tab returns from the STS first navigates to the other tab's deep link and consumes the slot; the second tab restores nothing.
  • An already-authenticated tab that merely hits a guarded route also consumes the pending slot and navigates away, since the guards call checkSavedRedirectRouteAndNavigate when authenticated.

The redirect round trip always returns to the tab that saved the route, so the slot is inherently tab-local state.

The fix

AutoLoginService writes the route to sessionStorage (per tab, key <configId>-redirect), accessed via inject(DOCUMENT).defaultView like the other window globals in this library:

  • Reads fall back once to the configured storage, so a login started before this change still restores its route; deletion clears both locations.
  • sessionStorage unavailable (SSR) → the previous behavior is kept unchanged.
  • The guards are untouched — they already go through AutoLoginService.

Trade-off: a route saved in one tab is no longer restored by a login completing in a different tab — that cross-tab restore was the same mechanism as the overwriting.

Test plan

  • New tests: route kept out of the shared storage, restore + cleanup in the saving tab, legacy slot consumed and cleared, sessionStorage-unavailable fallback
  • Full lib suite passes (1053 tests)
  • lint / prettier / block-words hooks pass

@RhisiartK RhisiartK changed the title fix: keep the auto-login redirect route per browser tab so concurrent logins do not overwrite it fix: keep the auto-login redirect route per browser tab Aug 7, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes a multi-tab race in the auto-login guards by making the “pending redirect route” tab-local (using sessionStorage) instead of being stored in the configured/shared storage (e.g., localStorage), preventing concurrent logins in different tabs from overwriting each other’s redirect.

Changes:

  • Store and retrieve the auto-login redirect route from sessionStorage (keyed by <configId>-redirect), with fallback to configured storage for backward compatibility.
  • Add/extend unit tests covering tab-local behavior, legacy fallback/cleanup, and sessionStorage-unavailable fallback.
  • Update docs to clarify that the preserved route is stored per browser tab; minor test hardening in BrowserStorageService specs.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
projects/angular-auth-oidc-client/src/lib/storage/browser-storage.service.spec.ts Restores mutated global Storage in the hasStorage test (test hygiene).
projects/angular-auth-oidc-client/src/lib/auto-login/auto-login.service.ts Implements tab-local redirect route storage via sessionStorage, with legacy fallback and cleanup.
projects/angular-auth-oidc-client/src/lib/auto-login/auto-login.service.spec.ts Adds test coverage for per-tab redirect behavior, legacy fallback consumption, and missing sessionStorage fallback.
docs/site/angular-auth-oidc-client/docs/documentation/auto-login.md Documents that preserved routes are stored per browser tab using sessionStorage.
Suppressed comments (2)

projects/angular-auth-oidc-client/src/lib/auto-login/auto-login.service.ts:52

  • getStoredRedirectRoute also builds a sessionStorage key from config.configId even though it is optional, so a config without configId would read from undefined-redirect instead of matching the previous behavior (read from configured storage / return null).
    const tabLocalRoute = this.document.defaultView?.sessionStorage?.getItem(
      `${config.configId}-${STORAGE_KEY}`
    );

projects/angular-auth-oidc-client/src/lib/auto-login/auto-login.service.ts:64

  • deleteStoredRedirectRoute removes from sessionStorage using config.configId even when it is missing, which would attempt to remove undefined-redirect (and could delete unrelated data if anything else uses that key). Guarding on a truthy configId keeps semantics aligned with the configured storage path.
    this.document.defaultView?.sessionStorage?.removeItem(
      `${config.configId}-${STORAGE_KEY}`
    );

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +37 to +43
const tabLocalStorage = this.document.defaultView?.sessionStorage;

if (tabLocalStorage) {
tabLocalStorage.setItem(`${config.configId}-${STORAGE_KEY}`, url);
} else {
this.storageService.write(STORAGE_KEY, url, config);
}
Comment on lines +238 to +242
const originalStorage = Storage;

(Storage as any) = undefined;
expect((service as any).hasStorage()).toBeFalse();
Storage = Storage;
(Storage as any) = originalStorage;
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.

2 participants