Auth: Time out the session when the timeout modals "Stay logged in" refresh fails (closes #22986)#23079
Auth: Time out the session when the timeout modals "Stay logged in" refresh fails (closes #22986)#23079iOvergaard wants to merge 3 commits into
Conversation
…andle failed refresh (#22986) - Time the user out when the "Stay logged in" refresh fails (e.g. the refresh token expired while the modal was open), instead of silently swallowing the failure and logging the user out on their next action - Recompute the remaining session time from the wall clock when the warning timer fires — after system sleep or background-tab throttling the timer fires late and the session may already be expired - Drive the modal countdown from an absolute deadline instead of counting interval ticks, so it stays truthful across sleep/throttling - Subtract a 5s safety margin from the client-computed expiry so a click near the end of the countdown still reaches the server in time Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The import/order rule crashes ESLint 10 (eslint-plugin-import fixer incompatibility) whenever it needs to report a violation, which made the whole auth package unlintable. Reordering the imports to the configured group order (parent, sibling, external) avoids the crash. Also removes an invalid @required JSDoc tag flagged by the linter. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Claude finished @iOvergaard's task in 5m 43s —— View job PR ReviewTarget: Fixes three compounding defects in the session-timeout flow: a failed
Suggestions
Approved with Suggestions for improvementGood to go, but please carefully consider the importance of the suggestions. Labels applied: |
There was a problem hiding this comment.
Pull request overview
Improves the backoffice auth session-timeout flow so the “Stay logged in” action no longer silently succeeds when the refresh token has already expired, and makes the timeout countdown resilient to system sleep/background-tab timer throttling.
Changes:
- Ensure failed token refresh triggers
timeOut()(so re-auth starts immediately instead of leaving a stale in-memory session). - Recompute remaining session time from the wall clock when the warning timer fires (with a small safety margin) to avoid showing a timeout modal for an already-expired session.
- Drive the timeout modal countdown from an absolute deadline (and add unit tests covering sleep/throttling scenarios); additionally fixes import ordering in auth modal files.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| src/Umbraco.Web.UI.Client/src/packages/core/auth/modals/umb-auth-timeout-modal.element.ts | Countdown now uses an absolute deadline; adds interval-guard on reconnect; expires via onExpired callback. |
| src/Umbraco.Web.UI.Client/src/packages/core/auth/modals/umb-auth-timeout-modal.element.test.ts | New tests validating countdown tick behavior and wall-clock expiry after simulated sleep. |
| src/Umbraco.Web.UI.Client/src/packages/core/auth/modals/umb-app-auth-modal.token.ts | Import order adjustment; removes misleading @required JSDoc on an optional value. |
| src/Umbraco.Web.UI.Client/src/packages/core/auth/modals/umb-app-auth-modal.element.ts | Import order adjustment. |
| src/Umbraco.Web.UI.Client/src/packages/core/auth/modals/manifests.ts | Import order adjustment. |
| src/Umbraco.Web.UI.Client/src/packages/core/auth/controllers/auth-session-timeout.controller.ts | Recomputes remaining time at timer-fire; introduces expiry safety margin; #tryValidateToken() times out on refresh failure; getContext() moved into guarded try. |
| src/Umbraco.Web.UI.Client/src/packages/core/auth/controllers/auth-session-timeout.controller.test.ts | New tests for warning-zone modal opening, refresh success/failure behavior, and late timer firing after simulated sleep. |
When the refresh token is rejected with invalid_grant, every queued or subsequent API request would previously fire its own doomed /token call (observed 18+ in a row) before the user was finally logged out. - UmbAuthClient now distinguishes definitive OAuth rejections (400/401 with an error body) from transient failures (network errors, 5xx) - UmbAuthContext latches the session as dead on a definitive rejection, times the user out once, and short-circuits further refresh attempts until a new session is established (login, peer tab, re-auth) - Transient failures do not latch, so a network blip can still recover Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Summary
Clicking "Stay logged in" in the session timeout modal did not prevent the user from being logged out when the underlying token refresh failed (e.g. the refresh token had already expired). The failure was silently swallowed: the modal closed, the user appeared to stay logged in, and the next interaction logged them out (#22986).
What changed
#tryValidateToken()callstimeOut()whenvalidateToken()returns false, so the re-authentication flow starts right away instead of leaving a session that will be rejected on the next API call.Date.now()when it fires, and the modal countdown is driven by an absolute deadline — so neither lies after system sleep or background-tab timer throttling. A 5s safety margin ensures a click near the end of the countdown still reaches the server in time./tokenis not retried after a definitive failure.UmbAuthClientdistinguishes definitive OAuth rejections (invalid_grant-class) from transient failures (network errors, 5xx). A definitive rejection latches the session as dead and short-circuits all further refresh attempts until a new session is established; previously every pending API request fired its own doomed/tokencall. Transient failures don't latch, so they remain retryable. All clients configured viaconfigureClient()— including third-party ones — inherit this.import/orderfixer crashes ESLint 10 when reporting a violation; the underlying eslint-plugin-import incompatibility deserves a separate dependency bump).Testing
Date.now), the wall-clock countdown, and the refresh latch (fatal vs transient, cleared by a new session)./tokencall, immediate "session timed out" sign-in screen — for both the modal click and a burst of API requests on a dead session. Happy path re-verified: a prompt click extends the session.tscclean; no new lint warnings.No public API changes.
Fixes #22986
🤖 Generated with Claude Code