Backoffice auth flow: architectural improvements for login, timeout, and logout #21881
iOvergaard
started this conversation in
Features and ideas
Replies: 1 comment 1 reply
-
|
Hello @iOvergaard , |
Beta Was this translation helpful? Give feedback.
1 reply
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Context
The backoffice authentication flow currently serves multiple distinct UX scenarios through a single code path. While functional, the architecture has accumulated friction points as requirements have grown (multi-provider support, timeout recovery, multi-tab coordination, OpenIddict constraints). This issue documents the tensions and proposes a direction for future improvements.
Related: #21846 (current PR addressing some symptoms), umbraco/Umbraco.UI#1292 (UUI
closableproperty)The scenarios
The auth system must handle these distinct flows, each with different UX requirements:
/umbraco/logoutrouteToday, these are funneled through
UmbAppAuthController.makeAuthorizationRequest()→#showLoginModal()→UMB_MODAL_APP_AUTH→umb-auth-view, with branching based onuserLoginState. The branching works, but it means one component and one modal token carry the complexity of all five flows.Architectural tensions
1. Modal vs page duality
umb-auth-viewrenders the same UI whether it's inside a modal (timeout) or as a standalone page (logout route). But the two contexts have different requirements:onSuccesscallback to submit the modalThe view doesn't know which context it's in — it just receives
userLoginStateand anonSuccesscallback. This works but makes it hard to optimize either path independently.2. Redirect vs popup is coupled to login state
The transport mechanism (redirect vs popup) is determined by
userLoginState:This coupling means the view is making a transport decision based on UI state. If we ever need a non-redirect flow for a non-timeout case (or vice versa), this breaks down.
3. Token expiry calculation inconsistency
Token lifetime is calculated differently in two places:
token-check.worker.ts): usesissuedAt + expiresIn * TOKEN_EXPIRY_MULTIPLIER (4)TokenResponse.isValid(): usesissuedAt + expiresIn(no multiplier)This means the main thread considers a token expired before the worker does, or vice versa, leading to edge cases where refresh attempts and timeout detection disagree.
Tip
This is being addressed in PR #21830
4. Multi-tab token refresh is uncoordinated
When multiple tabs are open and a token expires:
makeRefreshTokenRequest()concurrentlyWeb Locks (
navigator.locks.request) would give single-writer semantics for token refresh, but aren't currently used for this.Tip
This is being addressed in PR #21830
5. Non-dismissable modal is a workaround
The auth modal must not be closable via ESC, but UUI's
UUIModalDialogElementalways bindscancel → close. We currently work around this with a custom subclass that overrides_openModal()internals (#21846, tracked upstream in umbraco/Umbraco.UI#1292). This is fragile — it duplicates parent behavior and will break if UUI refactors.Suggested direction
These don't all need to happen at once, but they point toward a cleaner architecture:
Separate the flows: Instead of one modal token with branching, consider distinct entry points:
Decouple transport from state: Let the controller/context decide redirect vs popup based on the flow type, not the view. The view should just say "authenticate with this provider" and not care how.
Unify token expiry: Single source of truth for token lifetime calculation, shared between worker and main thread.
Coordinate multi-tab refresh: Use Web Locks for token refresh to ensure only one tab refreshes at a time, with others waiting for the result via storage events.
UUI
closableproperty: Once umbraco/Umbraco.UI#1292 lands, replace the custom subclass with a simple config flag.Non-goals
This issue is not proposing:
The goal is incremental improvement — untangling concerns so each piece is simpler to reason about and maintain.
Beta Was this translation helpful? Give feedback.
All reactions