From ec0195920e179ad8118209432d09968f3330407f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20Can=20Karag=C3=B6z?= Date: Tue, 13 Oct 2026 14:06:04 +0300 Subject: [PATCH 1/7] docs: improve and expand documentation content --- docs/content/auth-flows/login-flow.md | 348 ++++++++++++++++++------- docs/content/auth-flows/logout-flow.md | 312 ++++++++++++++-------- 2 files changed, 447 insertions(+), 213 deletions(-) diff --git a/docs/content/auth-flows/login-flow.md b/docs/content/auth-flows/login-flow.md index 0df83bb1..ea1b9e10 100644 --- a/docs/content/auth-flows/login-flow.md +++ b/docs/content/auth-flows/login-flow.md @@ -4,160 +4,306 @@ order: 2 group: auth-flows --- +# Login Flow -# πŸ”‘ Login Flow -The login flow in UltimateAuth is not just credential validation. +Logging in is often described as a simple exchange: verify a password, issue a token, and consider the user authenticated. That description leaves out the part that matters most over time: **what exactly has been established and how can it be managed or revoked?** -πŸ‘‰ It is a **controlled session establishment process**. +UltimateAuth treats login as a **controlled session-establishment flow**. Credential verification is essential, but it is only one stage. A successful login connects an authenticated user to a tenant, a device context, a session hierarchy, and the security state that governs future requests. -
+The central idea is straightforward: -## 🧠 What is Login? -In traditional systems: +> **Login establishes a session. Tokens, when enabled, are credentials issued in connection with that sessionβ€”not the session itself.** -- Validate credentials -- Issue a token +This distinction shapes how UltimateAuth handles device awareness, session continuity, token issuance, revocation, and authentication policy. -In UltimateAuth: +## From a login response to an authentication lifecycle -πŸ‘‰ Login creates a **session hierarchy** +In a token-centric design, login is frequently treated as the moment a token is minted. The token then becomes the primary artifact developers reason about. This works for simple scenarios, but it can make questions about devices, concurrent logins, global sign-out, and session continuity harder to express consistently. + +UltimateAuth starts with the authentication lifecycle instead. It separates four responsibilities: + +1. **Identity resolution:** determine which user is attempting to authenticate. +2. **Authentication and security evaluation:** verify the credential and decide whether the attempt is allowed. +3. **Session establishment:** create a managed authentication instance in the appropriate device and user context. +4. **Credential delivery:** optionally issue access and refresh tokens and deliver the applicable credentials according to the server mode and client profile. + +These responsibilities are related, but they are not interchangeable. In particular, a valid password does not by itself create a session, and a token is not the sole source of authentication state. + +## The session hierarchy + +UltimateAuth models authentication through a hierarchy: + +```text +User +└── Root + β”œβ”€β”€ Chain (Device A) + β”‚ β”œβ”€β”€ Session 1 + β”‚ └── Session 2 + └── Chain (Device B) + └── Session 3 ``` -Root β†’ Chain β†’ Session + +Each level answers a different question: + +| Level | Responsibility | Why it matters | +| --- | --- | --- | +| **User** | The identity being authenticated | Login identifiers can change without changing the user's internal identity. | +| **Root** | The user-level session hierarchy | Provides a shared context for authentication across devices. | +| **Chain** | A device-associated sequence of sessions | Allows the system to reason about continuity and device-scoped authentication. | +| **Session** | A particular issued authentication instance | Provides the concrete session identity to which authentication credentials can be related. | + +The hierarchy is a **logical model**, not a claim that every login inserts a new root or chain. A login can continue an existing active chain for the same user and device. The session subsystem determines how the resulting session is issued and persisted. + +A chain is best understood as a **device-associated authentication lifecycle**, rather than as the physical device itself. The quality and stability of device identification depend on the configured device context. + +## How a login proceeds + +The standard password login flow can be viewed as the following pipeline: + +```text +Client request + β”‚ + β–Ό +Resolve authentication flow context + β”‚ Tenant Β· device Β· effective mode Β· client profile + β–Ό +Resolve identifier β†’ UserKey + β”‚ + β–Ό +Check user eligibility and security state + β”‚ + β–Ό +Validate credential using registered providers + β”‚ + β–Ό +Find an eligible active chain for this user/device + β”‚ + β–Ό +LoginAuthority decision + β”œβ”€β”€ Deny ─────────► Failure / lockout handling + β”œβ”€β”€ Challenge ────► Continuation required + └── Allow + β”‚ + β–Ό + Establish session + β”‚ + β–Ό + Optionally issue access/refresh tokens + β”‚ + β–Ό + Dispatch login event + β”‚ + β–Ό + Deliver credentials / redirect or OK ``` -> Login does not create a token -> β†’ It creates a session +This diagram shows the responsibilities in the current flow. Some security-state operations, such as recording failed attempts, happen as part of processing a rejected login. Therefore, **the authority decision precedes session issuance, not every possible state mutation**. -Tokens are optional outputs derived from the session. +### 1. Resolve the authentication context -
+Before credential validation, UltimateAuth operates within an `AuthFlowContext`. This context provides the effective tenant, device information, authentication mode, client profile, and applicable server options. -## πŸ”„ Step-by-Step Execution -The login flow follows a structured pipeline: +That context matters because the same logical login operation can have different delivery and session requirements depending on how the application is hosted and which kind of client is connecting. Login should not need to be reimplemented for each application style. -### 1️⃣ Identifier Resolution -The system resolves the user identity: +A resolvable device ID is required by the current login orchestrator. If device identification cannot be resolved, login cannot proceed to normal session establishment. -- Username / email / phone β†’ `UserKey` +### 2. Resolve the login identifier -### 2️⃣ User & Security State -The system loads: +A user may identify themselves with a username, email address, or another identifier supported by the configured resolver. UltimateAuth translates that external identifier into an internal `UserKey` through `ILoginIdentifierResolver`. -- User existence -- Account state -- Factor (credential) state +```text +User-provided identifier + β”‚ + β–Ό +ILoginIdentifierResolver + β”‚ + β–Ό + UserKey +``` -Checks include: +The internal user key, not the original text entered into the login form, becomes the stable reference used by the rest of the authentication flow. This helps keep **how a user signs in** separate from **who that user is**. -- Is the account locked? -- Is reauthentication required? +The identifiers actually accepted depend on the configured resolver and user data; support for a particular identifier format should not be assumed without configuration. -### 3️⃣ Credential Validation -Credentials are validated using providers: +### 3. Check eligibility and security state -- Password -- (Extensible: OTP, external providers, etc.) +Once the user is resolved, the login orchestrator retrieves their runtime state. The current implementation requires the user to exist, be permitted to authenticate (`CanAuthenticate`), and not be deleted (`IsDeleted`). -### 4️⃣ Authority Decision -The **LoginAuthority** evaluates the attempt. +It also examines two security scopes: -Possible outcomes: +- **Account state:** security conditions associated with the account as a whole. +- **Factor state:** security conditions associated with the credential type being used. -- βœ… Allow -- ❌ Deny -- ⚠️ Challenge (e.g. MFA) +An existing account or factor lock can stop the attempt before credential validation proceeds. Failed attempts, lockout duration, failure windows, and lock extension are governed by login options. -πŸ‘‰ No session is created before this decision. +This separation is intentional: a credential can be correct while the account is not currently eligible to authenticate. -### 5️⃣ Device & Chain Resolution -The system checks if the device is known: +### 4. Validate the credential -- Existing device β†’ reuse chain -- New device β†’ create new chain +UltimateAuth delegates credential verification to registered credential providers rather than embedding password comparison directly into the login orchestrator. -πŸ‘‰ A **Chain represents a device** +For the current standard HTTP login endpoint, the credential type is **Password**. The orchestrator also rejects unsupported credential types in this path. The provider architecture is extensible, but that extensibility should not be confused with a promise that OTP, passkeys, or external identity providers all use this exact password-login endpoint in v0.1.0. -### 6️⃣ Session Creation -A new session is issued: +For eligible credentials, the orchestrator checks that the credential matches the requested type, has not been deleted, is usable under its security rules, and validates the submitted secret through its provider. -- Linked to user -- Linked to device (chain) -- Bound to tenant +This gives the login flow a stable orchestration model while keeping credential-specific verification behind dedicated abstractions. -Session hierarchy: -``` -User β†’ Root β†’ Chain β†’ Session +### 5. Resolve device-associated session continuity + +Before the authority decision, the orchestrator looks for a session chain associated with the resolved user and device. If a chain exists and its state is active under the configured idle timeout, its `ChainId` can be carried into session issuance. + +Conceptually: + +```text +Same user + known device + active chain + β”‚ + β–Ό + Continue with that ChainId + +No eligible active chain + β”‚ + β–Ό + Session subsystem establishes + the appropriate new chain ``` -### 7️⃣ Token Issuance (Optional) -Depending on the mode and request: +This enables device-aware continuity without reducing a login to β€œissue another unrelated token.” The session subsystem remains responsible for the exact creation and persistence behavior. + +### 6. Make the authority decision + +`LoginAuthority` evaluates the login attempt and returns one of three outcomes: + +| Decision | Meaning | Session issued? | +| --- | --- | --- | +| **Allow** | The attempt may proceed to session establishment. | Yes, if the remaining issuance steps succeed. | +| **Deny** | Authentication is rejected. | No. | +| **Challenge** | An additional step or continuation is required. | No, not from this login attempt. | + +The current default authority considers user existence, lockout, reauthentication requirements, and credential validity. The broader flow also performs eligibility and lockout checks before invoking the authority. + +A challenge is represented through a `LoginContinuation`; the current orchestrator uses an MFA continuation type for its challenge branch. This represents a requirement to continue authentication, **not proof that an MFA exchange has already completed**. + +For rejected attempts, the orchestrator may record a failed factor attempt and return lockout or remaining-attempt information when the corresponding disclosure option is enabled. Applications should treat these details as policy-controlled rather than universally available. + +### 7. Establish the session + +Only an allowed, successfully validated **commit** login proceeds to session issuance. + +The orchestrator first records a successful factor authentication (unless explicitly suppressed by an internal execution option), then obtains user claims through `IUserClaimsProvider`. -- Access token may be issued -- Refresh token may be issued +It constructs a `SessionIssuanceContext` containing the tenant, user key, device, claims, effective mode, session options, and any eligible existing chain ID. The context is passed to `ISessionOrchestrator` using a login-session creation command. -πŸ‘‰ Tokens are derived from the session -πŸ‘‰ Not the source of truth +This is the decisive transition: -### 8️⃣ Event Dispatch -The system emits: +> The user has not merely supplied a valid secret; the system has established a managed authentication session. -- Login events -- Audit information +The resulting session has its own identity. That identity is available to the later token-issuance and response-delivery stages. -
+### 8. Issue tokens when permitted -## 🧩 What Gets Created? -A successful login creates: +After session issuance, access and refresh tokens may be requested from the token issuer. The current orchestrator issues them only when `LoginRequest.RequestTokens` is true. -### πŸ”Ή Root -- One per user -- Represents global authentication state +For the HTTP login endpoint, however, the server derives that setting from `AuthFlowContext.AllowsTokenIssuance`. A client cannot force token issuance merely by placing `RequestTokens: true` in its submitted payload. -### πŸ”Ή Chain -- One per device -- Manages device lifecycle +When issuance is enabled: -### πŸ”Ή Session -- Individual authentication instance -- Represents a single login +- The access token is issued in the context of the authenticated user and issued session. +- Refresh-token issuance is requested with persistence enabled. +- Both issuance paths operate with the tenant, user key, session ID, chain ID, and claims context. -
+Depending on the effective mode and issuer behavior, an individual token type may be absent. **Successful login and token issuance are deliberately separate concepts.** -## πŸ“± Device Awareness -UltimateAuth is device-aware by design: +### 9. Dispatch the login event and deliver the response -- Each device gets its own chain -- Sessions are grouped by device -- Logout can target device or all sessions +Following successful session and optional token issuance, UltimateAuth dispatches a `UserLoggedInContext` event containing the tenant, user key, time, device, and session ID. -
+The HTTP endpoint then delegates credential delivery to `ICredentialResponseWriter`. Session ID, access token, and refresh token are handled as distinct grant kinds. Delivery is resolved using the effective mode, authentication flow, client profile, and server configuration. -## πŸ” Security Considerations -The login flow includes built-in protections: +Depending on configuration, the HTTP response may complete with `200 OK` or a success redirect. A rejected login may produce `401 Unauthorized` or a configured failure redirect. -- Account lockout -- Failed attempt tracking -- Device binding -- Security version validation +**Important for v0.1.0:** `TokenResponseMode.Body` is currently unsupported and rejected during response resolution. Do not assume that the HTTP login endpoint returns a JSON object containing the issued tokens. Consult the configured delivery modes and client profile instead. -πŸ‘‰ Security decisions are centralized in the Authority +## TryLogin: evaluate without establishing a session -
+UltimateAuth distinguishes **previewing a login** from **committing a login**. + +`TryLogin` runs the authentication evaluation in preview mode. It can indicate whether credentials would pass the current checks, report permitted failure information, and return a continuation requirement. It does **not** issue a session or tokens. + +| Behavior | TryLogin (Preview) | Login (Commit) | +| --- | --- | --- | +| Resolve identifier and evaluate credentials | Yes | Yes | +| Apply applicable failure-attempt handling | Yes | Yes | +| Reset factor failure state on success | No | Yes, normally | +| Create a session | No | Yes, on success | +| Issue tokens | No | Optional | +| Return a preview receipt | Possible | Consumes a matching receipt, if supplied | + +On a successful preview, the server can create a short-lived `PreviewReceipt`. That receipt is associated with the user, tenant, credential type, device, identifier, client profile, and a fingerprint of the submitted login material. + +When a subsequent commit request includes a matching receipt, the server consumes it and can suppress a duplicate failed-attempt registration for that commit. **The receipt is not a replacement for the password**: the commit path still performs credential validation. + +This separation is useful when an application needs to assess login readiness or guide a user through an authentication experience without establishing a session prematurely. + +## Device awareness and session management + +Because login produces a managed session within a device-associated chain, UltimateAuth can model authentication beyond a single browser token: + +- **Multiple devices:** a user can have separate chains associated with distinct device contexts. +- **Session continuity:** an active chain can be considered when the same user signs in again from the same device. +- **Targeted session management:** session and chain identities give downstream operations a way to distinguish authentication instances and device-associated lifecycles. +- **Broader revocation:** the hierarchy provides the foundation for operations that affect more than one session. + +The exact scope and availability of logout and revocation operations are defined by their own APIs and policies. Login establishes the identities those operations can act upon; it does not by itself specify every revocation behavior. + +## Security is part of the flow, not an afterthought + +Several security properties follow from this design: + +**User eligibility is separate from credential correctness.** A valid secret does not override a disabled, deleted, or locked account. + +**Failed attempts are tracked as security state.** Lockout behavior is configurable and can be evaluated before session issuance. + +**Device context participates in session establishment.** The system can distinguish device-associated chains rather than treating all logins as anonymous token issuance events. + +**Session issuance follows an explicit decision.** A denied or challenged login does not proceed to normal session creation. + +**Response delivery is policy-driven.** Whether a session credential or token is deliveredβ€”and by which supported mechanismβ€”is determined by the effective server flow, not solely by a client request. + +**The hierarchy supports later lifecycle enforcement.** Token validation, session validity, security-version checks, refresh behavior, and revocation are related concerns, but their exact enforcement belongs to those respective flows. They should not be mistaken for checks all performed inside the login method itself. + +## A useful mental model + +Think of login as the moment UltimateAuth establishes a **managed, device-aware authentication relationship** between a user and an application. + +```text +Identity + Valid Credential + Security Decision + β”‚ + β–Ό + Managed Session + β”‚ + β”Œβ”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β” + β–Ό β–Ό + Session identity Optional tokens + β”‚ β”‚ + β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β–Ό + Ongoing auth lifecycle +``` -## 🧠 Mental Model -If you remember one thing: +The session is the managed authentication instance. Tokens, where used, are credentials associated with that instance. They can be issued, refreshed, and validated without redefining what a login means. -πŸ‘‰ Login = session creation -πŸ‘‰ Not token issuance +This is the shift UltimateAuth is designed to make: **from login as token generation to login as lifecycle-aware session establishment**. -## πŸ“Œ Key Takeaways +## Key takeaways -- Login is a flow, not a function -- Authority decides before any state change -- Sessions are the source of truth -- Tokens are optional representations -- Device context is always considered +- **Login is a flow, not a single credential-check function.** Identity resolution, eligibility, security evaluation, session issuance, and delivery are separate stages. +- **The hierarchy is `User β†’ Root β†’ Chain β†’ Session`.** It expresses user-level, device-associated, and individual session lifecycles. +- **A successful login establishes a session.** It does not necessarily create a new root or chain each time. +- **Tokens are optional.** Issuance is governed by the effective mode and server policy. +- **TryLogin is not Login.** Preview evaluates an attempt without issuing a session; commit establishes one. +- **The authority controls admission to session issuance.** Security-state handling can also occur before and after that decision. +- **Device context matters.** Session continuity and downstream session management are built around it. -## ➑️ Next Step +## Next step -Continue to **Refresh Flow** +Continue to **Refresh Flow** to learn how UltimateAuth extends or rotates authentication credentials while preserving the relationship to the managed session lifecycle. diff --git a/docs/content/auth-flows/logout-flow.md b/docs/content/auth-flows/logout-flow.md index 74d0eec7..1b9baaa9 100644 --- a/docs/content/auth-flows/logout-flow.md +++ b/docs/content/auth-flows/logout-flow.md @@ -4,178 +4,266 @@ order: 4 group: auth-flows --- - # πŸšͺ Logout Flow -The logout flow in UltimateAuth is not a single action. -πŸ‘‰ It represents different **levels of authentication termination**. +Authentication does not end when a client deletes a cookie or forgets a token. In UltimateAuth, ending access is a **server-side lifecycle decision**: the system determines which authentication state must stop being usable, at what scope, and whether the associated device relationship should survive a future login. + +That distinction is central to UltimateAuth's design. **Logout ends access; revoke permanently invalidates the selected trust boundary.** Both can prevent a session from being used, but they express different intentions and have different consequences for the session hierarchy. + +## Logout is more than clearing credentials -## 🧠 What is Logout? -In traditional systems: +A traditional application may implement logout by removing a browser cookie or deleting a locally stored token. That is useful client cleanup, but by itself it does not necessarily invalidate a credential already issued by the server. A copied token could remain usable until its expiration if the server has no corresponding invalidation mechanism. + +UltimateAuth instead models authentication around a hierarchy: + +```text +User +└── Root Global authentication lineage + β”œβ”€β”€ Chain Device-associated authentication lineage + β”‚ β”œβ”€β”€ Session Individual authentication instance + β”‚ └── Session Another instance in the same chain + └── Chain Another device + └── Session +``` -- Logout = remove cookie or token +This structure gives termination a meaningful scope. An application can end access on the current device, sign out other devices, sign out all devices, or explicitly revoke a session, chain, or root. The correct choice depends on the intent: ordinary sign-out, account security, device compromise, or global trust reset. -In UltimateAuth: +> **Mental model:** Logout is a controlled exit from authentication. Revoke is a permanent invalidation of the selected authentication state. A later login may establish new state, but it does not undo a revocation. -πŸ‘‰ Logout affects **session, device, or identity scope** +## Logout versus revoke -> Logout is not just removing access -> β†’ It is controlling session lifecycle +### Logout: end access without necessarily destroying the device lineage -## πŸ”€ Logout vs Revoke -UltimateAuth distinguishes between two concepts: +An ordinary logout is designed for situations such as a user selecting **Sign out** in an application. It ends the relevant authenticated access while allowing the device-associated chain to remain available for a subsequent login, subject to its state and lifetime rules. -### πŸ”Ή Logout (Soft Termination) +```text +Before logout +Root (active) +└── Chain (active) + └── Session (authenticated) -- Ends the current session -- Keeps the device (chain) active -- Allows re-login without resetting device context +After logout +Root (active) +└── Chain (retained) + └── Session (no longer usable) +After a later successful login +Root (active) +└── Chain (reusable if still eligible) + └── New Session ``` -Session β†’ Invalidated -Chain β†’ Still Active + +**Retaining a chain does not mean retaining an authenticated session.** The user must authenticate again. Reuse is conditional: an expired, revoked, or otherwise ineligible chain must not be treated as active merely because it still exists in storage. + +A subtle but important implementation detail: the current HTTP logout flow resolves the authenticated session and invokes the flow service, whose logout command identifies and logs out the **associated chain**. Consequently, the default logout endpoint should not be described as a guarantee that *only one session record* changes. Its practical boundary is the current device chain. + +### Revoke: permanently invalidate the selected trust boundary + +Revocation is a stronger security operation. A revoked session, chain, or root is not restored by a subsequent login, refresh, or cookie update. + +```text +Chain (active) +└── Session (active) + +Revoke chain + +Chain (revoked β€” terminal state) +└── Sessions (invalidated) + +Future authentication β†’ new eligible chain, not restoration of the old one ``` -πŸ‘‰ User can log in again and continue on the same device chain +**Revocation is irreversible for the revoked entity.** This does not mean the user is permanently locked out of the application. A legitimate user may authenticate again and establish a new session lineage if account policy allows it. What cannot happen is bringing the *revoked* session or chain back to life. + +This distinction is useful in security-sensitive scenarios: a lost device, suspected credential theft, administrator intervention, or a decision to invalidate all existing authentication relationships. + +## Choosing the termination scope + +| Operation | Intended scope | What it means | +| --- | --- | --- | +| **Logout current device** | Current session's chain | Ends authenticated access for the current device while preserving the possibility of later eligible chain reuse. | +| **Logout a device** | Selected chain | Ends access on the chosen device; can be initiated by the user or an authorized administrator. | +| **Logout other devices** | Other device sessions | Ends access elsewhere while preserving the specified current device context. | +| **Logout all devices** | All user sessions | Signs the user out across devices without treating this as a root revocation. | +| **Revoke session** | One session | Permanently invalidates the selected session. | +| **Revoke chain** | One device lineage | Permanently invalidates that chain and its sessions. | +| **Revoke other/all chains** | Multiple device lineages | Permanently invalidates selected device relationships. | +| **Revoke root** | Global authentication lineage | Invalidates the root and cascades to its chains and sessions. | + +These are not interchangeable aliases. In particular, **logout all devices** and **revoke root** both disrupt broad access, but they do not express the same lifecycle intent. + +### Why session-level revoke is separate + +UltimateAuth exposes a session revocation operation distinct from the ordinary logout endpoint. A single session can be revoked without necessarily revoking its parent chain. This is useful when an application needs to invalidate a particular authentication instance rather than the entire device relationship. + +### Why root revoke is exceptional + +The root represents the user's active authentication lineage, not the user's existence in the user directory. Root revocation cascades through the associated authentication hierarchy. A later permitted login may establish a **new** active root; it does not reverse the old root's revocation. + +Root revocation should therefore be reserved for an intentional global trust reset rather than ordinary sign-out. + +## How the current logout flow executes -### πŸ”₯ Revoke (Hard Invalidation) -- Invalidates session, chain, or root -- Cannot be undone -- Forces a completely new authentication path +### 1. Resolve the flow context +The HTTP endpoint obtains the current `AuthFlowContext`. This carries the relevant authentication state and response configuration, including the resolved session when one is available. + +The ordinary logout endpoint does not accept an arbitrary session ID from the browser and blindly invalidate it. It uses the session already resolved for the current flow. + +### 2. Request server-side termination + +When a `SessionSecurityContext` is present, the endpoint constructs a `LogoutRequest` using that session ID and calls the flow service: + +```csharp +var request = new LogoutRequest +{ + SessionId = session.SessionId +}; + +await _client.Flows.LogoutAsync(request); ``` -Chain β†’ Revoked -Sessions β†’ Revoked -Next login β†’ New chain + +The logout command resolves the session's chain and invokes the session issuer's `LogoutChainAsync` operation. The session-store implementation performs the chain logout transition. + +This is the authoritative part of logout. Removing a client cookie alone is not a substitute for this state transition. + +### 3. Clear cookies configured for this flow + +After the server-side operation, the HTTP handler attempts to delete credentials whose **effective delivery mode is Cookie**: + +- Session ID cookie +- Refresh token cookie +- Access token cookie + +The handler uses the configured cookie names through `IUAuthCookieManager`. It does not assume that all three credentials are cookies, and it does not attempt to delete header-delivered credentials from a remote client's storage. + +When the flow has no resolved session, the endpoint can still perform cookie cleanup and return a successful logout response. This makes sign-out useful even when a browser holds stale credentials. + +### 4. Resolve the response and optional redirect + +Once termination and cookie cleanup have been handled, UltimateAuth evaluates the logout response configuration. The result is either a redirect to the configured destination or a normal HTTP success response. + +With redirect disabled, the ordinary endpoint returns: + +```json +{ + "isSuccess": true +} ``` -πŸ‘‰ Revoke resets trust for that scope +With redirect enabled, it returns an HTTP redirect to the resolved target instead. Applications should not expect the JSON success body in that case. + +## Why redirect matters + +A redirect is **not required to invalidate authentication state**. It is a navigation concern that becomes important in browser-based authentication flows. + +Consider a server-rendered application with a dedicated sign-out page. Once the session has ended and cookies are cleared, the browser should often return to a public page, a login screen, or the page originally requested by the user. Redirecting completes that user journey without requiring the application to parse a logout response and navigate manually. + +Redirects can also help when the authentication endpoint and the client application are on different origins or when a centralized authentication service serves multiple clients. The server can perform the security operation and then send the browser back to the appropriate application destination. + +In contrast, a mobile client or API consumer commonly prefers an ordinary HTTP result and controls its own navigation. **Redirect is a response behavior, not a security primitive.** + +### Redirect resolution in UltimateAuth + +The response resolver selects the effective redirect settings for the logout flow. The redirect resolver then: -
+1. Checks whether redirect is enabled. +2. On success, considers an allowed return URL override when configured. +3. Otherwise uses the configured success path, resolved against the client base address. +4. If no valid destination is configured, leaves redirect disabled and allows a normal success response. -## 🧩 Levels of Termination -UltimateAuth supports multiple logout scopes: +For absolute return URL overrides, the resolver restricts schemes to HTTP or HTTPS and validates the origin against configured allowed client origins when that allowlist is populated. Relative paths are resolved against the selected client base address. -### πŸ”Ή Session-Level Logout -- Terminates a single session -- Other sessions on the same device may remain +This is why a return URL should be treated as **validated navigation input**, never as an unrestricted arbitrary destination. Applications should configure allowed origins carefully, especially in multi-client or hosted scenarios. -### πŸ“± Device-Level (Chain) -- Terminates all sessions on a device -- Device chain is invalidated or reset +> **Important:** Do not use a redirect as proof that logout succeeded, or assume that clearing browser cookies invalidates every credential issued to every device. The server-side termination scope determines what access has actually ended. -### 🌐 Global Logout (All Devices) -- Terminates all sessions across all devices -- Keeps root (user identity) intact +## Device-aware logout -### πŸ”₯ Root Revoke -- Invalidates entire authentication state -- All chains and sessions are revoked +UltimateAuth treats the device-associated chain as a first-class authentication boundary. That allows applications to build features such as: -πŸ‘‰ This is the strongest possible action +- **Sign out of this device** β€” end the current device's authenticated access. +- **Sign out of another device** β€” target a specific chain after the appropriate access checks. +- **Sign out of other devices** β€” preserve the current device while ending other sessions. +- **Sign out everywhere** β€” end all device sessions for the user. +- **Revoke a lost device** β€” permanently invalidate the compromised chain instead of merely signing it out. -
+These operations are exposed through distinct application-service methods, rather than overloading a single logout call with ambiguous behavior. -## πŸ”„ Step-by-Step Execution +### Self-service and administrative actions -### 1️⃣ Flow Context Resolution -The system resolves: +Device and all-device HTTP endpoints use access contexts carrying explicit action identifiers. They are executed through the access orchestrator, which enforces the applicable authorization decision before the requested state mutation. -- Current session -- User identity -- Tenant +For example, `LogoutDeviceSelfAsync` targets the authenticated user's device, whereas `LogoutDeviceAdminAsync` accepts a target user and executes under an administrative action context. Both ultimately call the device logout application service; authorization and target ownership are not left to the client. -### 2️⃣ Authority Decision -Logout operations are validated: +The default current-device logout endpoint follows its own flow-based path. Do not assume every termination operation shares the same exact handler or authorization pipeline. -- Authorization checks -- Access validation +## Security properties and boundaries -πŸ‘‰ Logout is not blindly executed +### Server-side invalidation comes first -### 3️⃣ Scope Determination -The system determines what to terminate: +The server must stop recognizing the affected authentication state. Browser cleanup improves user experience and reduces accidental credential reuse, but it is not the source of truth. -- Session -- Chain -- Root +### Revoked state is terminal -### 4️⃣ Execution -Depending on scope: +Revocation cannot be undone by setting `IsRevoked` back to false, presenting an old refresh token, or logging in again with the same device. A new authentication path must establish new eligible state. -#### Session Logout -- Session is revoked -- Other sessions unaffected +### Scope must be explicit -#### Chain Revoke / Logout -- All sessions in the chain are revoked -- Device trust is reset +A session revoke should not be casually described as a chain revoke. A logout-all operation should not be described as a root revoke. These distinctions determine whether existing device lineages are retained, ended, or permanently invalidated. -#### Global Logout -- All chains are revoked (optionally excluding current) +### Authorization and ownership matter -#### Root Revoke -- Entire identity state is invalidated +Self-service operations must stay within the authenticated user's permitted scope. Administrative operations must be evaluated under their corresponding action and target context. A client-provided chain identifier is not sufficient authorization to terminate someone else's session. -### 5️⃣ Event Dispatch -The system emits: +### Token delivery affects cleanup, not the termination model -- Logout events -- Audit logs +Whether a client receives credentials through cookies or headers, the server-side hierarchy still governs session-backed authentication. Clients using non-cookie delivery must also clear their own local credentials after successful termination. -
+The precise behavior of already-issued, independently validated tokens depends on the active authentication mode and token validation strategy. Avoid assuming every token format provides instantaneous server-side revocation without checking its validation model. -## πŸ“± Device Awareness -Logout behavior is device-aware: +## Practical examples -- Each device is a chain -- Logout can target specific devices -- Sessions are grouped by device +### A user signs out at the end of the day -πŸ‘‰ This enables fine-grained control +Use the ordinary logout flow. The current device's authenticated access ends, configured cookies are cleared, and the browser may be redirected to a public page. A later successful login can reuse the retained device chain if it remains eligible. -
+### A user loses a phone -## πŸ” Security Model +Use **chain revoke**, not merely a soft device logout, when the goal is to permanently invalidate that device's existing trust lineage. The user may later authenticate on a replacement device, but the revoked chain stays revoked. -### πŸ”’ Controlled Termination -All logout operations: +### A user wants to keep this device but sign out elsewhere -- Pass through orchestrator -- Are validated by authority +Use **logout other devices**. The current device's chain is excluded while other device sessions are terminated. This is different from permanently revoking every other chain. -πŸ‘‰ Prevents unauthorized session manipulation +### An account is believed to be compromised -### πŸ” Irreversible Revocation -- Revoked chains cannot be restored -- Revoked sessions remain invalid +Depending on the security policy, revoke all chains or the root. This is a stronger response than ordinary logout because it deliberately invalidates existing authentication lineages. Credential rotation and additional account recovery steps may also be required; revocation alone does not prove that compromised credentials have been changed. -πŸ‘‰ Ensures strong security guarantees +## Common misconceptions -### πŸ”— Identity Boundaries +**β€œLogout just deletes the cookie.”** No. The ordinary endpoint performs server-side termination when a session is resolved, then clears applicable cookies. -- Session β†’ temporary identity proof -- Chain β†’ device trust boundary -- Root β†’ global identity state +**β€œLogout and revoke are the same operation.”** No. Both can stop access, but revocation is permanent for the selected entity; soft logout can retain eligible device lineage. -πŸ‘‰ Logout operates within these boundaries +**β€œLogout always means exactly one session.”** Not in the current default HTTP flow. The resolved session identifies a chain, and the logout command operates on that chain. -
+**β€œRevocation permanently bans the user.”** No. It permanently invalidates existing authentication state at the selected scope. A permitted future login may create new state. -## 🧠 Mental Model -If you remember one thing: +**β€œRedirect makes logout secure.”** No. Redirect controls where the browser goes after logout; the session-store transition is what ends server-recognized access. -πŸ‘‰ Logout = ending a session -πŸ‘‰ Revoke = resetting trust +**β€œLogout all devices is the same as revoke root.”** No. They are different lifecycle operations with different implications for the authentication hierarchy. -## πŸ“Œ Key Takeaways +## Key takeaways -- Logout and revoke are different operations -- Logout is reversible (via re-login) -- Revoke is permanent and forces new authentication -- Device (chain) is a first-class concept -- Security is enforced through authority and orchestrator +- **Logout is a lifecycle operation**, not merely client-side credential deletion. +- **Revoke is irreversible** for the revoked session, chain, or root; a future login never restores that entity. +- **Root β†’ Chain β†’ Session** makes termination scope explicit and device-aware. +- **Current-device logout operates through the associated chain** in the current server implementation. +- **Other-device and all-device logout** are separate from hard chain/root revocation. +- **Redirect is optional navigation behavior**, especially useful for browser-based flows; it does not perform revocation. +- **Server-side state is authoritative** for session-backed access; client credential cleanup remains important. -## ➑️ Next Step +## ➑️ Next step -Continue to **Session Lifecycle** +Continue to **Session Lifecycle** to understand how roots, chains, and sessions are created, rotated, expired, logged out, and revoked over time. From eec4e0281d1c62178dcc665a2d850ebdad9c197d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20Can=20Karag=C3=B6z?= Date: Tue, 13 Oct 2026 15:23:26 +0300 Subject: [PATCH 2/7] Improve AuthFlow Page --- docs/content/auth-flows/index.md | 280 +++++++++++++++++++++---------- 1 file changed, 193 insertions(+), 87 deletions(-) diff --git a/docs/content/auth-flows/index.md b/docs/content/auth-flows/index.md index 4db4c8b3..8a508958 100644 --- a/docs/content/auth-flows/index.md +++ b/docs/content/auth-flows/index.md @@ -4,140 +4,246 @@ order: 1 group: auth-flows --- - # πŸ” Auth Flows -Authentication in UltimateAuth is not a single operation. +Authentication is often described as a collection of endpoints: one accepts a password, another renews a token, and another clears a cookie. Those endpoints are useful interfaces, but they are not the security model. + +**UltimateAuth treats authentication as a set of controlled flows operating within an explicitly resolved security context.** A flow represents an intentionβ€”establish access, continue it, or terminate itβ€”and the server determines which rules, state, and delivery behavior apply before executing that intention. + +This distinction matters because the same request can have different legitimate outcomes depending on the tenant, authentication mode, client profile, device, current session, and server policy. A flow should not have to guess those values independently. + +## A flow is more than an endpoint + +An endpoint receives an HTTP request. A flow describes what the request is attempting to do and provides a consistent context for the operation. + +In UltimateAuth, the HTTP boundary, context resolution, domain orchestration, authorization decisions, persistence, and response delivery have separate responsibilities. This makes authentication behavior easier to reason about without forcing every flow to use exactly the same internal implementation. + +> **Core principle:** Resolve the security context first. Make decisions against authoritative state. Commit only the changes permitted by the flow. Then deliver the outcome in the form appropriate for the client. + +## The request-to-result pipeline + +The following sequence describes the common **HTTP auth-flow boundary**. Individual flows have additional steps, and their internal order may differ. + +```text +Incoming HTTP request + | + v +UltimateAuth Middleware Pipeline + | + +-- TenantMiddleware + | - Resolve tenant context + | - Store tenant context in HttpContext + | + +-- SessionResolutionMiddleware + | - Resolve session identifier + | - Build SessionContext (or anonymous context) + | + +-- UserMiddleware + | - Resolve user context through IUserAccessor + | + |-- ASP.NET Core Authentication and Authorization Middleware (Optional) + | + v +Request Context Prepared + | + v +Endpoint metadata (AuthFlowType) + | + v +AuthFlowEndpointFilter + | + v +AuthFlow.BeginAsync(...) + | + v +AuthFlowContextFactory + - Read tenant, session, and user contexts + - Resolve client profile and effective server options + - Determine effective authentication mode + - Resolve primary token kind and response delivery policy + - Resolve device context and return URL + - Validate existing session where applicable + - Reject invalid or unresolved tenant + | + v +AuthFlowContext (request-scoped) + | + v +Endpoint handler and flow-specific validation + | + v +Flow service / orchestrator + | + v +Security and authorization decisions + | + v +Permitted state transitions and optional token operations + | + v +Response delivery, optional redirect, and flow events +``` -πŸ‘‰ It is a **flow-driven system**. +This is a **conceptual pipeline**, not a promise that every flow creates a session, issues a token, or dispatches the same events. Nor is there one universal authority method that all flows invoke identically. -
+### 1. A request identifies the flow -## 🧠 What is an Auth Flow? -An auth flow represents a complete authentication operation, such as: +UltimateAuth endpoints can carry `AuthFlowMetadata` describing the `AuthFlowType`. The `AuthFlowEndpointFilter` reads that metadata and invokes `IAuthFlow.BeginAsync(...)` before the endpoint handler executes. -- Logging in -- Refreshing a session -- Logging out +This is an important boundary: the endpoint does not need to assemble its own authentication environment from scattered headers, cookies, and settings. -Each flow: +The filter initializes the flow context; it does **not**, by itself, validate a password, authorize every operation, or mutate the session store. Those responsibilities belong to the appropriate downstream components. -- Has a defined lifecycle -- Runs through the orchestration pipeline -- Produces a controlled authentication outcome +### 2. The server constructs `AuthFlowContext` -πŸ‘‰ Instead of calling isolated APIs, you execute **flows**. +`AuthFlowContextFactory` resolves the inputs that govern how the operation must behave. The resulting context includes: -## πŸ”„ Why Flow-Based? -Traditional systems treat authentication as: +| Context value | Why it matters | +| --- | --- | +| `FlowType` | Identifies the intended authentication operation. | +| `Tenant` | Establishes the tenant boundary for state and decisions. | +| `ClientProfile` | Describes the kind of client interacting with the server. | +| `EffectiveMode` | Determines the authentication model actually in force for this request. | +| `Options` | Supplies the effective server policy. | +| `PrimaryTokenKind` | Identifies the primary credential/token kind for the effective mode. | +| `Device` | Carries the resolved device context. | +| `Session` | Carries validated session security context when available. | +| `IsAuthenticated` / `UserKey` | Represent the resolved authentication identity for this request. | +| `Response` | Describes effective credential delivery and redirect behavior. | +| `ReturnUrlInfo` | Carries parsed navigation intent for applicable browser flows. | -- A login endpoint -- A token generator -- A cookie setter +The factory rejects unresolved or invalid tenant context and disallowed authentication modes. When a session context is present, it invokes session validation. For session-authoritative modes such as `PureOpaque` and `Hybrid`, that validation also affects the resolved authentication state. -πŸ‘‰ These approaches often lead to fragmented logic. +**The context is resolved, not blindly trusted.** Request-provided information may influence resolution, but the effective mode, policy, identity, and session validity are determined by server-side components. -UltimateAuth solves this by: -- Structuring authentication as flows -- Enforcing a consistent execution model -- Centralizing security decisions +### 3. The context is made available to the handler -
+`AuthFlow.BeginAsync` stores the created context through the request-scoped `AuthFlowContextAccessor`. The endpoint handler can then access the same resolved context rather than reconstructing it. -## 🧩 What Happens During a Flow? -Every flow follows the same pattern: -``` -Flow β†’ Context β†’ Orchestrator β†’ Authority β†’ Result +This creates a shared vocabulary across the HTTP boundary: the handler and downstream services work with the same tenant, mode, device, and response policy. + +### 4. Flow-specific validation and orchestration begin + +Once the context exists, the endpoint handler reads and validates the operation-specific request. For example, login needs an identifier and secret; logout needs an applicable session or target scope; refresh needs valid renewal material. + +The handler delegates the actual operation to the relevant flow service or orchestrator. Those components coordinate credential checks, session operations, token operations, and state transitions as appropriate. + +There is no benefit in pretending these operations are identical. **Their shared architecture is the context and security boundary; their individual state transitions remain purpose-specific.** + +### 5. Decisions are evaluated against authoritative state + +UltimateAuth distinguishes between what a client *presents* and what the system *knows*. + +A cookie, bearer token, device identifier, or user-supplied value is evidence or inputβ€”not automatically the source of truth. The relevant authoritative data comes from server-managed user, credential, authentication-security, and session state, subject to the active authentication mode. + +For session-backed flows, the hierarchy is: + +```text +User + └── Root + └── Chain (device-associated lifecycle) + └── Session (authentication instance) ``` -- The **flow** defines the intent -- The **context** carries state -- The **orchestrator** coordinates execution -- The **authority** enforces rules +The root, chain, and session provide distinct scopes for continuity, validation, and invalidation. Tokens and cookies can represent access to that state, but should not be confused with the underlying lifecycle. -πŸ‘‰ This ensures consistent and secure behavior across all operations. +**Mode distinction:** Do not assume that every mode has identical persistence or session-issuance behavior. In particular, `PureJwt` is not a session-issuance mode in the current session issuer. The effective mode determines which state and proof are authoritative for a given operation. -
+### 6. Only permitted transitions are committed -## πŸ” Types of Flows -UltimateAuth provides built-in flows for common scenarios: +A successful authentication operation may create a session, rotate or touch existing state, revoke access, or leave persistent state unchanged. The actual effect depends on the flow and mode. -### πŸ”‘ Login Flow -Establishes authentication by: +For example: -- Validating credentials -- Creating session hierarchy (root, chain, session) -- Issuing tokens if required +- Login verifies credentials and, in session-issuing modes, establishes a session before optionally issuing tokens. +- Refresh evaluates existing renewal authority and applies its configured continuation behavior; it is not simply a request to mint another token. +- Logout ends access at the relevant scope, while revoke represents irreversible invalidation of the revoked object or hierarchy scope. -πŸ‘‰ [Learn more](./login-flow.md) +Where a session command passes through `UAuthSessionOrchestrator`, `IAuthAuthority` makes an allow, deny, or challenge decision before the session issuer executes that command. Other operations use their own relevant decision and access orchestration paths. -### πŸ”„ Refresh Flow -Extends an existing session: +This separation helps prevent HTTP transport details from becoming security policy. -- Rotates refresh tokens -- Maintains session continuity -- Applies sliding expiration +### 7. The outcome is delivered according to policy -πŸ‘‰ [Learn more](./refresh-flow.md) +A successful domain operation and the HTTP response carrying its outcome are different concerns. -### πŸšͺ Logout Flow -Terminates authentication: +`AuthResponseResolver` derives the effective response configuration from the authentication mode, flow type, client profile, and server options. It binds applicable cookie settings and validates the delivery configuration before the endpoint performs the operation. -- Revokes session(s) -- Invalidates tokens -- Supports device-level or global logout +This means a flow can use the same underlying authentication semantics while producing a client-appropriate HTTP outcomeβ€”for example, a response with managed cookies or a browser redirect when configured. -πŸ‘‰ [Learn more](./logout-flow.md) +**Current implementation note:** `TokenResponseMode.Body` credential delivery is explicitly unsupported by the response resolver. Do not assume that access or refresh credentials are returned in a JSON response body. -
+A redirect is a navigation decision, **not** an authentication or revocation mechanism. It can return a browser to the application after login or logout; the underlying security operation must stand independently of whether the browser follows the redirect. -## 🧠 Supporting Concepts -These flows operate on top of deeper system models: +## Why `AuthFlowContext` matters -### 🧬 Session Lifecycle +Without a shared flow context, individual endpoints tend to make their own assumptions: which tenant is active, whether a cookie represents an authenticated session, which token type is expected, whether a client should receive cookies, and where to navigate afterward. -- Root β†’ Chain β†’ Session hierarchy -- Device-aware session structure -- Lifecycle management and revocation +Those assumptions drift as applications add multiple clients, devices, tenants, or authentication modes. -πŸ‘‰ [Learn more](./session-lifecycle.md) +UltimateAuth instead resolves these questions at the flow boundary. That provides three practical benefits: -### 🎟 Token Behavior +1. **Consistency.** The same effective policy is available throughout the operation. +2. **Separation of concerns.** Endpoint handlers handle HTTP; orchestrators coordinate security-relevant work; response components handle delivery. +3. **Extensibility.** New client profiles and flow behaviors can integrate with established boundaries instead of duplicating authentication plumbing. -- Access token vs refresh token -- Opaque vs JWT -- Mode-dependent behavior +A context is not an authorization bypass. Possessing an `AuthFlowContext` does not imply that the requested operation has been approved. -πŸ‘‰ [Learn more](./token-behavior.md) +## Source of truth: proof versus state -### πŸ“± Device Management +A useful mental model is to distinguish **proof**, **policy**, and **state**: -- Device binding -- Multi-device sessions -- Security implications +| Concept | Examples | Responsibility | +| --- | --- | --- | +| Proof | Password, session identifier, access token, refresh token | Presents evidence for an operation. | +| Policy | Effective mode, server options, authority decisions | Determines what is permitted. | +| State | User eligibility, credential records, security state, session root/chain/session | Determines the authoritative lifecycle and validity where applicable. | +| Delivery | Cookies, supported response modes, redirect | Communicates the result to the client. | -πŸ‘‰ [Learn more](./device-management.md) +A valid-looking token is not, by itself, permission to ignore revocation, tenant boundaries, session state, or mode-specific validation. Conversely, not every authentication mode is built around a persisted session hierarchy. The authoritative checks must match the configured model. -
+This distinction is central to UltimateAuth's vision: **authentication is not the possession of a string; it is the outcome of a controlled security process.** -## 🧠 Mental Model +## Built-in flows at a glance -If you remember one thing: +The following pages explain each operation in detail. This overview focuses on their roles within the shared model. -πŸ‘‰ **Authentication is not a single step** -πŸ‘‰ **It is a controlled flow of state transitions** +| Flow | Purpose | Practical outcome | Guide | +| --- | --- | --- | --- | +| **Login** | Establish authenticated access from acceptable credentials. | Creates a valid authentication session in session-issuing modes; may issue tokens according to policy. | [Login Flow](./login-flow.md) | +| **Refresh** | Continue an existing authentication relationship using renewal authority. | Maintains or renews access according to mode, validation, and configured refresh behavior. | [Refresh Flow](./refresh-flow.md) | +| **Logout** | End access at the applicable session or device scope. | Terminates authentication access and handles configured client cleanup/navigation. | [Logout Flow](./logout-flow.md) | -## πŸ“Œ Key Takeaways +These flows are related but not interchangeable. Login establishes access; refresh continues it; logout terminates it. Revocation operations provide stronger invalidation controls for the relevant security scope. -- Authentication is executed as flows -- Each flow follows a consistent pipeline -- Sessions and tokens are created as part of flows -- Security is enforced centrally +## Concepts shared by the flows ---- +| Concept | What it contributes | Guide | +| --- | --- | --- | +| **Session lifecycle** | Root, chain, and session relationships; creation, continuity, expiry, and invalidation. | [Session Lifecycle](./session-lifecycle.md) | +| **Token behavior** | Access and refresh credentials, validation, renewal, and mode-specific behavior. | [Token Behavior](./token-behavior.md) | +| **Device management** | Device-associated chains, multi-device access, and targeted termination. | [Device Management](./device-management.md) | + +## What this design enables in real applications + +Consider an application with a browser front end, a mobile client, and an administrative interface. They may share the same user identity system, but they do not necessarily have the same credential delivery needs or session behavior. + +A flow-driven architecture lets the server establish the applicable context first and then run the requested operation under that context. Developers can reason about **what authentication action is taking place** separately from **how the resulting credentials or navigation are delivered**. + +It also makes security-sensitive behavior easier to locate: tenant and mode resolution occur at the boundary, domain validation happens in the flow, session operations use dedicated infrastructure, and response policy is resolved independently. + +This is the larger goal of UltimateAuth's unified modelβ€”not to make every authentication operation identical, but to make their differences explicit, controlled, and composable. + +## Key takeaways -## ➑️ Next Step +- **An auth flow is a controlled operation**, not merely an HTTP endpoint. +- **`AuthFlowEndpointFilter` initializes the request context** for endpoints carrying flow metadata. +- **`AuthFlowContext` resolves tenant, mode, client profile, device, identity/session information, and response policy** before the handler executes. +- **Proof is not the source of truth.** Validation depends on authoritative state and the effective authentication mode. +- **Authority and orchestration are distinct responsibilities.** The exact checks and state transitions vary by flow. +- **Credential delivery and redirects are outcomes**, not substitutes for security decisions. +- **Login, refresh, and logout share a model without sharing identical execution paths.** -Start with the most important flow: +## Next step -πŸ‘‰ Continue to **Login Flow** +Start with [Login Flow](./login-flow.md) to see how UltimateAuth turns validated credentials into controlled authenticated access. From 9ed86c130a5b1971370bf35485199d2f9a8f79e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20Can=20Karag=C3=B6z?= Date: Tue, 13 Oct 2026 16:18:11 +0300 Subject: [PATCH 3/7] Website Static Render Improvement --- docs/content/getting-started/index.md | 186 +++++++++++------- docs/content/getting-started/quickstart.md | 2 +- .../Pages/DocsContent.razor | 9 +- .../wwwroot/docs/auth-flows/index.json | 74 ++++--- .../wwwroot/docs/auth-flows/login-flow.json | 79 ++++---- .../wwwroot/docs/auth-flows/logout-flow.json | 127 +++++++----- .../wwwroot/docs/getting-started/index.json | 62 +++--- .../docs/getting-started/quickstart.json | 2 +- .../DocIndexEntry.cs | 3 + .../Program.cs | 47 +++++ .../wwwroot/robots.txt | 4 + 11 files changed, 365 insertions(+), 230 deletions(-) create mode 100644 docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm/DocIndexEntry.cs create mode 100644 docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm/wwwroot/robots.txt diff --git a/docs/content/getting-started/index.md b/docs/content/getting-started/index.md index 2fe4abd8..cc8c3fde 100644 --- a/docs/content/getting-started/index.md +++ b/docs/content/getting-started/index.md @@ -6,119 +6,167 @@ group: getting-started # πŸš€ Getting Started -Welcome to **UltimateAuth** β€” the modern authentication framework for .NET. +Welcome to **UltimateAuth**, a unified authentication and authorization framework for modern .NET applications. -UltimateAuth is designed to make authentication **simple to use**, while still being **powerful, flexible, and deeply secure** enough for real-world applications. +Authentication should not become a different system every time your application gains a new client, tenant, device, or security requirement. UltimateAuth brings these concerns into **one coherent model**β€”without pretending that every application needs the same authentication strategy. + +**One framework. Multiple authentication models. A consistent security lifecycle.** ## What is UltimateAuth? -UltimateAuth is a **flow-driven authentication framework** that reimagines how authentication works in modern .NET applications. +UltimateAuth is a modular, flow-driven framework for building and managing authentication in .NET. It connects concepts that are often implemented separately: + +- **Sessions, cookies and tokens** β€” stateful session management alongside access and refresh token workflows. +- **Authentication and authorization** β€” identity establishment and access decisions within a consistent architecture. +- **Users and credentials** β€” identity, identifiers, credentials, and their security state. +- **Devices and session continuity** β€” device-aware session chains, refresh, logout, and revocation. +- **Clients and delivery mechanisms** β€” browser, API, Blazor, and mobile scenarios with client-aware behavior. +- **Tenants and isolation** β€” tenant-aware request context and authentication boundaries. +- **Security policies and execution flows** β€” explicit decisions, validation, and controlled state transitions. + +The goal is not to hide these concepts. It is to make them **work together predictably**. + +## Unified Does Not Mean One-Size-Fits-All + +A server-rendered web application, a browser-based SPA, a mobile app, and a backend API do not share the same trust boundaries or credential transport requirements. + +UltimateAuth provides a common foundation while allowing behavior to be shaped by the **effective authentication mode**, **client profile**, **tenant**, and **request context**. + +| Concern | What UltimateAuth brings together | Why it matters | +| --- | --- | --- | +| Authentication models | Session-oriented and token-oriented workflows | Different clients can participate in one security architecture | +| Session lifecycle | Root β†’ Chain β†’ Session | Manage authentication at identity, device, and session scope | +| Client environments | ASP.NET Core, Blazor, APIs, and .NET mobile clients | Avoid rebuilding the core authentication model for each frontend | +| Multi-tenancy | Tenant resolution and tenant-bound context | Keep authentication operations within the intended tenant boundary | +| Credentials and identities | User identifiers, credential providers, and security state | Separate *who the user is* from *how they prove it* | +| Security enforcement | Context, validation, authority, and orchestration | Make decisions explicit rather than scattering checks across endpoints | +| Credential delivery | Mode- and client-aware response policies | Handle cookies, tokens, and redirects according to the scenario | +| Session termination | Logout, targeted revocation, and device management | Respond proportionately to ordinary sign-out and security incidents | + +**Unified** is therefore about a shared security modelβ€”not forcing all applications to use identical cookies, tokens, or endpoints. + +## Authentication Is More Than Issuing a Token + +In many applications, authentication is described as a short sequence: validate credentials, generate a token, return it to the client. + +That is only part of the story. -It unifies: +A production authentication system must also answer questions such as: -- Session-based authentication -- Token-based authentication (JWT) -- PKCE flows for public clients -- Multi-client environments (Blazor Server, WASM, MAUI, APIs) +- Which tenant does this request belong to? +- Which user and credential are being evaluated? +- Is an existing session still valid? +- Is the request coming from a known device? +- Can access be renewed, restricted, or terminated? +- What should happen when a credential changes or a device is no longer trusted? -into a single, consistent system. +UltimateAuth treats authentication as a **controlled lifecycle**. Login, refresh, and logout are operations within that lifecycleβ€”not unrelated endpoints that happen to exchange tokens. -Instead of choosing between cookies, sessions, or tokens, UltimateAuth allows you to use **the right model for each scenario β€” automatically**. +## Sessions Are First-Class -## What Makes UltimateAuth Different? +Where session-backed modes apply, UltimateAuth models authentication through a hierarchy: -### πŸ”Ή Session is a First-Class Concept +```text +User + └── Root Global session-security boundary + └── Chain Device-associated lifecycle + └── Session Individual authentication instance +``` -Unlike traditional systems, UltimateAuth treats sessions as a **core domain**, not a side effect. +This structure gives the system a vocabulary for different security actions. A session can be invalidated, a device-associated chain can be terminated, and a broader security boundary can be revoked. -- Root β†’ global security authority -- Chain β†’ device context -- Session β†’ actual authentication instance +**A token is not the entire authentication model.** It is a credential used within that model, subject to the applicable mode and validation rules. -This allows: +Not every authentication mode has identical persistence or session behavior. The session hierarchy describes the stateful lifecycle where that model is in use. -- Instant revocation -- Multi-device control -- Secure session lifecycle management +## Flow-Driven by Design -### πŸ”Ή Flow-Based, Not Token-Based +UltimateAuth organizes authentication operations around explicit flows. A flow brings together the request context, operation-specific validation, security decisions, permitted state transitions, and response behavior. -UltimateAuth is not cookie-based or token-based. +```text +Request + β†’ Tenant, session, and user resolution + β†’ AuthFlowContext + β†’ Flow-specific validation + β†’ Security decision and orchestration + β†’ State transition (when permitted) + β†’ Result and client-appropriate response +``` -It is **flow-based**: +This is not a claim that pipelines or orchestration are new inventions. The value is in applying them **consistently across authentication operations**, so the rules are easier to reason about, test, and extend. -- Login is a flow -- Refresh is a flow -- Re-authentication is a flow +For the implementation model, see [Auth Flows](../auth-flows/auth-flows.md). -πŸ‘‰ Authentication becomes **explicit, predictable, and controllable** +## Tenant-Aware from the Request Boundary -### πŸ”Ή Built for Blazor and Modern Clients +Multi-tenancy is not simply a `TenantId` added to a user table. Authentication must understand *which tenant's security context* a request is operating within. -UltimateAuth is designed from the ground up for: +UltimateAuth resolves tenant context as part of the ASP.NET Core request pipeline and carries it into the authentication flow. The flow context then combines that boundary with client settings, device information, and applicable session validation. -- Blazor Server -- Blazor WASM -- .NET MAUI +This helps teams design multi-tenant applications without treating tenant isolation as an afterthought. **Tenant resolution and configuration still need to be correctly integrated by the host application.** -With: +## Built for Different Clients, Not Just Different Screens -- Native PKCE support -- Built-in Blazor components (`UAuthLoginForm`, `UAuthApp`) -- Automatic client profile detection +UltimateAuth is designed for applications that may span several environments: -πŸ‘‰ No hacks. No manual glue code. +- **ASP.NET Core web applications** with server-managed authentication concerns. +- **Blazor Server and Blazor WebAssembly** with different hosting and transport constraints. +- **.NET MAUI** and other native clients with device-oriented authentication needs. +- **HTTP APIs** serving browsers, applications, and other services. -### πŸ”Ή Runtime-Aware Authentication +Client profiles and effective server options help adapt authentication behavior to the requesting client. Response policies determine how applicable credentials are delivered and whether a redirect belongs in the interaction. -Authentication behavior is not static. +The result is a common backend security vocabulary across multiple application typesβ€”not a promise that every client uses the same transport or requires zero configuration. -UltimateAuth adapts based on: +## Security Is a Lifecycle, Too -- Client type -- Auth mode -- Request context +Security is not finished when login succeeds. UltimateAuth's architecture accounts for what happens *after* access is established: -πŸ‘‰ Same system, different optimized behavior. +| Situation | Security question | UltimateAuth concept | +| --- | --- | --- | +| A user signs in | Should this attempt establish authentication? | Login flow and security decisions | +| A session needs renewal | Can continuity be preserved safely? | Refresh flow and session lifecycle | +| A user signs out | Which authentication scope should end? | Logout | +| A device is lost | How can that device's access be terminated? | Chain-level management and revocation | +| A credential is compromised | How should existing authentication be affected? | Credential security state and revocation policies | +| An administrator manages access | Is the operation permitted for this actor and target? | Authorization and access context | -## What Problems It Solves +A key distinction is **logout versus revoke**. Ordinary logout ends authentication according to its scope; revocation is an irreversible invalidation of the targeted security state. Signing in again may establish *new* authentication, but it does not restore what was revoked. -UltimateAuth addresses real-world challenges: +Explore the details in [Login Flow](../auth-flows/login-flow.md), [Refresh Flow](../auth-flows/refresh-flow.md), and [Logout Flow](../auth-flows/logout-flow.md). -### πŸ”Ή Multiple Client Types +## Modular and Extensible -Blazor Server, WASM, MAUI, and APIs all behave differently. +UltimateAuth is intended to serve both straightforward applications and systems with more demanding security requirements. Its architecture separates responsibilities such as core contracts, server execution, client integration, user management, credentials, and authorization. -UltimateAuth handles these differences automatically using **Client Profiles**. +This separation makes it possible to understand and evolve individual concerns without reducing authentication to a collection of disconnected custom handlers. -### πŸ”Ή Session vs Token Confusion +**Extensible does not mean every possible flow is already implemented.** Available modes, integrations, and capabilities should be evaluated against the version you are using; the documentation for each feature describes its current behavior and limitations. -Should you use cookies, sessions, or JWT? +## When Should You Choose UltimateAuth? -UltimateAuth removes this decision by supporting multiple auth modes and selecting the correct behavior at runtime. +UltimateAuth is especially relevant when: -### πŸ”Ή Secure Session Management -- Device-aware sessions -- Session revocation -- Refresh token rotation -- Replay protection +- You need quickly-installed, consistent, secure and easy-to-use auth in your .NET application. +- Your .NET application hasβ€”or will haveβ€”**more than one client type**. +- You need **device-aware session management**, not only token generation. +- You are designing a **multi-tenant** system with clear authentication boundaries. +- You want **authentication and authorization** to follow a consistent architectural model. +- You need control over **refresh, logout, revocation, and credential lifecycle**. +- You value a **modular, self-hosted, open-source** foundation you can understand and extend. -All built-in β€” no custom implementation required. +Also for a small application with minimal authentication needs, UltimateAuth brings the simplest possible solution. -### πŸ”Ή Complex Auth Flows -Login, logout, refresh, PKCE, multi-device control etc. +## Open Source First -All exposed as **simple application-level APIs**. +UltimateAuth is built with a simple commitment: **self-hosted authentication should remain accessible**. The framework is open source, and self-hosting is intended to remain free. A hosted offering may provide an operational alternative, not a separate tier of authentication features. -## When to Use UltimateAuth -Use UltimateAuth if: +The ambition is to build a framework that developers can adopt, inspect, contribute to, and trustβ€”not merely a service they must depend on. -- You are building a modern .NET application **Blazor Server, WASM or MAUI** -- You need **session + token hybrid authentication** -- You want **full control over authentication flows** -- You are building a **multi-client system (web + mobile + API)** -- You need **strong security with extensibility** +## Where to Go Next +You do not need to understand every internal component before getting started. -πŸ‘‰ Continue to **Quick Start** to build your first UltimateAuth application. +**Continue to Quick Start** to set up your first UltimateAuth application. Then explore [Auth Flows](../auth-flows/auth-flows.md) to understand how the pieces work together. +**The idea to remember:** UltimateAuth unifies authentication concepts without erasing their differences. One framework, a consistent lifecycle, and room for the security model your application actually needs. diff --git a/docs/content/getting-started/quickstart.md b/docs/content/getting-started/quickstart.md index ea2b7c47..1d5b1706 100644 --- a/docs/content/getting-started/quickstart.md +++ b/docs/content/getting-started/quickstart.md @@ -94,7 +94,7 @@ For in memory builder.Services.AddUltimateAuthSampleSeed(); ``` -For entity framework core: +For entity framework core, use scoped services: ```csharp builder.Services.AddScopedUltimateAuthSampleSeed(); ``` diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsContent.razor b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsContent.razor index e2b4bbfb..d39df8c7 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsContent.razor +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsContent.razor @@ -4,14 +4,7 @@ @inject NavigationManager Nav @inject IJSRuntime JS -@if (_loading || !RendererInfo.IsInteractive) -{ -
- -
- return; -} -else if (_notFound) +@if (_notFound) { diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/index.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/index.json index bb7849a2..f31bce6a 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/index.json +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/index.json @@ -1,76 +1,86 @@ { "Slug": "auth-flows/index", "Title": "Auth Flows", - "Html": "\n\u003Cp\u003EAuthentication in UltimateAuth is not a single operation.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 It is a \u003Cstrong\u003Eflow-driven system\u003C/strong\u003E.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022what-is-an-auth-flow\u0022\u003E\uD83E\uDDE0 What is an Auth Flow?\u003C/h2\u003E\n\u003Cp\u003EAn auth flow represents a complete authentication operation, such as:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELogging in\u003C/li\u003E\n\u003Cli\u003ERefreshing a session\u003C/li\u003E\n\u003Cli\u003ELogging out\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EEach flow:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EHas a defined lifecycle\u003C/li\u003E\n\u003Cli\u003ERuns through the orchestration pipeline\u003C/li\u003E\n\u003Cli\u003EProduces a controlled authentication outcome\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Instead of calling isolated APIs, you execute \u003Cstrong\u003Eflows\u003C/strong\u003E.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022why-flow-based\u0022\u003E\uD83D\uDD04 Why Flow-Based?\u003C/h2\u003E\n\u003Cp\u003ETraditional systems treat authentication as:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EA login endpoint\u003C/li\u003E\n\u003Cli\u003EA token generator\u003C/li\u003E\n\u003Cli\u003EA cookie setter\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 These approaches often lead to fragmented logic.\u003C/p\u003E\n\u003Cp\u003EUltimateAuth solves this by:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EStructuring authentication as flows\u003C/li\u003E\n\u003Cli\u003EEnforcing a consistent execution model\u003C/li\u003E\n\u003Cli\u003ECentralizing security decisions\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022what-happens-during-a-flow\u0022\u003E\uD83E\uDDE9 What Happens During a Flow?\u003C/h2\u003E\n\u003Cp\u003EEvery flow follows the same pattern:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003EFlow \u2192 Context \u2192 Orchestrator \u2192 Authority \u2192 Result\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cul\u003E\n\u003Cli\u003EThe \u003Cstrong\u003Eflow\u003C/strong\u003E defines the intent\u003C/li\u003E\n\u003Cli\u003EThe \u003Cstrong\u003Econtext\u003C/strong\u003E carries state\u003C/li\u003E\n\u003Cli\u003EThe \u003Cstrong\u003Eorchestrator\u003C/strong\u003E coordinates execution\u003C/li\u003E\n\u003Cli\u003EThe \u003Cstrong\u003Eauthority\u003C/strong\u003E enforces rules\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This ensures consistent and secure behavior across all operations.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022types-of-flows\u0022\u003E\uD83D\uDD10 Types of Flows\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth provides built-in flows for common scenarios:\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022login-flow\u0022\u003E\uD83D\uDD11 Login Flow\u003C/h3\u003E\n\u003Cp\u003EEstablishes authentication by:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EValidating credentials\u003C/li\u003E\n\u003Cli\u003ECreating session hierarchy (root, chain, session)\u003C/li\u003E\n\u003Cli\u003EIssuing tokens if required\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Ca href=\u0022./login-flow.md\u0022\u003ELearn more\u003C/a\u003E\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022refresh-flow\u0022\u003E\uD83D\uDD04 Refresh Flow\u003C/h3\u003E\n\u003Cp\u003EExtends an existing session:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERotates refresh tokens\u003C/li\u003E\n\u003Cli\u003EMaintains session continuity\u003C/li\u003E\n\u003Cli\u003EApplies sliding expiration\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Ca href=\u0022./refresh-flow.md\u0022\u003ELearn more\u003C/a\u003E\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022logout-flow\u0022\u003E\uD83D\uDEAA Logout Flow\u003C/h3\u003E\n\u003Cp\u003ETerminates authentication:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERevokes session(s)\u003C/li\u003E\n\u003Cli\u003EInvalidates tokens\u003C/li\u003E\n\u003Cli\u003ESupports device-level or global logout\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Ca href=\u0022./logout-flow.md\u0022\u003ELearn more\u003C/a\u003E\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022supporting-concepts\u0022\u003E\uD83E\uDDE0 Supporting Concepts\u003C/h2\u003E\n\u003Cp\u003EThese flows operate on top of deeper system models:\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022session-lifecycle\u0022\u003E\uD83E\uDDEC Session Lifecycle\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ERoot \u2192 Chain \u2192 Session hierarchy\u003C/li\u003E\n\u003Cli\u003EDevice-aware session structure\u003C/li\u003E\n\u003Cli\u003ELifecycle management and revocation\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Ca href=\u0022./session-lifecycle.md\u0022\u003ELearn more\u003C/a\u003E\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022token-behavior\u0022\u003E\uD83C\uDF9F Token Behavior\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EAccess token vs refresh token\u003C/li\u003E\n\u003Cli\u003EOpaque vs JWT\u003C/li\u003E\n\u003Cli\u003EMode-dependent behavior\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Ca href=\u0022./token-behavior.md\u0022\u003ELearn more\u003C/a\u003E\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022device-management\u0022\u003E\uD83D\uDCF1 Device Management\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EDevice binding\u003C/li\u003E\n\u003Cli\u003EMulti-device sessions\u003C/li\u003E\n\u003Cli\u003ESecurity implications\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Ca href=\u0022./device-management.md\u0022\u003ELearn more\u003C/a\u003E\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Cstrong\u003EAuthentication is not a single step\u003C/strong\u003E\u003Cbr /\u003E\n\uD83D\uDC49 \u003Cstrong\u003EIt is a controlled flow of state transitions\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022key-takeaways\u0022\u003E\uD83D\uDCCC Key Takeaways\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EAuthentication is executed as flows\u003C/li\u003E\n\u003Cli\u003EEach flow follows a consistent pipeline\u003C/li\u003E\n\u003Cli\u003ESessions and tokens are created as part of flows\u003C/li\u003E\n\u003Cli\u003ESecurity is enforced centrally\u003C/li\u003E\n\u003C/ul\u003E\n\u003Chr /\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003EStart with the most important flow:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Continue to \u003Cstrong\u003ELogin Flow\u003C/strong\u003E\u003C/p\u003E\n", + "Html": "\n\u003Cp\u003EAuthentication is often described as a collection of endpoints: one accepts a password, another renews a token, and another clears a cookie. Those endpoints are useful interfaces, but they are not the security model.\u003C/p\u003E\n\u003Cp\u003E\u003Cstrong\u003EUltimateAuth treats authentication as a set of controlled flows operating within an explicitly resolved security context.\u003C/strong\u003E A flow represents an intention\u2014establish access, continue it, or terminate it\u2014and the server determines which rules, state, and delivery behavior apply before executing that intention.\u003C/p\u003E\n\u003Cp\u003EThis distinction matters because the same request can have different legitimate outcomes depending on the tenant, authentication mode, client profile, device, current session, and server policy. A flow should not have to guess those values independently.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022a-flow-is-more-than-an-endpoint\u0022\u003EA flow is more than an endpoint\u003C/h2\u003E\n\u003Cp\u003EAn endpoint receives an HTTP request. A flow describes what the request is attempting to do and provides a consistent context for the operation.\u003C/p\u003E\n\u003Cp\u003EIn UltimateAuth, the HTTP boundary, context resolution, domain orchestration, authorization decisions, persistence, and response delivery have separate responsibilities. This makes authentication behavior easier to reason about without forcing every flow to use exactly the same internal implementation.\u003C/p\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003E\u003Cstrong\u003ECore principle:\u003C/strong\u003E Resolve the security context first. Make decisions against authoritative state. Commit only the changes permitted by the flow. Then deliver the outcome in the form appropriate for the client.\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022the-request-to-result-pipeline\u0022\u003EThe request-to-result pipeline\u003C/h2\u003E\n\u003Cp\u003EThe following sequence describes the common \u003Cstrong\u003EHTTP auth-flow boundary\u003C/strong\u003E. Individual flows have additional steps, and their internal order may differ.\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EIncoming HTTP request\n |\n v\nUltimateAuth Middleware Pipeline\n |\n \u002B-- TenantMiddleware\n | - Resolve tenant context\n | - Store tenant context in HttpContext\n |\n \u002B-- SessionResolutionMiddleware\n | - Resolve session identifier\n | - Build SessionContext (or anonymous context)\n |\n \u002B-- UserMiddleware\n | - Resolve user context through IUserAccessor\n |\n |-- ASP.NET Core Authentication and Authorization Middleware (Optional)\n |\n v\nRequest Context Prepared\n |\n v\nEndpoint metadata (AuthFlowType)\n |\n v\nAuthFlowEndpointFilter\n |\n v\nAuthFlow.BeginAsync(...)\n |\n v\nAuthFlowContextFactory\n - Read tenant, session, and user contexts\n - Resolve client profile and effective server options\n - Determine effective authentication mode\n - Resolve primary token kind and response delivery policy\n - Resolve device context and return URL\n - Validate existing session where applicable\n - Reject invalid or unresolved tenant\n |\n v\nAuthFlowContext (request-scoped)\n |\n v\nEndpoint handler and flow-specific validation\n |\n v\nFlow service / orchestrator\n |\n v\nSecurity and authorization decisions\n |\n v\nPermitted state transitions and optional token operations\n |\n v\nResponse delivery, optional redirect, and flow events\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EThis is a \u003Cstrong\u003Econceptual pipeline\u003C/strong\u003E, not a promise that every flow creates a session, issues a token, or dispatches the same events. Nor is there one universal authority method that all flows invoke identically.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022a-request-identifies-the-flow\u0022\u003E1. A request identifies the flow\u003C/h3\u003E\n\u003Cp\u003EUltimateAuth endpoints can carry \u003Ccode\u003EAuthFlowMetadata\u003C/code\u003E describing the \u003Ccode\u003EAuthFlowType\u003C/code\u003E. The \u003Ccode\u003EAuthFlowEndpointFilter\u003C/code\u003E reads that metadata and invokes \u003Ccode\u003EIAuthFlow.BeginAsync(...)\u003C/code\u003E before the endpoint handler executes.\u003C/p\u003E\n\u003Cp\u003EThis is an important boundary: the endpoint does not need to assemble its own authentication environment from scattered headers, cookies, and settings.\u003C/p\u003E\n\u003Cp\u003EThe filter initializes the flow context; it does \u003Cstrong\u003Enot\u003C/strong\u003E, by itself, validate a password, authorize every operation, or mutate the session store. Those responsibilities belong to the appropriate downstream components.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022the-server-constructs-authflowcontext\u0022\u003E2. The server constructs \u003Ccode\u003EAuthFlowContext\u003C/code\u003E\u003C/h3\u003E\n\u003Cp\u003E\u003Ccode\u003EAuthFlowContextFactory\u003C/code\u003E resolves the inputs that govern how the operation must behave. The resulting context includes:\u003C/p\u003E\n\u003Ctable\u003E\n\u003Cthead\u003E\n\u003Ctr\u003E\n\u003Cth\u003EContext value\u003C/th\u003E\n\u003Cth\u003EWhy it matters\u003C/th\u003E\n\u003C/tr\u003E\n\u003C/thead\u003E\n\u003Ctbody\u003E\n\u003Ctr\u003E\n\u003Ctd\u003E\u003Ccode\u003EFlowType\u003C/code\u003E\u003C/td\u003E\n\u003Ctd\u003EIdentifies the intended authentication operation.\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003E\u003Ccode\u003ETenant\u003C/code\u003E\u003C/td\u003E\n\u003Ctd\u003EEstablishes the tenant boundary for state and decisions.\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003E\u003Ccode\u003EClientProfile\u003C/code\u003E\u003C/td\u003E\n\u003Ctd\u003EDescribes the kind of client interacting with the server.\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003E\u003Ccode\u003EEffectiveMode\u003C/code\u003E\u003C/td\u003E\n\u003Ctd\u003EDetermines the authentication model actually in force for this request.\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003E\u003Ccode\u003EOptions\u003C/code\u003E\u003C/td\u003E\n\u003Ctd\u003ESupplies the effective server policy.\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003E\u003Ccode\u003EPrimaryTokenKind\u003C/code\u003E\u003C/td\u003E\n\u003Ctd\u003EIdentifies the primary credential/token kind for the effective mode.\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003E\u003Ccode\u003EDevice\u003C/code\u003E\u003C/td\u003E\n\u003Ctd\u003ECarries the resolved device context.\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003E\u003Ccode\u003ESession\u003C/code\u003E\u003C/td\u003E\n\u003Ctd\u003ECarries validated session security context when available.\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003E\u003Ccode\u003EIsAuthenticated\u003C/code\u003E / \u003Ccode\u003EUserKey\u003C/code\u003E\u003C/td\u003E\n\u003Ctd\u003ERepresent the resolved authentication identity for this request.\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003E\u003Ccode\u003EResponse\u003C/code\u003E\u003C/td\u003E\n\u003Ctd\u003EDescribes effective credential delivery and redirect behavior.\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003E\u003Ccode\u003EReturnUrlInfo\u003C/code\u003E\u003C/td\u003E\n\u003Ctd\u003ECarries parsed navigation intent for applicable browser flows.\u003C/td\u003E\n\u003C/tr\u003E\n\u003C/tbody\u003E\n\u003C/table\u003E\n\u003Cp\u003EThe factory rejects unresolved or invalid tenant context and disallowed authentication modes. When a session context is present, it invokes session validation. For session-authoritative modes such as \u003Ccode\u003EPureOpaque\u003C/code\u003E and \u003Ccode\u003EHybrid\u003C/code\u003E, that validation also affects the resolved authentication state.\u003C/p\u003E\n\u003Cp\u003E\u003Cstrong\u003EThe context is resolved, not blindly trusted.\u003C/strong\u003E Request-provided information may influence resolution, but the effective mode, policy, identity, and session validity are determined by server-side components.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022the-context-is-made-available-to-the-handler\u0022\u003E3. The context is made available to the handler\u003C/h3\u003E\n\u003Cp\u003E\u003Ccode\u003EAuthFlow.BeginAsync\u003C/code\u003E stores the created context through the request-scoped \u003Ccode\u003EAuthFlowContextAccessor\u003C/code\u003E. The endpoint handler can then access the same resolved context rather than reconstructing it.\u003C/p\u003E\n\u003Cp\u003EThis creates a shared vocabulary across the HTTP boundary: the handler and downstream services work with the same tenant, mode, device, and response policy.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022flow-specific-validation-and-orchestration-begin\u0022\u003E4. Flow-specific validation and orchestration begin\u003C/h3\u003E\n\u003Cp\u003EOnce the context exists, the endpoint handler reads and validates the operation-specific request. For example, login needs an identifier and secret; logout needs an applicable session or target scope; refresh needs valid renewal material.\u003C/p\u003E\n\u003Cp\u003EThe handler delegates the actual operation to the relevant flow service or orchestrator. Those components coordinate credential checks, session operations, token operations, and state transitions as appropriate.\u003C/p\u003E\n\u003Cp\u003EThere is no benefit in pretending these operations are identical. \u003Cstrong\u003ETheir shared architecture is the context and security boundary; their individual state transitions remain purpose-specific.\u003C/strong\u003E\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022decisions-are-evaluated-against-authoritative-state\u0022\u003E5. Decisions are evaluated against authoritative state\u003C/h3\u003E\n\u003Cp\u003EUltimateAuth distinguishes between what a client \u003Cem\u003Epresents\u003C/em\u003E and what the system \u003Cem\u003Eknows\u003C/em\u003E.\u003C/p\u003E\n\u003Cp\u003EA cookie, bearer token, device identifier, or user-supplied value is evidence or input\u2014not automatically the source of truth. The relevant authoritative data comes from server-managed user, credential, authentication-security, and session state, subject to the active authentication mode.\u003C/p\u003E\n\u003Cp\u003EFor session-backed flows, the hierarchy is:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EUser\n \u2514\u2500\u2500 Root\n \u2514\u2500\u2500 Chain (device-associated lifecycle)\n \u2514\u2500\u2500 Session (authentication instance)\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EThe root, chain, and session provide distinct scopes for continuity, validation, and invalidation. Tokens and cookies can represent access to that state, but should not be confused with the underlying lifecycle.\u003C/p\u003E\n\u003Cp\u003E\u003Cstrong\u003EMode distinction:\u003C/strong\u003E Do not assume that every mode has identical persistence or session-issuance behavior. In particular, \u003Ccode\u003EPureJwt\u003C/code\u003E is not a session-issuance mode in the current session issuer. The effective mode determines which state and proof are authoritative for a given operation.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022only-permitted-transitions-are-committed\u0022\u003E6. Only permitted transitions are committed\u003C/h3\u003E\n\u003Cp\u003EA successful authentication operation may create a session, rotate or touch existing state, revoke access, or leave persistent state unchanged. The actual effect depends on the flow and mode.\u003C/p\u003E\n\u003Cp\u003EFor example:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELogin verifies credentials and, in session-issuing modes, establishes a session before optionally issuing tokens.\u003C/li\u003E\n\u003Cli\u003ERefresh evaluates existing renewal authority and applies its configured continuation behavior; it is not simply a request to mint another token.\u003C/li\u003E\n\u003Cli\u003ELogout ends access at the relevant scope, while revoke represents irreversible invalidation of the revoked object or hierarchy scope.\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EWhere a session command passes through \u003Ccode\u003EUAuthSessionOrchestrator\u003C/code\u003E, \u003Ccode\u003EIAuthAuthority\u003C/code\u003E makes an allow, deny, or challenge decision before the session issuer executes that command. Other operations use their own relevant decision and access orchestration paths.\u003C/p\u003E\n\u003Cp\u003EThis separation helps prevent HTTP transport details from becoming security policy.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022the-outcome-is-delivered-according-to-policy\u0022\u003E7. The outcome is delivered according to policy\u003C/h3\u003E\n\u003Cp\u003EA successful domain operation and the HTTP response carrying its outcome are different concerns.\u003C/p\u003E\n\u003Cp\u003E\u003Ccode\u003EAuthResponseResolver\u003C/code\u003E derives the effective response configuration from the authentication mode, flow type, client profile, and server options. It binds applicable cookie settings and validates the delivery configuration before the endpoint performs the operation.\u003C/p\u003E\n\u003Cp\u003EThis means a flow can use the same underlying authentication semantics while producing a client-appropriate HTTP outcome\u2014for example, a response with managed cookies or a browser redirect when configured.\u003C/p\u003E\n\u003Cp\u003E\u003Cstrong\u003ECurrent implementation note:\u003C/strong\u003E \u003Ccode\u003ETokenResponseMode.Body\u003C/code\u003E credential delivery is explicitly unsupported by the response resolver. Do not assume that access or refresh credentials are returned in a JSON response body.\u003C/p\u003E\n\u003Cp\u003EA redirect is a navigation decision, \u003Cstrong\u003Enot\u003C/strong\u003E an authentication or revocation mechanism. It can return a browser to the application after login or logout; the underlying security operation must stand independently of whether the browser follows the redirect.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022why-authflowcontext-matters\u0022\u003EWhy \u003Ccode\u003EAuthFlowContext\u003C/code\u003E matters\u003C/h2\u003E\n\u003Cp\u003EWithout a shared flow context, individual endpoints tend to make their own assumptions: which tenant is active, whether a cookie represents an authenticated session, which token type is expected, whether a client should receive cookies, and where to navigate afterward.\u003C/p\u003E\n\u003Cp\u003EThose assumptions drift as applications add multiple clients, devices, tenants, or authentication modes.\u003C/p\u003E\n\u003Cp\u003EUltimateAuth instead resolves these questions at the flow boundary. That provides three practical benefits:\u003C/p\u003E\n\u003Col\u003E\n\u003Cli\u003E\u003Cstrong\u003EConsistency.\u003C/strong\u003E The same effective policy is available throughout the operation.\u003C/li\u003E\n\u003Cli\u003E\u003Cstrong\u003ESeparation of concerns.\u003C/strong\u003E Endpoint handlers handle HTTP; orchestrators coordinate security-relevant work; response components handle delivery.\u003C/li\u003E\n\u003Cli\u003E\u003Cstrong\u003EExtensibility.\u003C/strong\u003E New client profiles and flow behaviors can integrate with established boundaries instead of duplicating authentication plumbing.\u003C/li\u003E\n\u003C/ol\u003E\n\u003Cp\u003EA context is not an authorization bypass. Possessing an \u003Ccode\u003EAuthFlowContext\u003C/code\u003E does not imply that the requested operation has been approved.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022source-of-truth-proof-versus-state\u0022\u003ESource of truth: proof versus state\u003C/h2\u003E\n\u003Cp\u003EA useful mental model is to distinguish \u003Cstrong\u003Eproof\u003C/strong\u003E, \u003Cstrong\u003Epolicy\u003C/strong\u003E, and \u003Cstrong\u003Estate\u003C/strong\u003E:\u003C/p\u003E\n\u003Ctable\u003E\n\u003Cthead\u003E\n\u003Ctr\u003E\n\u003Cth\u003EConcept\u003C/th\u003E\n\u003Cth\u003EExamples\u003C/th\u003E\n\u003Cth\u003EResponsibility\u003C/th\u003E\n\u003C/tr\u003E\n\u003C/thead\u003E\n\u003Ctbody\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EProof\u003C/td\u003E\n\u003Ctd\u003EPassword, session identifier, access token, refresh token\u003C/td\u003E\n\u003Ctd\u003EPresents evidence for an operation.\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EPolicy\u003C/td\u003E\n\u003Ctd\u003EEffective mode, server options, authority decisions\u003C/td\u003E\n\u003Ctd\u003EDetermines what is permitted.\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EState\u003C/td\u003E\n\u003Ctd\u003EUser eligibility, credential records, security state, session root/chain/session\u003C/td\u003E\n\u003Ctd\u003EDetermines the authoritative lifecycle and validity where applicable.\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EDelivery\u003C/td\u003E\n\u003Ctd\u003ECookies, supported response modes, redirect\u003C/td\u003E\n\u003Ctd\u003ECommunicates the result to the client.\u003C/td\u003E\n\u003C/tr\u003E\n\u003C/tbody\u003E\n\u003C/table\u003E\n\u003Cp\u003EA valid-looking token is not, by itself, permission to ignore revocation, tenant boundaries, session state, or mode-specific validation. Conversely, not every authentication mode is built around a persisted session hierarchy. The authoritative checks must match the configured model.\u003C/p\u003E\n\u003Cp\u003EThis distinction is central to UltimateAuth\u0027s vision: \u003Cstrong\u003Eauthentication is not the possession of a string; it is the outcome of a controlled security process.\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022built-in-flows-at-a-glance\u0022\u003EBuilt-in flows at a glance\u003C/h2\u003E\n\u003Cp\u003EThe following pages explain each operation in detail. This overview focuses on their roles within the shared model.\u003C/p\u003E\n\u003Ctable\u003E\n\u003Cthead\u003E\n\u003Ctr\u003E\n\u003Cth\u003EFlow\u003C/th\u003E\n\u003Cth\u003EPurpose\u003C/th\u003E\n\u003Cth\u003EPractical outcome\u003C/th\u003E\n\u003Cth\u003EGuide\u003C/th\u003E\n\u003C/tr\u003E\n\u003C/thead\u003E\n\u003Ctbody\u003E\n\u003Ctr\u003E\n\u003Ctd\u003E\u003Cstrong\u003ELogin\u003C/strong\u003E\u003C/td\u003E\n\u003Ctd\u003EEstablish authenticated access from acceptable credentials.\u003C/td\u003E\n\u003Ctd\u003ECreates a valid authentication session in session-issuing modes; may issue tokens according to policy.\u003C/td\u003E\n\u003Ctd\u003E\u003Ca href=\u0022./login-flow.md\u0022\u003ELogin Flow\u003C/a\u003E\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003E\u003Cstrong\u003ERefresh\u003C/strong\u003E\u003C/td\u003E\n\u003Ctd\u003EContinue an existing authentication relationship using renewal authority.\u003C/td\u003E\n\u003Ctd\u003EMaintains or renews access according to mode, validation, and configured refresh behavior.\u003C/td\u003E\n\u003Ctd\u003E\u003Ca href=\u0022./refresh-flow.md\u0022\u003ERefresh Flow\u003C/a\u003E\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003E\u003Cstrong\u003ELogout\u003C/strong\u003E\u003C/td\u003E\n\u003Ctd\u003EEnd access at the applicable session or device scope.\u003C/td\u003E\n\u003Ctd\u003ETerminates authentication access and handles configured client cleanup/navigation.\u003C/td\u003E\n\u003Ctd\u003E\u003Ca href=\u0022./logout-flow.md\u0022\u003ELogout Flow\u003C/a\u003E\u003C/td\u003E\n\u003C/tr\u003E\n\u003C/tbody\u003E\n\u003C/table\u003E\n\u003Cp\u003EThese flows are related but not interchangeable. Login establishes access; refresh continues it; logout terminates it. Revocation operations provide stronger invalidation controls for the relevant security scope.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022concepts-shared-by-the-flows\u0022\u003EConcepts shared by the flows\u003C/h2\u003E\n\u003Ctable\u003E\n\u003Cthead\u003E\n\u003Ctr\u003E\n\u003Cth\u003EConcept\u003C/th\u003E\n\u003Cth\u003EWhat it contributes\u003C/th\u003E\n\u003Cth\u003EGuide\u003C/th\u003E\n\u003C/tr\u003E\n\u003C/thead\u003E\n\u003Ctbody\u003E\n\u003Ctr\u003E\n\u003Ctd\u003E\u003Cstrong\u003ESession lifecycle\u003C/strong\u003E\u003C/td\u003E\n\u003Ctd\u003ERoot, chain, and session relationships; creation, continuity, expiry, and invalidation.\u003C/td\u003E\n\u003Ctd\u003E\u003Ca href=\u0022./session-lifecycle.md\u0022\u003ESession Lifecycle\u003C/a\u003E\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003E\u003Cstrong\u003EToken behavior\u003C/strong\u003E\u003C/td\u003E\n\u003Ctd\u003EAccess and refresh credentials, validation, renewal, and mode-specific behavior.\u003C/td\u003E\n\u003Ctd\u003E\u003Ca href=\u0022./token-behavior.md\u0022\u003EToken Behavior\u003C/a\u003E\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003E\u003Cstrong\u003EDevice management\u003C/strong\u003E\u003C/td\u003E\n\u003Ctd\u003EDevice-associated chains, multi-device access, and targeted termination.\u003C/td\u003E\n\u003Ctd\u003E\u003Ca href=\u0022./device-management.md\u0022\u003EDevice Management\u003C/a\u003E\u003C/td\u003E\n\u003C/tr\u003E\n\u003C/tbody\u003E\n\u003C/table\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022what-this-design-enables-in-real-applications\u0022\u003EWhat this design enables in real applications\u003C/h2\u003E\n\u003Cp\u003EConsider an application with a browser front end, a mobile client, and an administrative interface. They may share the same user identity system, but they do not necessarily have the same credential delivery needs or session behavior.\u003C/p\u003E\n\u003Cp\u003EA flow-driven architecture lets the server establish the applicable context first and then run the requested operation under that context. Developers can reason about \u003Cstrong\u003Ewhat authentication action is taking place\u003C/strong\u003E separately from \u003Cstrong\u003Ehow the resulting credentials or navigation are delivered\u003C/strong\u003E.\u003C/p\u003E\n\u003Cp\u003EIt also makes security-sensitive behavior easier to locate: tenant and mode resolution occur at the boundary, domain validation happens in the flow, session operations use dedicated infrastructure, and response policy is resolved independently.\u003C/p\u003E\n\u003Cp\u003EThis is the larger goal of UltimateAuth\u0027s unified model\u2014not to make every authentication operation identical, but to make their differences explicit, controlled, and composable.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022key-takeaways\u0022\u003EKey takeaways\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u003Cstrong\u003EAn auth flow is a controlled operation\u003C/strong\u003E, not merely an HTTP endpoint.\u003C/li\u003E\n\u003Cli\u003E\u003Cstrong\u003E\u003Ccode\u003EAuthFlowEndpointFilter\u003C/code\u003E initializes the request context\u003C/strong\u003E for endpoints carrying flow metadata.\u003C/li\u003E\n\u003Cli\u003E\u003Cstrong\u003E\u003Ccode\u003EAuthFlowContext\u003C/code\u003E resolves tenant, mode, client profile, device, identity/session information, and response policy\u003C/strong\u003E before the handler executes.\u003C/li\u003E\n\u003Cli\u003E\u003Cstrong\u003EProof is not the source of truth.\u003C/strong\u003E Validation depends on authoritative state and the effective authentication mode.\u003C/li\u003E\n\u003Cli\u003E\u003Cstrong\u003EAuthority and orchestration are distinct responsibilities.\u003C/strong\u003E The exact checks and state transitions vary by flow.\u003C/li\u003E\n\u003Cli\u003E\u003Cstrong\u003ECredential delivery and redirects are outcomes\u003C/strong\u003E, not substitutes for security decisions.\u003C/li\u003E\n\u003Cli\u003E\u003Cstrong\u003ELogin, refresh, and logout share a model without sharing identical execution paths.\u003C/strong\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022next-step\u0022\u003ENext step\u003C/h2\u003E\n\u003Cp\u003EStart with \u003Ca href=\u0022./login-flow.md\u0022\u003ELogin Flow\u003C/a\u003E to see how UltimateAuth turns validated credentials into controlled authenticated access.\u003C/p\u003E\n", "Headings": [ { - "Id": "what-is-an-auth-flow", - "Text": "\uD83E\uDDE0 What is an Auth Flow?", + "Id": "a-flow-is-more-than-an-endpoint", + "Text": "A flow is more than an endpoint", "Level": 0 }, { - "Id": "why-flow-based", - "Text": "\uD83D\uDD04 Why Flow-Based?", + "Id": "the-request-to-result-pipeline", + "Text": "The request-to-result pipeline", "Level": 0 }, { - "Id": "what-happens-during-a-flow", - "Text": "\uD83E\uDDE9 What Happens During a Flow?", - "Level": 0 + "Id": "a-request-identifies-the-flow", + "Text": "1. A request identifies the flow", + "Level": 1 }, { - "Id": "types-of-flows", - "Text": "\uD83D\uDD10 Types of Flows", - "Level": 0 + "Id": "the-server-constructs-authflowcontext", + "Text": "2. The server constructs AuthFlowContext", + "Level": 1 }, { - "Id": "login-flow", - "Text": "\uD83D\uDD11 Login Flow", + "Id": "the-context-is-made-available-to-the-handler", + "Text": "3. The context is made available to the handler", "Level": 1 }, { - "Id": "refresh-flow", - "Text": "\uD83D\uDD04 Refresh Flow", + "Id": "flow-specific-validation-and-orchestration-begin", + "Text": "4. Flow-specific validation and orchestration begin", "Level": 1 }, { - "Id": "logout-flow", - "Text": "\uD83D\uDEAA Logout Flow", + "Id": "decisions-are-evaluated-against-authoritative-state", + "Text": "5. Decisions are evaluated against authoritative state", "Level": 1 }, { - "Id": "supporting-concepts", - "Text": "\uD83E\uDDE0 Supporting Concepts", - "Level": 0 + "Id": "only-permitted-transitions-are-committed", + "Text": "6. Only permitted transitions are committed", + "Level": 1 }, { - "Id": "session-lifecycle", - "Text": "\uD83E\uDDEC Session Lifecycle", + "Id": "the-outcome-is-delivered-according-to-policy", + "Text": "7. The outcome is delivered according to policy", "Level": 1 }, { - "Id": "token-behavior", - "Text": "\uD83C\uDF9F Token Behavior", - "Level": 1 + "Id": "why-authflowcontext-matters", + "Text": "Why AuthFlowContext matters", + "Level": 0 }, { - "Id": "device-management", - "Text": "\uD83D\uDCF1 Device Management", - "Level": 1 + "Id": "source-of-truth-proof-versus-state", + "Text": "Source of truth: proof versus state", + "Level": 0 + }, + { + "Id": "built-in-flows-at-a-glance", + "Text": "Built-in flows at a glance", + "Level": 0 + }, + { + "Id": "concepts-shared-by-the-flows", + "Text": "Concepts shared by the flows", + "Level": 0 }, { - "Id": "mental-model", - "Text": "\uD83E\uDDE0 Mental Model", + "Id": "what-this-design-enables-in-real-applications", + "Text": "What this design enables in real applications", "Level": 0 }, { "Id": "key-takeaways", - "Text": "\uD83D\uDCCC Key Takeaways", + "Text": "Key takeaways", "Level": 0 }, { "Id": "next-step", - "Text": "\u27A1\uFE0F Next Step", + "Text": "Next step", "Level": 0 } ] diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/login-flow.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/login-flow.json index 497b1331..f9f627e5 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/login-flow.json +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/login-flow.json @@ -1,101 +1,96 @@ { "Slug": "auth-flows/login-flow", "Title": "Login", - "Html": "\n\u003Cp\u003EThe login flow in UltimateAuth is not just credential validation.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 It is a \u003Cstrong\u003Econtrolled session establishment process\u003C/strong\u003E.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022what-is-login\u0022\u003E\uD83E\uDDE0 What is Login?\u003C/h2\u003E\n\u003Cp\u003EIn traditional systems:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EValidate credentials\u003C/li\u003E\n\u003Cli\u003EIssue a token\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EIn UltimateAuth:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Login creates a \u003Cstrong\u003Esession hierarchy\u003C/strong\u003E\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003ERoot \u2192 Chain \u2192 Session\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003ELogin does not create a token\u003Cbr /\u003E\n\u2192 It creates a session\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Cp\u003ETokens are optional outputs derived from the session.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022step-by-step-execution\u0022\u003E\uD83D\uDD04 Step-by-Step Execution\u003C/h2\u003E\n\u003Cp\u003EThe login flow follows a structured pipeline:\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022identifier-resolution\u0022\u003E1\uFE0F\u20E3 Identifier Resolution\u003C/h3\u003E\n\u003Cp\u003EThe system resolves the user identity:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EUsername / email / phone \u2192 \u003Ccode\u003EUserKey\u003C/code\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022user-security-state\u0022\u003E2\uFE0F\u20E3 User \u0026amp; Security State\u003C/h3\u003E\n\u003Cp\u003EThe system loads:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EUser existence\u003C/li\u003E\n\u003Cli\u003EAccount state\u003C/li\u003E\n\u003Cli\u003EFactor (credential) state\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EChecks include:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EIs the account locked?\u003C/li\u003E\n\u003Cli\u003EIs reauthentication required?\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022credential-validation\u0022\u003E3\uFE0F\u20E3 Credential Validation\u003C/h3\u003E\n\u003Cp\u003ECredentials are validated using providers:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EPassword\u003C/li\u003E\n\u003Cli\u003E(Extensible: OTP, external providers, etc.)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022authority-decision\u0022\u003E4\uFE0F\u20E3 Authority Decision\u003C/h3\u003E\n\u003Cp\u003EThe \u003Cstrong\u003ELoginAuthority\u003C/strong\u003E evaluates the attempt.\u003C/p\u003E\n\u003Cp\u003EPossible outcomes:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u2705 Allow\u003C/li\u003E\n\u003Cli\u003E\u274C Deny\u003C/li\u003E\n\u003Cli\u003E\u26A0\uFE0F Challenge (e.g. MFA)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 No session is created before this decision.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022device-chain-resolution\u0022\u003E5\uFE0F\u20E3 Device \u0026amp; Chain Resolution\u003C/h3\u003E\n\u003Cp\u003EThe system checks if the device is known:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EExisting device \u2192 reuse chain\u003C/li\u003E\n\u003Cli\u003ENew device \u2192 create new chain\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 A \u003Cstrong\u003EChain represents a device\u003C/strong\u003E\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022session-creation\u0022\u003E6\uFE0F\u20E3 Session Creation\u003C/h3\u003E\n\u003Cp\u003EA new session is issued:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELinked to user\u003C/li\u003E\n\u003Cli\u003ELinked to device (chain)\u003C/li\u003E\n\u003Cli\u003EBound to tenant\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003ESession hierarchy:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003EUser \u2192 Root \u2192 Chain \u2192 Session\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022token-issuance-optional\u0022\u003E7\uFE0F\u20E3 Token Issuance (Optional)\u003C/h3\u003E\n\u003Cp\u003EDepending on the mode and request:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EAccess token may be issued\u003C/li\u003E\n\u003Cli\u003ERefresh token may be issued\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Tokens are derived from the session\u003Cbr /\u003E\n\uD83D\uDC49 Not the source of truth\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022event-dispatch\u0022\u003E8\uFE0F\u20E3 Event Dispatch\u003C/h3\u003E\n\u003Cp\u003EThe system emits:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELogin events\u003C/li\u003E\n\u003Cli\u003EAudit information\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022what-gets-created\u0022\u003E\uD83E\uDDE9 What Gets Created?\u003C/h2\u003E\n\u003Cp\u003EA successful login creates:\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022root\u0022\u003E\uD83D\uDD39 Root\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EOne per user\u003C/li\u003E\n\u003Cli\u003ERepresents global authentication state\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022chain\u0022\u003E\uD83D\uDD39 Chain\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EOne per device\u003C/li\u003E\n\u003Cli\u003EManages device lifecycle\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022session\u0022\u003E\uD83D\uDD39 Session\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EIndividual authentication instance\u003C/li\u003E\n\u003Cli\u003ERepresents a single login\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022device-awareness\u0022\u003E\uD83D\uDCF1 Device Awareness\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth is device-aware by design:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EEach device gets its own chain\u003C/li\u003E\n\u003Cli\u003ESessions are grouped by device\u003C/li\u003E\n\u003Cli\u003ELogout can target device or all sessions\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022security-considerations\u0022\u003E\uD83D\uDD10 Security Considerations\u003C/h2\u003E\n\u003Cp\u003EThe login flow includes built-in protections:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EAccount lockout\u003C/li\u003E\n\u003Cli\u003EFailed attempt tracking\u003C/li\u003E\n\u003Cli\u003EDevice binding\u003C/li\u003E\n\u003Cli\u003ESecurity version validation\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Security decisions are centralized in the Authority\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Login = session creation\u003Cbr /\u003E\n\uD83D\uDC49 Not token issuance\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022key-takeaways\u0022\u003E\uD83D\uDCCC Key Takeaways\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003ELogin is a flow, not a function\u003C/li\u003E\n\u003Cli\u003EAuthority decides before any state change\u003C/li\u003E\n\u003Cli\u003ESessions are the source of truth\u003C/li\u003E\n\u003Cli\u003ETokens are optional representations\u003C/li\u003E\n\u003Cli\u003EDevice context is always considered\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003EContinue to \u003Cstrong\u003ERefresh Flow\u003C/strong\u003E\u003C/p\u003E\n", + "Html": "\n\u003Cp\u003ELogging in is often described as a simple exchange: verify a password, issue a token, and consider the user authenticated. That description leaves out the part that matters most over time: \u003Cstrong\u003Ewhat exactly has been established and how can it be managed or revoked?\u003C/strong\u003E\u003C/p\u003E\n\u003Cp\u003EUltimateAuth treats login as a \u003Cstrong\u003Econtrolled session-establishment flow\u003C/strong\u003E. Credential verification is essential, but it is only one stage. A successful login connects an authenticated user to a tenant, a device context, a session hierarchy, and the security state that governs future requests.\u003C/p\u003E\n\u003Cp\u003EThe central idea is straightforward:\u003C/p\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003E\u003Cstrong\u003ELogin establishes a session. Tokens, when enabled, are credentials issued in connection with that session\u2014not the session itself.\u003C/strong\u003E\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Cp\u003EThis distinction shapes how UltimateAuth handles device awareness, session continuity, token issuance, revocation, and authentication policy.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022from-a-login-response-to-an-authentication-lifecycle\u0022\u003EFrom a login response to an authentication lifecycle\u003C/h2\u003E\n\u003Cp\u003EIn a token-centric design, login is frequently treated as the moment a token is minted. The token then becomes the primary artifact developers reason about. This works for simple scenarios, but it can make questions about devices, concurrent logins, global sign-out, and session continuity harder to express consistently.\u003C/p\u003E\n\u003Cp\u003EUltimateAuth starts with the authentication lifecycle instead. It separates four responsibilities:\u003C/p\u003E\n\u003Col\u003E\n\u003Cli\u003E\u003Cstrong\u003EIdentity resolution:\u003C/strong\u003E determine which user is attempting to authenticate.\u003C/li\u003E\n\u003Cli\u003E\u003Cstrong\u003EAuthentication and security evaluation:\u003C/strong\u003E verify the credential and decide whether the attempt is allowed.\u003C/li\u003E\n\u003Cli\u003E\u003Cstrong\u003ESession establishment:\u003C/strong\u003E create a managed authentication instance in the appropriate device and user context.\u003C/li\u003E\n\u003Cli\u003E\u003Cstrong\u003ECredential delivery:\u003C/strong\u003E optionally issue access and refresh tokens and deliver the applicable credentials according to the server mode and client profile.\u003C/li\u003E\n\u003C/ol\u003E\n\u003Cp\u003EThese responsibilities are related, but they are not interchangeable. In particular, a valid password does not by itself create a session, and a token is not the sole source of authentication state.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022the-session-hierarchy\u0022\u003EThe session hierarchy\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth models authentication through a hierarchy:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EUser\n\u2514\u2500\u2500 Root\n \u251C\u2500\u2500 Chain (Device A)\n \u2502 \u251C\u2500\u2500 Session 1\n \u2502 \u2514\u2500\u2500 Session 2\n \u2514\u2500\u2500 Chain (Device B)\n \u2514\u2500\u2500 Session 3\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EEach level answers a different question:\u003C/p\u003E\n\u003Ctable\u003E\n\u003Cthead\u003E\n\u003Ctr\u003E\n\u003Cth\u003ELevel\u003C/th\u003E\n\u003Cth\u003EResponsibility\u003C/th\u003E\n\u003Cth\u003EWhy it matters\u003C/th\u003E\n\u003C/tr\u003E\n\u003C/thead\u003E\n\u003Ctbody\u003E\n\u003Ctr\u003E\n\u003Ctd\u003E\u003Cstrong\u003EUser\u003C/strong\u003E\u003C/td\u003E\n\u003Ctd\u003EThe identity being authenticated\u003C/td\u003E\n\u003Ctd\u003ELogin identifiers can change without changing the user\u0027s internal identity.\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003E\u003Cstrong\u003ERoot\u003C/strong\u003E\u003C/td\u003E\n\u003Ctd\u003EThe user-level session hierarchy\u003C/td\u003E\n\u003Ctd\u003EProvides a shared context for authentication across devices.\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003E\u003Cstrong\u003EChain\u003C/strong\u003E\u003C/td\u003E\n\u003Ctd\u003EA device-associated sequence of sessions\u003C/td\u003E\n\u003Ctd\u003EAllows the system to reason about continuity and device-scoped authentication.\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003E\u003Cstrong\u003ESession\u003C/strong\u003E\u003C/td\u003E\n\u003Ctd\u003EA particular issued authentication instance\u003C/td\u003E\n\u003Ctd\u003EProvides the concrete session identity to which authentication credentials can be related.\u003C/td\u003E\n\u003C/tr\u003E\n\u003C/tbody\u003E\n\u003C/table\u003E\n\u003Cp\u003EThe hierarchy is a \u003Cstrong\u003Elogical model\u003C/strong\u003E, not a claim that every login inserts a new root or chain. A login can continue an existing active chain for the same user and device. The session subsystem determines how the resulting session is issued and persisted.\u003C/p\u003E\n\u003Cp\u003EA chain is best understood as a \u003Cstrong\u003Edevice-associated authentication lifecycle\u003C/strong\u003E, rather than as the physical device itself. The quality and stability of device identification depend on the configured device context.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022how-a-login-proceeds\u0022\u003EHow a login proceeds\u003C/h2\u003E\n\u003Cp\u003EThe standard password login flow can be viewed as the following pipeline:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EClient request\n \u2502\n \u25BC\nResolve authentication flow context\n \u2502 Tenant \u00B7 device \u00B7 effective mode \u00B7 client profile\n \u25BC\nResolve identifier \u2192 UserKey\n \u2502\n \u25BC\nCheck user eligibility and security state\n \u2502\n \u25BC\nValidate credential using registered providers\n \u2502\n \u25BC\nFind an eligible active chain for this user/device\n \u2502\n \u25BC\nLoginAuthority decision\n \u251C\u2500\u2500 Deny \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u25BA Failure / lockout handling\n \u251C\u2500\u2500 Challenge \u2500\u2500\u2500\u2500\u25BA Continuation required\n \u2514\u2500\u2500 Allow\n \u2502\n \u25BC\n Establish session\n \u2502\n \u25BC\n Optionally issue access/refresh tokens\n \u2502\n \u25BC\n Dispatch login event\n \u2502\n \u25BC\n Deliver credentials / redirect or OK\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EThis diagram shows the responsibilities in the current flow. Some security-state operations, such as recording failed attempts, happen as part of processing a rejected login. Therefore, \u003Cstrong\u003Ethe authority decision precedes session issuance, not every possible state mutation\u003C/strong\u003E.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022resolve-the-authentication-context\u0022\u003E1. Resolve the authentication context\u003C/h3\u003E\n\u003Cp\u003EBefore credential validation, UltimateAuth operates within an \u003Ccode\u003EAuthFlowContext\u003C/code\u003E. This context provides the effective tenant, device information, authentication mode, client profile, and applicable server options.\u003C/p\u003E\n\u003Cp\u003EThat context matters because the same logical login operation can have different delivery and session requirements depending on how the application is hosted and which kind of client is connecting. Login should not need to be reimplemented for each application style.\u003C/p\u003E\n\u003Cp\u003EA resolvable device ID is required by the current login orchestrator. If device identification cannot be resolved, login cannot proceed to normal session establishment.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022resolve-the-login-identifier\u0022\u003E2. Resolve the login identifier\u003C/h3\u003E\n\u003Cp\u003EA user may identify themselves with a username, email address, or another identifier supported by the configured resolver. UltimateAuth translates that external identifier into an internal \u003Ccode\u003EUserKey\u003C/code\u003E through \u003Ccode\u003EILoginIdentifierResolver\u003C/code\u003E.\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EUser-provided identifier\n \u2502\n \u25BC\nILoginIdentifierResolver\n \u2502\n \u25BC\n UserKey\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EThe internal user key, not the original text entered into the login form, becomes the stable reference used by the rest of the authentication flow. This helps keep \u003Cstrong\u003Ehow a user signs in\u003C/strong\u003E separate from \u003Cstrong\u003Ewho that user is\u003C/strong\u003E.\u003C/p\u003E\n\u003Cp\u003EThe identifiers actually accepted depend on the configured resolver and user data; support for a particular identifier format should not be assumed without configuration.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022check-eligibility-and-security-state\u0022\u003E3. Check eligibility and security state\u003C/h3\u003E\n\u003Cp\u003EOnce the user is resolved, the login orchestrator retrieves their runtime state. The current implementation requires the user to exist, be permitted to authenticate (\u003Ccode\u003ECanAuthenticate\u003C/code\u003E), and not be deleted (\u003Ccode\u003EIsDeleted\u003C/code\u003E).\u003C/p\u003E\n\u003Cp\u003EIt also examines two security scopes:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u003Cstrong\u003EAccount state:\u003C/strong\u003E security conditions associated with the account as a whole.\u003C/li\u003E\n\u003Cli\u003E\u003Cstrong\u003EFactor state:\u003C/strong\u003E security conditions associated with the credential type being used.\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EAn existing account or factor lock can stop the attempt before credential validation proceeds. Failed attempts, lockout duration, failure windows, and lock extension are governed by login options.\u003C/p\u003E\n\u003Cp\u003EThis separation is intentional: a credential can be correct while the account is not currently eligible to authenticate.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022validate-the-credential\u0022\u003E4. Validate the credential\u003C/h3\u003E\n\u003Cp\u003EUltimateAuth delegates credential verification to registered credential providers rather than embedding password comparison directly into the login orchestrator.\u003C/p\u003E\n\u003Cp\u003EFor the current standard HTTP login endpoint, the credential type is \u003Cstrong\u003EPassword\u003C/strong\u003E. The orchestrator also rejects unsupported credential types in this path. The provider architecture is extensible, but that extensibility should not be confused with a promise that OTP, passkeys, or external identity providers all use this exact password-login endpoint in v0.1.0.\u003C/p\u003E\n\u003Cp\u003EFor eligible credentials, the orchestrator checks that the credential matches the requested type, has not been deleted, is usable under its security rules, and validates the submitted secret through its provider.\u003C/p\u003E\n\u003Cp\u003EThis gives the login flow a stable orchestration model while keeping credential-specific verification behind dedicated abstractions.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022resolve-device-associated-session-continuity\u0022\u003E5. Resolve device-associated session continuity\u003C/h3\u003E\n\u003Cp\u003EBefore the authority decision, the orchestrator looks for a session chain associated with the resolved user and device. If a chain exists and its state is active under the configured idle timeout, its \u003Ccode\u003EChainId\u003C/code\u003E can be carried into session issuance.\u003C/p\u003E\n\u003Cp\u003EConceptually:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003ESame user \u002B known device \u002B active chain\n \u2502\n \u25BC\n Continue with that ChainId\n\nNo eligible active chain\n \u2502\n \u25BC\n Session subsystem establishes\n the appropriate new chain\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EThis enables device-aware continuity without reducing a login to \u201Cissue another unrelated token.\u201D The session subsystem remains responsible for the exact creation and persistence behavior.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022make-the-authority-decision\u0022\u003E6. Make the authority decision\u003C/h3\u003E\n\u003Cp\u003E\u003Ccode\u003ELoginAuthority\u003C/code\u003E evaluates the login attempt and returns one of three outcomes:\u003C/p\u003E\n\u003Ctable\u003E\n\u003Cthead\u003E\n\u003Ctr\u003E\n\u003Cth\u003EDecision\u003C/th\u003E\n\u003Cth\u003EMeaning\u003C/th\u003E\n\u003Cth\u003ESession issued?\u003C/th\u003E\n\u003C/tr\u003E\n\u003C/thead\u003E\n\u003Ctbody\u003E\n\u003Ctr\u003E\n\u003Ctd\u003E\u003Cstrong\u003EAllow\u003C/strong\u003E\u003C/td\u003E\n\u003Ctd\u003EThe attempt may proceed to session establishment.\u003C/td\u003E\n\u003Ctd\u003EYes, if the remaining issuance steps succeed.\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003E\u003Cstrong\u003EDeny\u003C/strong\u003E\u003C/td\u003E\n\u003Ctd\u003EAuthentication is rejected.\u003C/td\u003E\n\u003Ctd\u003ENo.\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003E\u003Cstrong\u003EChallenge\u003C/strong\u003E\u003C/td\u003E\n\u003Ctd\u003EAn additional step or continuation is required.\u003C/td\u003E\n\u003Ctd\u003ENo, not from this login attempt.\u003C/td\u003E\n\u003C/tr\u003E\n\u003C/tbody\u003E\n\u003C/table\u003E\n\u003Cp\u003EThe current default authority considers user existence, lockout, reauthentication requirements, and credential validity. The broader flow also performs eligibility and lockout checks before invoking the authority.\u003C/p\u003E\n\u003Cp\u003EA challenge is represented through a \u003Ccode\u003ELoginContinuation\u003C/code\u003E; the current orchestrator uses an MFA continuation type for its challenge branch. This represents a requirement to continue authentication, \u003Cstrong\u003Enot proof that an MFA exchange has already completed\u003C/strong\u003E.\u003C/p\u003E\n\u003Cp\u003EFor rejected attempts, the orchestrator may record a failed factor attempt and return lockout or remaining-attempt information when the corresponding disclosure option is enabled. Applications should treat these details as policy-controlled rather than universally available.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022establish-the-session\u0022\u003E7. Establish the session\u003C/h3\u003E\n\u003Cp\u003EOnly an allowed, successfully validated \u003Cstrong\u003Ecommit\u003C/strong\u003E login proceeds to session issuance.\u003C/p\u003E\n\u003Cp\u003EThe orchestrator first records a successful factor authentication (unless explicitly suppressed by an internal execution option), then obtains user claims through \u003Ccode\u003EIUserClaimsProvider\u003C/code\u003E.\u003C/p\u003E\n\u003Cp\u003EIt constructs a \u003Ccode\u003ESessionIssuanceContext\u003C/code\u003E containing the tenant, user key, device, claims, effective mode, session options, and any eligible existing chain ID. The context is passed to \u003Ccode\u003EISessionOrchestrator\u003C/code\u003E using a login-session creation command.\u003C/p\u003E\n\u003Cp\u003EThis is the decisive transition:\u003C/p\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003EThe user has not merely supplied a valid secret; the system has established a managed authentication session.\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Cp\u003EThe resulting session has its own identity. That identity is available to the later token-issuance and response-delivery stages.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022issue-tokens-when-permitted\u0022\u003E8. Issue tokens when permitted\u003C/h3\u003E\n\u003Cp\u003EAfter session issuance, access and refresh tokens may be requested from the token issuer. The current orchestrator issues them only when \u003Ccode\u003ELoginRequest.RequestTokens\u003C/code\u003E is true.\u003C/p\u003E\n\u003Cp\u003EFor the HTTP login endpoint, however, the server derives that setting from \u003Ccode\u003EAuthFlowContext.AllowsTokenIssuance\u003C/code\u003E. A client cannot force token issuance merely by placing \u003Ccode\u003ERequestTokens: true\u003C/code\u003E in its submitted payload.\u003C/p\u003E\n\u003Cp\u003EWhen issuance is enabled:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EThe access token is issued in the context of the authenticated user and issued session.\u003C/li\u003E\n\u003Cli\u003ERefresh-token issuance is requested with persistence enabled.\u003C/li\u003E\n\u003Cli\u003EBoth issuance paths operate with the tenant, user key, session ID, chain ID, and claims context.\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EDepending on the effective mode and issuer behavior, an individual token type may be absent. \u003Cstrong\u003ESuccessful login and token issuance are deliberately separate concepts.\u003C/strong\u003E\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022dispatch-the-login-event-and-deliver-the-response\u0022\u003E9. Dispatch the login event and deliver the response\u003C/h3\u003E\n\u003Cp\u003EFollowing successful session and optional token issuance, UltimateAuth dispatches a \u003Ccode\u003EUserLoggedInContext\u003C/code\u003E event containing the tenant, user key, time, device, and session ID.\u003C/p\u003E\n\u003Cp\u003EThe HTTP endpoint then delegates credential delivery to \u003Ccode\u003EICredentialResponseWriter\u003C/code\u003E. Session ID, access token, and refresh token are handled as distinct grant kinds. Delivery is resolved using the effective mode, authentication flow, client profile, and server configuration.\u003C/p\u003E\n\u003Cp\u003EDepending on configuration, the HTTP response may complete with \u003Ccode\u003E200 OK\u003C/code\u003E or a success redirect. A rejected login may produce \u003Ccode\u003E401 Unauthorized\u003C/code\u003E or a configured failure redirect.\u003C/p\u003E\n\u003Cp\u003E\u003Cstrong\u003EImportant for v0.1.0:\u003C/strong\u003E \u003Ccode\u003ETokenResponseMode.Body\u003C/code\u003E is currently unsupported and rejected during response resolution. Do not assume that the HTTP login endpoint returns a JSON object containing the issued tokens. Consult the configured delivery modes and client profile instead.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022trylogin-evaluate-without-establishing-a-session\u0022\u003ETryLogin: evaluate without establishing a session\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth distinguishes \u003Cstrong\u003Epreviewing a login\u003C/strong\u003E from \u003Cstrong\u003Ecommitting a login\u003C/strong\u003E.\u003C/p\u003E\n\u003Cp\u003E\u003Ccode\u003ETryLogin\u003C/code\u003E runs the authentication evaluation in preview mode. It can indicate whether credentials would pass the current checks, report permitted failure information, and return a continuation requirement. It does \u003Cstrong\u003Enot\u003C/strong\u003E issue a session or tokens.\u003C/p\u003E\n\u003Ctable\u003E\n\u003Cthead\u003E\n\u003Ctr\u003E\n\u003Cth\u003EBehavior\u003C/th\u003E\n\u003Cth\u003ETryLogin (Preview)\u003C/th\u003E\n\u003Cth\u003ELogin (Commit)\u003C/th\u003E\n\u003C/tr\u003E\n\u003C/thead\u003E\n\u003Ctbody\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EResolve identifier and evaluate credentials\u003C/td\u003E\n\u003Ctd\u003EYes\u003C/td\u003E\n\u003Ctd\u003EYes\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EApply applicable failure-attempt handling\u003C/td\u003E\n\u003Ctd\u003EYes\u003C/td\u003E\n\u003Ctd\u003EYes\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EReset factor failure state on success\u003C/td\u003E\n\u003Ctd\u003ENo\u003C/td\u003E\n\u003Ctd\u003EYes, normally\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003ECreate a session\u003C/td\u003E\n\u003Ctd\u003ENo\u003C/td\u003E\n\u003Ctd\u003EYes, on success\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EIssue tokens\u003C/td\u003E\n\u003Ctd\u003ENo\u003C/td\u003E\n\u003Ctd\u003EOptional\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EReturn a preview receipt\u003C/td\u003E\n\u003Ctd\u003EPossible\u003C/td\u003E\n\u003Ctd\u003EConsumes a matching receipt, if supplied\u003C/td\u003E\n\u003C/tr\u003E\n\u003C/tbody\u003E\n\u003C/table\u003E\n\u003Cp\u003EOn a successful preview, the server can create a short-lived \u003Ccode\u003EPreviewReceipt\u003C/code\u003E. That receipt is associated with the user, tenant, credential type, device, identifier, client profile, and a fingerprint of the submitted login material.\u003C/p\u003E\n\u003Cp\u003EWhen a subsequent commit request includes a matching receipt, the server consumes it and can suppress a duplicate failed-attempt registration for that commit. \u003Cstrong\u003EThe receipt is not a replacement for the password\u003C/strong\u003E: the commit path still performs credential validation.\u003C/p\u003E\n\u003Cp\u003EThis separation is useful when an application needs to assess login readiness or guide a user through an authentication experience without establishing a session prematurely.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022device-awareness-and-session-management\u0022\u003EDevice awareness and session management\u003C/h2\u003E\n\u003Cp\u003EBecause login produces a managed session within a device-associated chain, UltimateAuth can model authentication beyond a single browser token:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u003Cstrong\u003EMultiple devices:\u003C/strong\u003E a user can have separate chains associated with distinct device contexts.\u003C/li\u003E\n\u003Cli\u003E\u003Cstrong\u003ESession continuity:\u003C/strong\u003E an active chain can be considered when the same user signs in again from the same device.\u003C/li\u003E\n\u003Cli\u003E\u003Cstrong\u003ETargeted session management:\u003C/strong\u003E session and chain identities give downstream operations a way to distinguish authentication instances and device-associated lifecycles.\u003C/li\u003E\n\u003Cli\u003E\u003Cstrong\u003EBroader revocation:\u003C/strong\u003E the hierarchy provides the foundation for operations that affect more than one session.\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EThe exact scope and availability of logout and revocation operations are defined by their own APIs and policies. Login establishes the identities those operations can act upon; it does not by itself specify every revocation behavior.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022security-is-part-of-the-flow-not-an-afterthought\u0022\u003ESecurity is part of the flow, not an afterthought\u003C/h2\u003E\n\u003Cp\u003ESeveral security properties follow from this design:\u003C/p\u003E\n\u003Cp\u003E\u003Cstrong\u003EUser eligibility is separate from credential correctness.\u003C/strong\u003E A valid secret does not override a disabled, deleted, or locked account.\u003C/p\u003E\n\u003Cp\u003E\u003Cstrong\u003EFailed attempts are tracked as security state.\u003C/strong\u003E Lockout behavior is configurable and can be evaluated before session issuance.\u003C/p\u003E\n\u003Cp\u003E\u003Cstrong\u003EDevice context participates in session establishment.\u003C/strong\u003E The system can distinguish device-associated chains rather than treating all logins as anonymous token issuance events.\u003C/p\u003E\n\u003Cp\u003E\u003Cstrong\u003ESession issuance follows an explicit decision.\u003C/strong\u003E A denied or challenged login does not proceed to normal session creation.\u003C/p\u003E\n\u003Cp\u003E\u003Cstrong\u003EResponse delivery is policy-driven.\u003C/strong\u003E Whether a session credential or token is delivered\u2014and by which supported mechanism\u2014is determined by the effective server flow, not solely by a client request.\u003C/p\u003E\n\u003Cp\u003E\u003Cstrong\u003EThe hierarchy supports later lifecycle enforcement.\u003C/strong\u003E Token validation, session validity, security-version checks, refresh behavior, and revocation are related concerns, but their exact enforcement belongs to those respective flows. They should not be mistaken for checks all performed inside the login method itself.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022a-useful-mental-model\u0022\u003EA useful mental model\u003C/h2\u003E\n\u003Cp\u003EThink of login as the moment UltimateAuth establishes a \u003Cstrong\u003Emanaged, device-aware authentication relationship\u003C/strong\u003E between a user and an application.\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EIdentity \u002B Valid Credential \u002B Security Decision\n \u2502\n \u25BC\n Managed Session\n \u2502\n \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n \u25BC \u25BC\n Session identity Optional tokens\n \u2502 \u2502\n \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n \u25BC\n Ongoing auth lifecycle\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EThe session is the managed authentication instance. Tokens, where used, are credentials associated with that instance. They can be issued, refreshed, and validated without redefining what a login means.\u003C/p\u003E\n\u003Cp\u003EThis is the shift UltimateAuth is designed to make: \u003Cstrong\u003Efrom login as token generation to login as lifecycle-aware session establishment\u003C/strong\u003E.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022key-takeaways\u0022\u003EKey takeaways\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u003Cstrong\u003ELogin is a flow, not a single credential-check function.\u003C/strong\u003E Identity resolution, eligibility, security evaluation, session issuance, and delivery are separate stages.\u003C/li\u003E\n\u003Cli\u003E\u003Cstrong\u003EThe hierarchy is \u003Ccode\u003EUser \u2192 Root \u2192 Chain \u2192 Session\u003C/code\u003E.\u003C/strong\u003E It expresses user-level, device-associated, and individual session lifecycles.\u003C/li\u003E\n\u003Cli\u003E\u003Cstrong\u003EA successful login establishes a session.\u003C/strong\u003E It does not necessarily create a new root or chain each time.\u003C/li\u003E\n\u003Cli\u003E\u003Cstrong\u003ETokens are optional.\u003C/strong\u003E Issuance is governed by the effective mode and server policy.\u003C/li\u003E\n\u003Cli\u003E\u003Cstrong\u003ETryLogin is not Login.\u003C/strong\u003E Preview evaluates an attempt without issuing a session; commit establishes one.\u003C/li\u003E\n\u003Cli\u003E\u003Cstrong\u003EThe authority controls admission to session issuance.\u003C/strong\u003E Security-state handling can also occur before and after that decision.\u003C/li\u003E\n\u003Cli\u003E\u003Cstrong\u003EDevice context matters.\u003C/strong\u003E Session continuity and downstream session management are built around it.\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022next-step\u0022\u003ENext step\u003C/h2\u003E\n\u003Cp\u003EContinue to \u003Cstrong\u003ERefresh Flow\u003C/strong\u003E to learn how UltimateAuth extends or rotates authentication credentials while preserving the relationship to the managed session lifecycle.\u003C/p\u003E\n", "Headings": [ { - "Id": "what-is-login", - "Text": "\uD83E\uDDE0 What is Login?", + "Id": "from-a-login-response-to-an-authentication-lifecycle", + "Text": "From a login response to an authentication lifecycle", "Level": 0 }, { - "Id": "step-by-step-execution", - "Text": "\uD83D\uDD04 Step-by-Step Execution", + "Id": "the-session-hierarchy", + "Text": "The session hierarchy", "Level": 0 }, { - "Id": "identifier-resolution", - "Text": "1\uFE0F\u20E3 Identifier Resolution", - "Level": 1 + "Id": "how-a-login-proceeds", + "Text": "How a login proceeds", + "Level": 0 }, { - "Id": "user-security-state", - "Text": "2\uFE0F\u20E3 User \u0026 Security State", + "Id": "resolve-the-authentication-context", + "Text": "1. Resolve the authentication context", "Level": 1 }, { - "Id": "credential-validation", - "Text": "3\uFE0F\u20E3 Credential Validation", + "Id": "resolve-the-login-identifier", + "Text": "2. Resolve the login identifier", "Level": 1 }, { - "Id": "authority-decision", - "Text": "4\uFE0F\u20E3 Authority Decision", + "Id": "check-eligibility-and-security-state", + "Text": "3. Check eligibility and security state", "Level": 1 }, { - "Id": "device-chain-resolution", - "Text": "5\uFE0F\u20E3 Device \u0026 Chain Resolution", + "Id": "validate-the-credential", + "Text": "4. Validate the credential", "Level": 1 }, { - "Id": "session-creation", - "Text": "6\uFE0F\u20E3 Session Creation", + "Id": "resolve-device-associated-session-continuity", + "Text": "5. Resolve device-associated session continuity", "Level": 1 }, { - "Id": "token-issuance-optional", - "Text": "7\uFE0F\u20E3 Token Issuance (Optional)", + "Id": "make-the-authority-decision", + "Text": "6. Make the authority decision", "Level": 1 }, { - "Id": "event-dispatch", - "Text": "8\uFE0F\u20E3 Event Dispatch", + "Id": "establish-the-session", + "Text": "7. Establish the session", "Level": 1 }, { - "Id": "what-gets-created", - "Text": "\uD83E\uDDE9 What Gets Created?", - "Level": 0 - }, - { - "Id": "root", - "Text": "\uD83D\uDD39 Root", + "Id": "issue-tokens-when-permitted", + "Text": "8. Issue tokens when permitted", "Level": 1 }, { - "Id": "chain", - "Text": "\uD83D\uDD39 Chain", + "Id": "dispatch-the-login-event-and-deliver-the-response", + "Text": "9. Dispatch the login event and deliver the response", "Level": 1 }, { - "Id": "session", - "Text": "\uD83D\uDD39 Session", - "Level": 1 + "Id": "trylogin-evaluate-without-establishing-a-session", + "Text": "TryLogin: evaluate without establishing a session", + "Level": 0 }, { - "Id": "device-awareness", - "Text": "\uD83D\uDCF1 Device Awareness", + "Id": "device-awareness-and-session-management", + "Text": "Device awareness and session management", "Level": 0 }, { - "Id": "security-considerations", - "Text": "\uD83D\uDD10 Security Considerations", + "Id": "security-is-part-of-the-flow-not-an-afterthought", + "Text": "Security is part of the flow, not an afterthought", "Level": 0 }, { - "Id": "mental-model", - "Text": "\uD83E\uDDE0 Mental Model", + "Id": "a-useful-mental-model", + "Text": "A useful mental model", "Level": 0 }, { "Id": "key-takeaways", - "Text": "\uD83D\uDCCC Key Takeaways", + "Text": "Key takeaways", "Level": 0 }, { "Id": "next-step", - "Text": "\u27A1\uFE0F Next Step", + "Text": "Next step", "Level": 0 } ] diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/logout-flow.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/logout-flow.json index 4d17bcda..8fb130d3 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/logout-flow.json +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/logout-flow.json @@ -1,121 +1,156 @@ { "Slug": "auth-flows/logout-flow", "Title": "Logout", - "Html": "\n\u003Cp\u003EThe logout flow in UltimateAuth is not a single action.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 It represents different \u003Cstrong\u003Elevels of authentication termination\u003C/strong\u003E.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022what-is-logout\u0022\u003E\uD83E\uDDE0 What is Logout?\u003C/h2\u003E\n\u003Cp\u003EIn traditional systems:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELogout = remove cookie or token\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EIn UltimateAuth:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Logout affects \u003Cstrong\u003Esession, device, or identity scope\u003C/strong\u003E\u003C/p\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003ELogout is not just removing access\u003Cbr /\u003E\n\u2192 It is controlling session lifecycle\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022logout-vs-revoke\u0022\u003E\uD83D\uDD00 Logout vs Revoke\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth distinguishes between two concepts:\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022logout-soft-termination\u0022\u003E\uD83D\uDD39 Logout (Soft Termination)\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EEnds the current session\u003C/li\u003E\n\u003Cli\u003EKeeps the device (chain) active\u003C/li\u003E\n\u003Cli\u003EAllows re-login without resetting device context\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cpre\u003E\u003Ccode\u003ESession \u2192 Invalidated\nChain \u2192 Still Active\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 User can log in again and continue on the same device chain\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022revoke-hard-invalidation\u0022\u003E\uD83D\uDD25 Revoke (Hard Invalidation)\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EInvalidates session, chain, or root\u003C/li\u003E\n\u003Cli\u003ECannot be undone\u003C/li\u003E\n\u003Cli\u003EForces a completely new authentication path\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cpre\u003E\u003Ccode\u003EChain \u2192 Revoked\nSessions \u2192 Revoked\nNext login \u2192 New chain\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Revoke resets trust for that scope\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022levels-of-termination\u0022\u003E\uD83E\uDDE9 Levels of Termination\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth supports multiple logout scopes:\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022session-level-logout\u0022\u003E\uD83D\uDD39 Session-Level Logout\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ETerminates a single session\u003C/li\u003E\n\u003Cli\u003EOther sessions on the same device may remain\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022device-level-chain\u0022\u003E\uD83D\uDCF1 Device-Level (Chain)\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ETerminates all sessions on a device\u003C/li\u003E\n\u003Cli\u003EDevice chain is invalidated or reset\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022global-logout-all-devices\u0022\u003E\uD83C\uDF10 Global Logout (All Devices)\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ETerminates all sessions across all devices\u003C/li\u003E\n\u003Cli\u003EKeeps root (user identity) intact\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022root-revoke\u0022\u003E\uD83D\uDD25 Root Revoke\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EInvalidates entire authentication state\u003C/li\u003E\n\u003Cli\u003EAll chains and sessions are revoked\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This is the strongest possible action\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022step-by-step-execution\u0022\u003E\uD83D\uDD04 Step-by-Step Execution\u003C/h2\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022flow-context-resolution\u0022\u003E1\uFE0F\u20E3 Flow Context Resolution\u003C/h3\u003E\n\u003Cp\u003EThe system resolves:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ECurrent session\u003C/li\u003E\n\u003Cli\u003EUser identity\u003C/li\u003E\n\u003Cli\u003ETenant\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022authority-decision\u0022\u003E2\uFE0F\u20E3 Authority Decision\u003C/h3\u003E\n\u003Cp\u003ELogout operations are validated:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EAuthorization checks\u003C/li\u003E\n\u003Cli\u003EAccess validation\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Logout is not blindly executed\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022scope-determination\u0022\u003E3\uFE0F\u20E3 Scope Determination\u003C/h3\u003E\n\u003Cp\u003EThe system determines what to terminate:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession\u003C/li\u003E\n\u003Cli\u003EChain\u003C/li\u003E\n\u003Cli\u003ERoot\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022execution\u0022\u003E4\uFE0F\u20E3 Execution\u003C/h3\u003E\n\u003Cp\u003EDepending on scope:\u003C/p\u003E\n\u003Ch4 id=\u0022session-logout\u0022\u003ESession Logout\u003C/h4\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession is revoked\u003C/li\u003E\n\u003Cli\u003EOther sessions unaffected\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch4 id=\u0022chain-revoke-logout\u0022\u003EChain Revoke / Logout\u003C/h4\u003E\n\u003Cul\u003E\n\u003Cli\u003EAll sessions in the chain are revoked\u003C/li\u003E\n\u003Cli\u003EDevice trust is reset\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch4 id=\u0022global-logout\u0022\u003EGlobal Logout\u003C/h4\u003E\n\u003Cul\u003E\n\u003Cli\u003EAll chains are revoked (optionally excluding current)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch4 id=\u0022root-revoke-1\u0022\u003ERoot Revoke\u003C/h4\u003E\n\u003Cul\u003E\n\u003Cli\u003EEntire identity state is invalidated\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022event-dispatch\u0022\u003E5\uFE0F\u20E3 Event Dispatch\u003C/h3\u003E\n\u003Cp\u003EThe system emits:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELogout events\u003C/li\u003E\n\u003Cli\u003EAudit logs\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022device-awareness\u0022\u003E\uD83D\uDCF1 Device Awareness\u003C/h2\u003E\n\u003Cp\u003ELogout behavior is device-aware:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EEach device is a chain\u003C/li\u003E\n\u003Cli\u003ELogout can target specific devices\u003C/li\u003E\n\u003Cli\u003ESessions are grouped by device\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This enables fine-grained control\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022security-model\u0022\u003E\uD83D\uDD10 Security Model\u003C/h2\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022controlled-termination\u0022\u003E\uD83D\uDD12 Controlled Termination\u003C/h3\u003E\n\u003Cp\u003EAll logout operations:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EPass through orchestrator\u003C/li\u003E\n\u003Cli\u003EAre validated by authority\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Prevents unauthorized session manipulation\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022irreversible-revocation\u0022\u003E\uD83D\uDD01 Irreversible Revocation\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ERevoked chains cannot be restored\u003C/li\u003E\n\u003Cli\u003ERevoked sessions remain invalid\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Ensures strong security guarantees\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022identity-boundaries\u0022\u003E\uD83D\uDD17 Identity Boundaries\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession \u2192 temporary identity proof\u003C/li\u003E\n\u003Cli\u003EChain \u2192 device trust boundary\u003C/li\u003E\n\u003Cli\u003ERoot \u2192 global identity state\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Logout operates within these boundaries\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Logout = ending a session\u003Cbr /\u003E\n\uD83D\uDC49 Revoke = resetting trust\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022key-takeaways\u0022\u003E\uD83D\uDCCC Key Takeaways\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003ELogout and revoke are different operations\u003C/li\u003E\n\u003Cli\u003ELogout is reversible (via re-login)\u003C/li\u003E\n\u003Cli\u003ERevoke is permanent and forces new authentication\u003C/li\u003E\n\u003Cli\u003EDevice (chain) is a first-class concept\u003C/li\u003E\n\u003Cli\u003ESecurity is enforced through authority and orchestrator\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003EContinue to \u003Cstrong\u003ESession Lifecycle\u003C/strong\u003E\u003C/p\u003E\n", + "Html": "\n\u003Cp\u003EAuthentication does not end when a client deletes a cookie or forgets a token. In UltimateAuth, ending access is a \u003Cstrong\u003Eserver-side lifecycle decision\u003C/strong\u003E: the system determines which authentication state must stop being usable, at what scope, and whether the associated device relationship should survive a future login.\u003C/p\u003E\n\u003Cp\u003EThat distinction is central to UltimateAuth\u0027s design. \u003Cstrong\u003ELogout ends access; revoke permanently invalidates the selected trust boundary.\u003C/strong\u003E Both can prevent a session from being used, but they express different intentions and have different consequences for the session hierarchy.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022logout-is-more-than-clearing-credentials\u0022\u003ELogout is more than clearing credentials\u003C/h2\u003E\n\u003Cp\u003EA traditional application may implement logout by removing a browser cookie or deleting a locally stored token. That is useful client cleanup, but by itself it does not necessarily invalidate a credential already issued by the server. A copied token could remain usable until its expiration if the server has no corresponding invalidation mechanism.\u003C/p\u003E\n\u003Cp\u003EUltimateAuth instead models authentication around a hierarchy:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EUser\n\u2514\u2500\u2500 Root Global authentication lineage\n \u251C\u2500\u2500 Chain Device-associated authentication lineage\n \u2502 \u251C\u2500\u2500 Session Individual authentication instance\n \u2502 \u2514\u2500\u2500 Session Another instance in the same chain\n \u2514\u2500\u2500 Chain Another device\n \u2514\u2500\u2500 Session\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EThis structure gives termination a meaningful scope. An application can end access on the current device, sign out other devices, sign out all devices, or explicitly revoke a session, chain, or root. The correct choice depends on the intent: ordinary sign-out, account security, device compromise, or global trust reset.\u003C/p\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003E\u003Cstrong\u003EMental model:\u003C/strong\u003E Logout is a controlled exit from authentication. Revoke is a permanent invalidation of the selected authentication state. A later login may establish new state, but it does not undo a revocation.\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022logout-versus-revoke\u0022\u003ELogout versus revoke\u003C/h2\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022logout-end-access-without-necessarily-destroying-the-device-lineage\u0022\u003ELogout: end access without necessarily destroying the device lineage\u003C/h3\u003E\n\u003Cp\u003EAn ordinary logout is designed for situations such as a user selecting \u003Cstrong\u003ESign out\u003C/strong\u003E in an application. It ends the relevant authenticated access while allowing the device-associated chain to remain available for a subsequent login, subject to its state and lifetime rules.\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EBefore logout\nRoot (active)\n\u2514\u2500\u2500 Chain (active)\n \u2514\u2500\u2500 Session (authenticated)\n\nAfter logout\nRoot (active)\n\u2514\u2500\u2500 Chain (retained)\n \u2514\u2500\u2500 Session (no longer usable)\n\nAfter a later successful login\nRoot (active)\n\u2514\u2500\u2500 Chain (reusable if still eligible)\n \u2514\u2500\u2500 New Session\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\u003Cstrong\u003ERetaining a chain does not mean retaining an authenticated session.\u003C/strong\u003E The user must authenticate again. Reuse is conditional: an expired, revoked, or otherwise ineligible chain must not be treated as active merely because it still exists in storage.\u003C/p\u003E\n\u003Cp\u003EA subtle but important implementation detail: the current HTTP logout flow resolves the authenticated session and invokes the flow service, whose logout command identifies and logs out the \u003Cstrong\u003Eassociated chain\u003C/strong\u003E. Consequently, the default logout endpoint should not be described as a guarantee that \u003Cem\u003Eonly one session record\u003C/em\u003E changes. Its practical boundary is the current device chain.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022revoke-permanently-invalidate-the-selected-trust-boundary\u0022\u003ERevoke: permanently invalidate the selected trust boundary\u003C/h3\u003E\n\u003Cp\u003ERevocation is a stronger security operation. A revoked session, chain, or root is not restored by a subsequent login, refresh, or cookie update.\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EChain (active)\n\u2514\u2500\u2500 Session (active)\n\nRevoke chain\n\nChain (revoked \u2014 terminal state)\n\u2514\u2500\u2500 Sessions (invalidated)\n\nFuture authentication \u2192 new eligible chain, not restoration of the old one\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\u003Cstrong\u003ERevocation is irreversible for the revoked entity.\u003C/strong\u003E This does not mean the user is permanently locked out of the application. A legitimate user may authenticate again and establish a new session lineage if account policy allows it. What cannot happen is bringing the \u003Cem\u003Erevoked\u003C/em\u003E session or chain back to life.\u003C/p\u003E\n\u003Cp\u003EThis distinction is useful in security-sensitive scenarios: a lost device, suspected credential theft, administrator intervention, or a decision to invalidate all existing authentication relationships.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022choosing-the-termination-scope\u0022\u003EChoosing the termination scope\u003C/h2\u003E\n\u003Ctable\u003E\n\u003Cthead\u003E\n\u003Ctr\u003E\n\u003Cth\u003EOperation\u003C/th\u003E\n\u003Cth\u003EIntended scope\u003C/th\u003E\n\u003Cth\u003EWhat it means\u003C/th\u003E\n\u003C/tr\u003E\n\u003C/thead\u003E\n\u003Ctbody\u003E\n\u003Ctr\u003E\n\u003Ctd\u003E\u003Cstrong\u003ELogout current device\u003C/strong\u003E\u003C/td\u003E\n\u003Ctd\u003ECurrent session\u0027s chain\u003C/td\u003E\n\u003Ctd\u003EEnds authenticated access for the current device while preserving the possibility of later eligible chain reuse.\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003E\u003Cstrong\u003ELogout a device\u003C/strong\u003E\u003C/td\u003E\n\u003Ctd\u003ESelected chain\u003C/td\u003E\n\u003Ctd\u003EEnds access on the chosen device; can be initiated by the user or an authorized administrator.\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003E\u003Cstrong\u003ELogout other devices\u003C/strong\u003E\u003C/td\u003E\n\u003Ctd\u003EOther device sessions\u003C/td\u003E\n\u003Ctd\u003EEnds access elsewhere while preserving the specified current device context.\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003E\u003Cstrong\u003ELogout all devices\u003C/strong\u003E\u003C/td\u003E\n\u003Ctd\u003EAll user sessions\u003C/td\u003E\n\u003Ctd\u003ESigns the user out across devices without treating this as a root revocation.\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003E\u003Cstrong\u003ERevoke session\u003C/strong\u003E\u003C/td\u003E\n\u003Ctd\u003EOne session\u003C/td\u003E\n\u003Ctd\u003EPermanently invalidates the selected session.\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003E\u003Cstrong\u003ERevoke chain\u003C/strong\u003E\u003C/td\u003E\n\u003Ctd\u003EOne device lineage\u003C/td\u003E\n\u003Ctd\u003EPermanently invalidates that chain and its sessions.\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003E\u003Cstrong\u003ERevoke other/all chains\u003C/strong\u003E\u003C/td\u003E\n\u003Ctd\u003EMultiple device lineages\u003C/td\u003E\n\u003Ctd\u003EPermanently invalidates selected device relationships.\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003E\u003Cstrong\u003ERevoke root\u003C/strong\u003E\u003C/td\u003E\n\u003Ctd\u003EGlobal authentication lineage\u003C/td\u003E\n\u003Ctd\u003EInvalidates the root and cascades to its chains and sessions.\u003C/td\u003E\n\u003C/tr\u003E\n\u003C/tbody\u003E\n\u003C/table\u003E\n\u003Cp\u003EThese are not interchangeable aliases. In particular, \u003Cstrong\u003Elogout all devices\u003C/strong\u003E and \u003Cstrong\u003Erevoke root\u003C/strong\u003E both disrupt broad access, but they do not express the same lifecycle intent.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022why-session-level-revoke-is-separate\u0022\u003EWhy session-level revoke is separate\u003C/h3\u003E\n\u003Cp\u003EUltimateAuth exposes a session revocation operation distinct from the ordinary logout endpoint. A single session can be revoked without necessarily revoking its parent chain. This is useful when an application needs to invalidate a particular authentication instance rather than the entire device relationship.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022why-root-revoke-is-exceptional\u0022\u003EWhy root revoke is exceptional\u003C/h3\u003E\n\u003Cp\u003EThe root represents the user\u0027s active authentication lineage, not the user\u0027s existence in the user directory. Root revocation cascades through the associated authentication hierarchy. A later permitted login may establish a \u003Cstrong\u003Enew\u003C/strong\u003E active root; it does not reverse the old root\u0027s revocation.\u003C/p\u003E\n\u003Cp\u003ERoot revocation should therefore be reserved for an intentional global trust reset rather than ordinary sign-out.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022how-the-current-logout-flow-executes\u0022\u003EHow the current logout flow executes\u003C/h2\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022resolve-the-flow-context\u0022\u003E1. Resolve the flow context\u003C/h3\u003E\n\u003Cp\u003EThe HTTP endpoint obtains the current \u003Ccode\u003EAuthFlowContext\u003C/code\u003E. This carries the relevant authentication state and response configuration, including the resolved session when one is available.\u003C/p\u003E\n\u003Cp\u003EThe ordinary logout endpoint does not accept an arbitrary session ID from the browser and blindly invalidate it. It uses the session already resolved for the current flow.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022request-server-side-termination\u0022\u003E2. Request server-side termination\u003C/h3\u003E\n\u003Cp\u003EWhen a \u003Ccode\u003ESessionSecurityContext\u003C/code\u003E is present, the endpoint constructs a \u003Ccode\u003ELogoutRequest\u003C/code\u003E using that session ID and calls the flow service:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Evar request = new LogoutRequest\n{\n SessionId = session.SessionId\n};\n\nawait _client.Flows.LogoutAsync(request);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EThe logout command resolves the session\u0027s chain and invokes the session issuer\u0027s \u003Ccode\u003ELogoutChainAsync\u003C/code\u003E operation. The session-store implementation performs the chain logout transition.\u003C/p\u003E\n\u003Cp\u003EThis is the authoritative part of logout. Removing a client cookie alone is not a substitute for this state transition.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022clear-cookies-configured-for-this-flow\u0022\u003E3. Clear cookies configured for this flow\u003C/h3\u003E\n\u003Cp\u003EAfter the server-side operation, the HTTP handler attempts to delete credentials whose \u003Cstrong\u003Eeffective delivery mode is Cookie\u003C/strong\u003E:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession ID cookie\u003C/li\u003E\n\u003Cli\u003ERefresh token cookie\u003C/li\u003E\n\u003Cli\u003EAccess token cookie\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EThe handler uses the configured cookie names through \u003Ccode\u003EIUAuthCookieManager\u003C/code\u003E. It does not assume that all three credentials are cookies, and it does not attempt to delete header-delivered credentials from a remote client\u0027s storage.\u003C/p\u003E\n\u003Cp\u003EWhen the flow has no resolved session, the endpoint can still perform cookie cleanup and return a successful logout response. This makes sign-out useful even when a browser holds stale credentials.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022resolve-the-response-and-optional-redirect\u0022\u003E4. Resolve the response and optional redirect\u003C/h3\u003E\n\u003Cp\u003EOnce termination and cookie cleanup have been handled, UltimateAuth evaluates the logout response configuration. The result is either a redirect to the configured destination or a normal HTTP success response.\u003C/p\u003E\n\u003Cp\u003EWith redirect disabled, the ordinary endpoint returns:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-json\u0022\u003E{\n \u0026quot;isSuccess\u0026quot;: true\n}\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EWith redirect enabled, it returns an HTTP redirect to the resolved target instead. Applications should not expect the JSON success body in that case.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022why-redirect-matters\u0022\u003EWhy redirect matters\u003C/h2\u003E\n\u003Cp\u003EA redirect is \u003Cstrong\u003Enot required to invalidate authentication state\u003C/strong\u003E. It is a navigation concern that becomes important in browser-based authentication flows.\u003C/p\u003E\n\u003Cp\u003EConsider a server-rendered application with a dedicated sign-out page. Once the session has ended and cookies are cleared, the browser should often return to a public page, a login screen, or the page originally requested by the user. Redirecting completes that user journey without requiring the application to parse a logout response and navigate manually.\u003C/p\u003E\n\u003Cp\u003ERedirects can also help when the authentication endpoint and the client application are on different origins or when a centralized authentication service serves multiple clients. The server can perform the security operation and then send the browser back to the appropriate application destination.\u003C/p\u003E\n\u003Cp\u003EIn contrast, a mobile client or API consumer commonly prefers an ordinary HTTP result and controls its own navigation. \u003Cstrong\u003ERedirect is a response behavior, not a security primitive.\u003C/strong\u003E\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022redirect-resolution-in-ultimateauth\u0022\u003ERedirect resolution in UltimateAuth\u003C/h3\u003E\n\u003Cp\u003EThe response resolver selects the effective redirect settings for the logout flow. The redirect resolver then:\u003C/p\u003E\n\u003Col\u003E\n\u003Cli\u003EChecks whether redirect is enabled.\u003C/li\u003E\n\u003Cli\u003EOn success, considers an allowed return URL override when configured.\u003C/li\u003E\n\u003Cli\u003EOtherwise uses the configured success path, resolved against the client base address.\u003C/li\u003E\n\u003Cli\u003EIf no valid destination is configured, leaves redirect disabled and allows a normal success response.\u003C/li\u003E\n\u003C/ol\u003E\n\u003Cp\u003EFor absolute return URL overrides, the resolver restricts schemes to HTTP or HTTPS and validates the origin against configured allowed client origins when that allowlist is populated. Relative paths are resolved against the selected client base address.\u003C/p\u003E\n\u003Cp\u003EThis is why a return URL should be treated as \u003Cstrong\u003Evalidated navigation input\u003C/strong\u003E, never as an unrestricted arbitrary destination. Applications should configure allowed origins carefully, especially in multi-client or hosted scenarios.\u003C/p\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003E\u003Cstrong\u003EImportant:\u003C/strong\u003E Do not use a redirect as proof that logout succeeded, or assume that clearing browser cookies invalidates every credential issued to every device. The server-side termination scope determines what access has actually ended.\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022device-aware-logout\u0022\u003EDevice-aware logout\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth treats the device-associated chain as a first-class authentication boundary. That allows applications to build features such as:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u003Cstrong\u003ESign out of this device\u003C/strong\u003E \u2014 end the current device\u0027s authenticated access.\u003C/li\u003E\n\u003Cli\u003E\u003Cstrong\u003ESign out of another device\u003C/strong\u003E \u2014 target a specific chain after the appropriate access checks.\u003C/li\u003E\n\u003Cli\u003E\u003Cstrong\u003ESign out of other devices\u003C/strong\u003E \u2014 preserve the current device while ending other sessions.\u003C/li\u003E\n\u003Cli\u003E\u003Cstrong\u003ESign out everywhere\u003C/strong\u003E \u2014 end all device sessions for the user.\u003C/li\u003E\n\u003Cli\u003E\u003Cstrong\u003ERevoke a lost device\u003C/strong\u003E \u2014 permanently invalidate the compromised chain instead of merely signing it out.\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EThese operations are exposed through distinct application-service methods, rather than overloading a single logout call with ambiguous behavior.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022self-service-and-administrative-actions\u0022\u003ESelf-service and administrative actions\u003C/h3\u003E\n\u003Cp\u003EDevice and all-device HTTP endpoints use access contexts carrying explicit action identifiers. They are executed through the access orchestrator, which enforces the applicable authorization decision before the requested state mutation.\u003C/p\u003E\n\u003Cp\u003EFor example, \u003Ccode\u003ELogoutDeviceSelfAsync\u003C/code\u003E targets the authenticated user\u0027s device, whereas \u003Ccode\u003ELogoutDeviceAdminAsync\u003C/code\u003E accepts a target user and executes under an administrative action context. Both ultimately call the device logout application service; authorization and target ownership are not left to the client.\u003C/p\u003E\n\u003Cp\u003EThe default current-device logout endpoint follows its own flow-based path. Do not assume every termination operation shares the same exact handler or authorization pipeline.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022security-properties-and-boundaries\u0022\u003ESecurity properties and boundaries\u003C/h2\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022server-side-invalidation-comes-first\u0022\u003EServer-side invalidation comes first\u003C/h3\u003E\n\u003Cp\u003EThe server must stop recognizing the affected authentication state. Browser cleanup improves user experience and reduces accidental credential reuse, but it is not the source of truth.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022revoked-state-is-terminal\u0022\u003ERevoked state is terminal\u003C/h3\u003E\n\u003Cp\u003ERevocation cannot be undone by setting \u003Ccode\u003EIsRevoked\u003C/code\u003E back to false, presenting an old refresh token, or logging in again with the same device. A new authentication path must establish new eligible state.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022scope-must-be-explicit\u0022\u003EScope must be explicit\u003C/h3\u003E\n\u003Cp\u003EA session revoke should not be casually described as a chain revoke. A logout-all operation should not be described as a root revoke. These distinctions determine whether existing device lineages are retained, ended, or permanently invalidated.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022authorization-and-ownership-matter\u0022\u003EAuthorization and ownership matter\u003C/h3\u003E\n\u003Cp\u003ESelf-service operations must stay within the authenticated user\u0027s permitted scope. Administrative operations must be evaluated under their corresponding action and target context. A client-provided chain identifier is not sufficient authorization to terminate someone else\u0027s session.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022token-delivery-affects-cleanup-not-the-termination-model\u0022\u003EToken delivery affects cleanup, not the termination model\u003C/h3\u003E\n\u003Cp\u003EWhether a client receives credentials through cookies or headers, the server-side hierarchy still governs session-backed authentication. Clients using non-cookie delivery must also clear their own local credentials after successful termination.\u003C/p\u003E\n\u003Cp\u003EThe precise behavior of already-issued, independently validated tokens depends on the active authentication mode and token validation strategy. Avoid assuming every token format provides instantaneous server-side revocation without checking its validation model.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022practical-examples\u0022\u003EPractical examples\u003C/h2\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022a-user-signs-out-at-the-end-of-the-day\u0022\u003EA user signs out at the end of the day\u003C/h3\u003E\n\u003Cp\u003EUse the ordinary logout flow. The current device\u0027s authenticated access ends, configured cookies are cleared, and the browser may be redirected to a public page. A later successful login can reuse the retained device chain if it remains eligible.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022a-user-loses-a-phone\u0022\u003EA user loses a phone\u003C/h3\u003E\n\u003Cp\u003EUse \u003Cstrong\u003Echain revoke\u003C/strong\u003E, not merely a soft device logout, when the goal is to permanently invalidate that device\u0027s existing trust lineage. The user may later authenticate on a replacement device, but the revoked chain stays revoked.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022a-user-wants-to-keep-this-device-but-sign-out-elsewhere\u0022\u003EA user wants to keep this device but sign out elsewhere\u003C/h3\u003E\n\u003Cp\u003EUse \u003Cstrong\u003Elogout other devices\u003C/strong\u003E. The current device\u0027s chain is excluded while other device sessions are terminated. This is different from permanently revoking every other chain.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022an-account-is-believed-to-be-compromised\u0022\u003EAn account is believed to be compromised\u003C/h3\u003E\n\u003Cp\u003EDepending on the security policy, revoke all chains or the root. This is a stronger response than ordinary logout because it deliberately invalidates existing authentication lineages. Credential rotation and additional account recovery steps may also be required; revocation alone does not prove that compromised credentials have been changed.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022common-misconceptions\u0022\u003ECommon misconceptions\u003C/h2\u003E\n\u003Cp\u003E\u003Cstrong\u003E\u201CLogout just deletes the cookie.\u201D\u003C/strong\u003E No. The ordinary endpoint performs server-side termination when a session is resolved, then clears applicable cookies.\u003C/p\u003E\n\u003Cp\u003E\u003Cstrong\u003E\u201CLogout and revoke are the same operation.\u201D\u003C/strong\u003E No. Both can stop access, but revocation is permanent for the selected entity; soft logout can retain eligible device lineage.\u003C/p\u003E\n\u003Cp\u003E\u003Cstrong\u003E\u201CLogout always means exactly one session.\u201D\u003C/strong\u003E Not in the current default HTTP flow. The resolved session identifies a chain, and the logout command operates on that chain.\u003C/p\u003E\n\u003Cp\u003E\u003Cstrong\u003E\u201CRevocation permanently bans the user.\u201D\u003C/strong\u003E No. It permanently invalidates existing authentication state at the selected scope. A permitted future login may create new state.\u003C/p\u003E\n\u003Cp\u003E\u003Cstrong\u003E\u201CRedirect makes logout secure.\u201D\u003C/strong\u003E No. Redirect controls where the browser goes after logout; the session-store transition is what ends server-recognized access.\u003C/p\u003E\n\u003Cp\u003E\u003Cstrong\u003E\u201CLogout all devices is the same as revoke root.\u201D\u003C/strong\u003E No. They are different lifecycle operations with different implications for the authentication hierarchy.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022key-takeaways\u0022\u003EKey takeaways\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u003Cstrong\u003ELogout is a lifecycle operation\u003C/strong\u003E, not merely client-side credential deletion.\u003C/li\u003E\n\u003Cli\u003E\u003Cstrong\u003ERevoke is irreversible\u003C/strong\u003E for the revoked session, chain, or root; a future login never restores that entity.\u003C/li\u003E\n\u003Cli\u003E\u003Cstrong\u003ERoot \u2192 Chain \u2192 Session\u003C/strong\u003E makes termination scope explicit and device-aware.\u003C/li\u003E\n\u003Cli\u003E\u003Cstrong\u003ECurrent-device logout operates through the associated chain\u003C/strong\u003E in the current server implementation.\u003C/li\u003E\n\u003Cli\u003E\u003Cstrong\u003EOther-device and all-device logout\u003C/strong\u003E are separate from hard chain/root revocation.\u003C/li\u003E\n\u003Cli\u003E\u003Cstrong\u003ERedirect is optional navigation behavior\u003C/strong\u003E, especially useful for browser-based flows; it does not perform revocation.\u003C/li\u003E\n\u003Cli\u003E\u003Cstrong\u003EServer-side state is authoritative\u003C/strong\u003E for session-backed access; client credential cleanup remains important.\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next step\u003C/h2\u003E\n\u003Cp\u003EContinue to \u003Cstrong\u003ESession Lifecycle\u003C/strong\u003E to understand how roots, chains, and sessions are created, rotated, expired, logged out, and revoked over time.\u003C/p\u003E\n", "Headings": [ { - "Id": "what-is-logout", - "Text": "\uD83E\uDDE0 What is Logout?", + "Id": "logout-is-more-than-clearing-credentials", + "Text": "Logout is more than clearing credentials", "Level": 0 }, { - "Id": "logout-vs-revoke", - "Text": "\uD83D\uDD00 Logout vs Revoke", + "Id": "logout-versus-revoke", + "Text": "Logout versus revoke", "Level": 0 }, { - "Id": "logout-soft-termination", - "Text": "\uD83D\uDD39 Logout (Soft Termination)", + "Id": "logout-end-access-without-necessarily-destroying-the-device-lineage", + "Text": "Logout: end access without necessarily destroying the device lineage", "Level": 1 }, { - "Id": "revoke-hard-invalidation", - "Text": "\uD83D\uDD25 Revoke (Hard Invalidation)", + "Id": "revoke-permanently-invalidate-the-selected-trust-boundary", + "Text": "Revoke: permanently invalidate the selected trust boundary", "Level": 1 }, { - "Id": "levels-of-termination", - "Text": "\uD83E\uDDE9 Levels of Termination", + "Id": "choosing-the-termination-scope", + "Text": "Choosing the termination scope", "Level": 0 }, { - "Id": "session-level-logout", - "Text": "\uD83D\uDD39 Session-Level Logout", + "Id": "why-session-level-revoke-is-separate", + "Text": "Why session-level revoke is separate", "Level": 1 }, { - "Id": "device-level-chain", - "Text": "\uD83D\uDCF1 Device-Level (Chain)", + "Id": "why-root-revoke-is-exceptional", + "Text": "Why root revoke is exceptional", "Level": 1 }, { - "Id": "global-logout-all-devices", - "Text": "\uD83C\uDF10 Global Logout (All Devices)", + "Id": "how-the-current-logout-flow-executes", + "Text": "How the current logout flow executes", + "Level": 0 + }, + { + "Id": "resolve-the-flow-context", + "Text": "1. Resolve the flow context", + "Level": 1 + }, + { + "Id": "request-server-side-termination", + "Text": "2. Request server-side termination", "Level": 1 }, { - "Id": "root-revoke", - "Text": "\uD83D\uDD25 Root Revoke", + "Id": "clear-cookies-configured-for-this-flow", + "Text": "3. Clear cookies configured for this flow", "Level": 1 }, { - "Id": "step-by-step-execution", - "Text": "\uD83D\uDD04 Step-by-Step Execution", + "Id": "resolve-the-response-and-optional-redirect", + "Text": "4. Resolve the response and optional redirect", + "Level": 1 + }, + { + "Id": "why-redirect-matters", + "Text": "Why redirect matters", "Level": 0 }, { - "Id": "flow-context-resolution", - "Text": "1\uFE0F\u20E3 Flow Context Resolution", + "Id": "redirect-resolution-in-ultimateauth", + "Text": "Redirect resolution in UltimateAuth", "Level": 1 }, { - "Id": "authority-decision", - "Text": "2\uFE0F\u20E3 Authority Decision", + "Id": "device-aware-logout", + "Text": "Device-aware logout", + "Level": 0 + }, + { + "Id": "self-service-and-administrative-actions", + "Text": "Self-service and administrative actions", "Level": 1 }, { - "Id": "scope-determination", - "Text": "3\uFE0F\u20E3 Scope Determination", + "Id": "security-properties-and-boundaries", + "Text": "Security properties and boundaries", + "Level": 0 + }, + { + "Id": "server-side-invalidation-comes-first", + "Text": "Server-side invalidation comes first", "Level": 1 }, { - "Id": "execution", - "Text": "4\uFE0F\u20E3 Execution", + "Id": "revoked-state-is-terminal", + "Text": "Revoked state is terminal", "Level": 1 }, { - "Id": "event-dispatch", - "Text": "5\uFE0F\u20E3 Event Dispatch", + "Id": "scope-must-be-explicit", + "Text": "Scope must be explicit", "Level": 1 }, { - "Id": "device-awareness", - "Text": "\uD83D\uDCF1 Device Awareness", - "Level": 0 + "Id": "authorization-and-ownership-matter", + "Text": "Authorization and ownership matter", + "Level": 1 }, { - "Id": "security-model", - "Text": "\uD83D\uDD10 Security Model", + "Id": "token-delivery-affects-cleanup-not-the-termination-model", + "Text": "Token delivery affects cleanup, not the termination model", + "Level": 1 + }, + { + "Id": "practical-examples", + "Text": "Practical examples", "Level": 0 }, { - "Id": "controlled-termination", - "Text": "\uD83D\uDD12 Controlled Termination", + "Id": "a-user-signs-out-at-the-end-of-the-day", + "Text": "A user signs out at the end of the day", + "Level": 1 + }, + { + "Id": "a-user-loses-a-phone", + "Text": "A user loses a phone", "Level": 1 }, { - "Id": "irreversible-revocation", - "Text": "\uD83D\uDD01 Irreversible Revocation", + "Id": "a-user-wants-to-keep-this-device-but-sign-out-elsewhere", + "Text": "A user wants to keep this device but sign out elsewhere", "Level": 1 }, { - "Id": "identity-boundaries", - "Text": "\uD83D\uDD17 Identity Boundaries", + "Id": "an-account-is-believed-to-be-compromised", + "Text": "An account is believed to be compromised", "Level": 1 }, { - "Id": "mental-model", - "Text": "\uD83E\uDDE0 Mental Model", + "Id": "common-misconceptions", + "Text": "Common misconceptions", "Level": 0 }, { "Id": "key-takeaways", - "Text": "\uD83D\uDCCC Key Takeaways", + "Text": "Key takeaways", "Level": 0 }, { "Id": "next-step", - "Text": "\u27A1\uFE0F Next Step", + "Text": "\u27A1\uFE0F Next step", "Level": 0 } ] diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/getting-started/index.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/getting-started/index.json index 63bc48a1..09f5f9c3 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/getting-started/index.json +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/getting-started/index.json @@ -1,7 +1,7 @@ { "Slug": "getting-started/index", "Title": "Getting Started", - "Html": "\n\u003Cp\u003EWelcome to \u003Cstrong\u003EUltimateAuth\u003C/strong\u003E \u2014 the modern authentication framework for .NET.\u003C/p\u003E\n\u003Cp\u003EUltimateAuth is designed to make authentication \u003Cstrong\u003Esimple to use\u003C/strong\u003E, while still being \u003Cstrong\u003Epowerful, flexible, and deeply secure\u003C/strong\u003E enough for real-world applications.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022what-is-ultimateauth\u0022\u003EWhat is UltimateAuth?\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth is a \u003Cstrong\u003Eflow-driven authentication framework\u003C/strong\u003E that reimagines how authentication works in modern .NET applications.\u003C/p\u003E\n\u003Cp\u003EIt unifies:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession-based authentication\u003C/li\u003E\n\u003Cli\u003EToken-based authentication (JWT)\u003C/li\u003E\n\u003Cli\u003EPKCE flows for public clients\u003C/li\u003E\n\u003Cli\u003EMulti-client environments (Blazor Server, WASM, MAUI, APIs)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003Einto a single, consistent system.\u003C/p\u003E\n\u003Cp\u003EInstead of choosing between cookies, sessions, or tokens, UltimateAuth allows you to use \u003Cstrong\u003Ethe right model for each scenario \u2014 automatically\u003C/strong\u003E.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022what-makes-ultimateauth-different\u0022\u003EWhat Makes UltimateAuth Different?\u003C/h2\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022session-is-a-first-class-concept\u0022\u003E\uD83D\uDD39 Session is a First-Class Concept\u003C/h3\u003E\n\u003Cp\u003EUnlike traditional systems, UltimateAuth treats sessions as a \u003Cstrong\u003Ecore domain\u003C/strong\u003E, not a side effect.\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERoot \u2192 global security authority\u003C/li\u003E\n\u003Cli\u003EChain \u2192 device context\u003C/li\u003E\n\u003Cli\u003ESession \u2192 actual authentication instance\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EThis allows:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EInstant revocation\u003C/li\u003E\n\u003Cli\u003EMulti-device control\u003C/li\u003E\n\u003Cli\u003ESecure session lifecycle management\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022flow-based-not-token-based\u0022\u003E\uD83D\uDD39 Flow-Based, Not Token-Based\u003C/h3\u003E\n\u003Cp\u003EUltimateAuth is not cookie-based or token-based.\u003C/p\u003E\n\u003Cp\u003EIt is \u003Cstrong\u003Eflow-based\u003C/strong\u003E:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELogin is a flow\u003C/li\u003E\n\u003Cli\u003ERefresh is a flow\u003C/li\u003E\n\u003Cli\u003ERe-authentication is a flow\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Authentication becomes \u003Cstrong\u003Eexplicit, predictable, and controllable\u003C/strong\u003E\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022built-for-blazor-and-modern-clients\u0022\u003E\uD83D\uDD39 Built for Blazor and Modern Clients\u003C/h3\u003E\n\u003Cp\u003EUltimateAuth is designed from the ground up for:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EBlazor Server\u003C/li\u003E\n\u003Cli\u003EBlazor WASM\u003C/li\u003E\n\u003Cli\u003E.NET MAUI\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EWith:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ENative PKCE support\u003C/li\u003E\n\u003Cli\u003EBuilt-in Blazor components (\u003Ccode\u003EUAuthLoginForm\u003C/code\u003E, \u003Ccode\u003EUAuthApp\u003C/code\u003E)\u003C/li\u003E\n\u003Cli\u003EAutomatic client profile detection\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 No hacks. No manual glue code.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022runtime-aware-authentication\u0022\u003E\uD83D\uDD39 Runtime-Aware Authentication\u003C/h3\u003E\n\u003Cp\u003EAuthentication behavior is not static.\u003C/p\u003E\n\u003Cp\u003EUltimateAuth adapts based on:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EClient type\u003C/li\u003E\n\u003Cli\u003EAuth mode\u003C/li\u003E\n\u003Cli\u003ERequest context\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Same system, different optimized behavior.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022what-problems-it-solves\u0022\u003EWhat Problems It Solves\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth addresses real-world challenges:\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022multiple-client-types\u0022\u003E\uD83D\uDD39 Multiple Client Types\u003C/h3\u003E\n\u003Cp\u003EBlazor Server, WASM, MAUI, and APIs all behave differently.\u003C/p\u003E\n\u003Cp\u003EUltimateAuth handles these differences automatically using \u003Cstrong\u003EClient Profiles\u003C/strong\u003E.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022session-vs-token-confusion\u0022\u003E\uD83D\uDD39 Session vs Token Confusion\u003C/h3\u003E\n\u003Cp\u003EShould you use cookies, sessions, or JWT?\u003C/p\u003E\n\u003Cp\u003EUltimateAuth removes this decision by supporting multiple auth modes and selecting the correct behavior at runtime.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022secure-session-management\u0022\u003E\uD83D\uDD39 Secure Session Management\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EDevice-aware sessions\u003C/li\u003E\n\u003Cli\u003ESession revocation\u003C/li\u003E\n\u003Cli\u003ERefresh token rotation\u003C/li\u003E\n\u003Cli\u003EReplay protection\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EAll built-in \u2014 no custom implementation required.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022complex-auth-flows\u0022\u003E\uD83D\uDD39 Complex Auth Flows\u003C/h3\u003E\n\u003Cp\u003ELogin, logout, refresh, PKCE, multi-device control etc.\u003C/p\u003E\n\u003Cp\u003EAll exposed as \u003Cstrong\u003Esimple application-level APIs\u003C/strong\u003E.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022when-to-use-ultimateauth\u0022\u003EWhen to Use UltimateAuth\u003C/h2\u003E\n\u003Cp\u003EUse UltimateAuth if:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EYou are building a modern .NET application \u003Cstrong\u003EBlazor Server, WASM or MAUI\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003EYou need \u003Cstrong\u003Esession \u002B token hybrid authentication\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003EYou want \u003Cstrong\u003Efull control over authentication flows\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003EYou are building a \u003Cstrong\u003Emulti-client system (web \u002B mobile \u002B API)\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003EYou need \u003Cstrong\u003Estrong security with extensibility\u003C/strong\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Continue to \u003Cstrong\u003EQuick Start\u003C/strong\u003E to build your first UltimateAuth application.\u003C/p\u003E\n", + "Html": "\n\u003Cp\u003EWelcome to \u003Cstrong\u003EUltimateAuth\u003C/strong\u003E, a unified authentication and authorization framework for modern .NET applications.\u003C/p\u003E\n\u003Cp\u003EAuthentication should not become a different system every time your application gains a new client, tenant, device, or security requirement. UltimateAuth brings these concerns into \u003Cstrong\u003Eone coherent model\u003C/strong\u003E\u2014without pretending that every application needs the same authentication strategy.\u003C/p\u003E\n\u003Cp\u003E\u003Cstrong\u003EOne framework. Multiple authentication models. A consistent security lifecycle.\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022what-is-ultimateauth\u0022\u003EWhat is UltimateAuth?\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth is a modular, flow-driven framework for building and managing authentication in .NET. It connects concepts that are often implemented separately:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u003Cstrong\u003ESessions, cookies and tokens\u003C/strong\u003E \u2014 stateful session management alongside access and refresh token workflows.\u003C/li\u003E\n\u003Cli\u003E\u003Cstrong\u003EAuthentication and authorization\u003C/strong\u003E \u2014 identity establishment and access decisions within a consistent architecture.\u003C/li\u003E\n\u003Cli\u003E\u003Cstrong\u003EUsers and credentials\u003C/strong\u003E \u2014 identity, identifiers, credentials, and their security state.\u003C/li\u003E\n\u003Cli\u003E\u003Cstrong\u003EDevices and session continuity\u003C/strong\u003E \u2014 device-aware session chains, refresh, logout, and revocation.\u003C/li\u003E\n\u003Cli\u003E\u003Cstrong\u003EClients and delivery mechanisms\u003C/strong\u003E \u2014 browser, API, Blazor, and mobile scenarios with client-aware behavior.\u003C/li\u003E\n\u003Cli\u003E\u003Cstrong\u003ETenants and isolation\u003C/strong\u003E \u2014 tenant-aware request context and authentication boundaries.\u003C/li\u003E\n\u003Cli\u003E\u003Cstrong\u003ESecurity policies and execution flows\u003C/strong\u003E \u2014 explicit decisions, validation, and controlled state transitions.\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EThe goal is not to hide these concepts. It is to make them \u003Cstrong\u003Ework together predictably\u003C/strong\u003E.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022unified-does-not-mean-one-size-fits-all\u0022\u003EUnified Does Not Mean One-Size-Fits-All\u003C/h2\u003E\n\u003Cp\u003EA server-rendered web application, a browser-based SPA, a mobile app, and a backend API do not share the same trust boundaries or credential transport requirements.\u003C/p\u003E\n\u003Cp\u003EUltimateAuth provides a common foundation while allowing behavior to be shaped by the \u003Cstrong\u003Eeffective authentication mode\u003C/strong\u003E, \u003Cstrong\u003Eclient profile\u003C/strong\u003E, \u003Cstrong\u003Etenant\u003C/strong\u003E, and \u003Cstrong\u003Erequest context\u003C/strong\u003E.\u003C/p\u003E\n\u003Ctable\u003E\n\u003Cthead\u003E\n\u003Ctr\u003E\n\u003Cth\u003EConcern\u003C/th\u003E\n\u003Cth\u003EWhat UltimateAuth brings together\u003C/th\u003E\n\u003Cth\u003EWhy it matters\u003C/th\u003E\n\u003C/tr\u003E\n\u003C/thead\u003E\n\u003Ctbody\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EAuthentication models\u003C/td\u003E\n\u003Ctd\u003ESession-oriented and token-oriented workflows\u003C/td\u003E\n\u003Ctd\u003EDifferent clients can participate in one security architecture\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003ESession lifecycle\u003C/td\u003E\n\u003Ctd\u003ERoot \u2192 Chain \u2192 Session\u003C/td\u003E\n\u003Ctd\u003EManage authentication at identity, device, and session scope\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EClient environments\u003C/td\u003E\n\u003Ctd\u003EASP.NET Core, Blazor, APIs, and .NET mobile clients\u003C/td\u003E\n\u003Ctd\u003EAvoid rebuilding the core authentication model for each frontend\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EMulti-tenancy\u003C/td\u003E\n\u003Ctd\u003ETenant resolution and tenant-bound context\u003C/td\u003E\n\u003Ctd\u003EKeep authentication operations within the intended tenant boundary\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003ECredentials and identities\u003C/td\u003E\n\u003Ctd\u003EUser identifiers, credential providers, and security state\u003C/td\u003E\n\u003Ctd\u003ESeparate \u003Cem\u003Ewho the user is\u003C/em\u003E from \u003Cem\u003Ehow they prove it\u003C/em\u003E\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003ESecurity enforcement\u003C/td\u003E\n\u003Ctd\u003EContext, validation, authority, and orchestration\u003C/td\u003E\n\u003Ctd\u003EMake decisions explicit rather than scattering checks across endpoints\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003ECredential delivery\u003C/td\u003E\n\u003Ctd\u003EMode- and client-aware response policies\u003C/td\u003E\n\u003Ctd\u003EHandle cookies, tokens, and redirects according to the scenario\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003ESession termination\u003C/td\u003E\n\u003Ctd\u003ELogout, targeted revocation, and device management\u003C/td\u003E\n\u003Ctd\u003ERespond proportionately to ordinary sign-out and security incidents\u003C/td\u003E\n\u003C/tr\u003E\n\u003C/tbody\u003E\n\u003C/table\u003E\n\u003Cp\u003E\u003Cstrong\u003EUnified\u003C/strong\u003E is therefore about a shared security model\u2014not forcing all applications to use identical cookies, tokens, or endpoints.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022authentication-is-more-than-issuing-a-token\u0022\u003EAuthentication Is More Than Issuing a Token\u003C/h2\u003E\n\u003Cp\u003EIn many applications, authentication is described as a short sequence: validate credentials, generate a token, return it to the client.\u003C/p\u003E\n\u003Cp\u003EThat is only part of the story.\u003C/p\u003E\n\u003Cp\u003EA production authentication system must also answer questions such as:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EWhich tenant does this request belong to?\u003C/li\u003E\n\u003Cli\u003EWhich user and credential are being evaluated?\u003C/li\u003E\n\u003Cli\u003EIs an existing session still valid?\u003C/li\u003E\n\u003Cli\u003EIs the request coming from a known device?\u003C/li\u003E\n\u003Cli\u003ECan access be renewed, restricted, or terminated?\u003C/li\u003E\n\u003Cli\u003EWhat should happen when a credential changes or a device is no longer trusted?\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EUltimateAuth treats authentication as a \u003Cstrong\u003Econtrolled lifecycle\u003C/strong\u003E. Login, refresh, and logout are operations within that lifecycle\u2014not unrelated endpoints that happen to exchange tokens.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022sessions-are-first-class\u0022\u003ESessions Are First-Class\u003C/h2\u003E\n\u003Cp\u003EWhere session-backed modes apply, UltimateAuth models authentication through a hierarchy:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EUser\n \u2514\u2500\u2500 Root Global session-security boundary\n \u2514\u2500\u2500 Chain Device-associated lifecycle\n \u2514\u2500\u2500 Session Individual authentication instance\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EThis structure gives the system a vocabulary for different security actions. A session can be invalidated, a device-associated chain can be terminated, and a broader security boundary can be revoked.\u003C/p\u003E\n\u003Cp\u003E\u003Cstrong\u003EA token is not the entire authentication model.\u003C/strong\u003E It is a credential used within that model, subject to the applicable mode and validation rules.\u003C/p\u003E\n\u003Cp\u003ENot every authentication mode has identical persistence or session behavior. The session hierarchy describes the stateful lifecycle where that model is in use.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022flow-driven-by-design\u0022\u003EFlow-Driven by Design\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth organizes authentication operations around explicit flows. A flow brings together the request context, operation-specific validation, security decisions, permitted state transitions, and response behavior.\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003ERequest\n \u2192 Tenant, session, and user resolution\n \u2192 AuthFlowContext\n \u2192 Flow-specific validation\n \u2192 Security decision and orchestration\n \u2192 State transition (when permitted)\n \u2192 Result and client-appropriate response\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EThis is not a claim that pipelines or orchestration are new inventions. The value is in applying them \u003Cstrong\u003Econsistently across authentication operations\u003C/strong\u003E, so the rules are easier to reason about, test, and extend.\u003C/p\u003E\n\u003Cp\u003EFor the implementation model, see \u003Ca href=\u0022../auth-flows/auth-flows.md\u0022\u003EAuth Flows\u003C/a\u003E.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022tenant-aware-from-the-request-boundary\u0022\u003ETenant-Aware from the Request Boundary\u003C/h2\u003E\n\u003Cp\u003EMulti-tenancy is not simply a \u003Ccode\u003ETenantId\u003C/code\u003E added to a user table. Authentication must understand \u003Cem\u003Ewhich tenant\u0027s security context\u003C/em\u003E a request is operating within.\u003C/p\u003E\n\u003Cp\u003EUltimateAuth resolves tenant context as part of the ASP.NET Core request pipeline and carries it into the authentication flow. The flow context then combines that boundary with client settings, device information, and applicable session validation.\u003C/p\u003E\n\u003Cp\u003EThis helps teams design multi-tenant applications without treating tenant isolation as an afterthought. \u003Cstrong\u003ETenant resolution and configuration still need to be correctly integrated by the host application.\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022built-for-different-clients-not-just-different-screens\u0022\u003EBuilt for Different Clients, Not Just Different Screens\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth is designed for applications that may span several environments:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u003Cstrong\u003EASP.NET Core web applications\u003C/strong\u003E with server-managed authentication concerns.\u003C/li\u003E\n\u003Cli\u003E\u003Cstrong\u003EBlazor Server and Blazor WebAssembly\u003C/strong\u003E with different hosting and transport constraints.\u003C/li\u003E\n\u003Cli\u003E\u003Cstrong\u003E.NET MAUI\u003C/strong\u003E and other native clients with device-oriented authentication needs.\u003C/li\u003E\n\u003Cli\u003E\u003Cstrong\u003EHTTP APIs\u003C/strong\u003E serving browsers, applications, and other services.\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EClient profiles and effective server options help adapt authentication behavior to the requesting client. Response policies determine how applicable credentials are delivered and whether a redirect belongs in the interaction.\u003C/p\u003E\n\u003Cp\u003EThe result is a common backend security vocabulary across multiple application types\u2014not a promise that every client uses the same transport or requires zero configuration.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022security-is-a-lifecycle-too\u0022\u003ESecurity Is a Lifecycle, Too\u003C/h2\u003E\n\u003Cp\u003ESecurity is not finished when login succeeds. UltimateAuth\u0027s architecture accounts for what happens \u003Cem\u003Eafter\u003C/em\u003E access is established:\u003C/p\u003E\n\u003Ctable\u003E\n\u003Cthead\u003E\n\u003Ctr\u003E\n\u003Cth\u003ESituation\u003C/th\u003E\n\u003Cth\u003ESecurity question\u003C/th\u003E\n\u003Cth\u003EUltimateAuth concept\u003C/th\u003E\n\u003C/tr\u003E\n\u003C/thead\u003E\n\u003Ctbody\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EA user signs in\u003C/td\u003E\n\u003Ctd\u003EShould this attempt establish authentication?\u003C/td\u003E\n\u003Ctd\u003ELogin flow and security decisions\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EA session needs renewal\u003C/td\u003E\n\u003Ctd\u003ECan continuity be preserved safely?\u003C/td\u003E\n\u003Ctd\u003ERefresh flow and session lifecycle\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EA user signs out\u003C/td\u003E\n\u003Ctd\u003EWhich authentication scope should end?\u003C/td\u003E\n\u003Ctd\u003ELogout\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EA device is lost\u003C/td\u003E\n\u003Ctd\u003EHow can that device\u0027s access be terminated?\u003C/td\u003E\n\u003Ctd\u003EChain-level management and revocation\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EA credential is compromised\u003C/td\u003E\n\u003Ctd\u003EHow should existing authentication be affected?\u003C/td\u003E\n\u003Ctd\u003ECredential security state and revocation policies\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EAn administrator manages access\u003C/td\u003E\n\u003Ctd\u003EIs the operation permitted for this actor and target?\u003C/td\u003E\n\u003Ctd\u003EAuthorization and access context\u003C/td\u003E\n\u003C/tr\u003E\n\u003C/tbody\u003E\n\u003C/table\u003E\n\u003Cp\u003EA key distinction is \u003Cstrong\u003Elogout versus revoke\u003C/strong\u003E. Ordinary logout ends authentication according to its scope; revocation is an irreversible invalidation of the targeted security state. Signing in again may establish \u003Cem\u003Enew\u003C/em\u003E authentication, but it does not restore what was revoked.\u003C/p\u003E\n\u003Cp\u003EExplore the details in \u003Ca href=\u0022../auth-flows/login-flow.md\u0022\u003ELogin Flow\u003C/a\u003E, \u003Ca href=\u0022../auth-flows/refresh-flow.md\u0022\u003ERefresh Flow\u003C/a\u003E, and \u003Ca href=\u0022../auth-flows/logout-flow.md\u0022\u003ELogout Flow\u003C/a\u003E.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022modular-and-extensible\u0022\u003EModular and Extensible\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth is intended to serve both straightforward applications and systems with more demanding security requirements. Its architecture separates responsibilities such as core contracts, server execution, client integration, user management, credentials, and authorization.\u003C/p\u003E\n\u003Cp\u003EThis separation makes it possible to understand and evolve individual concerns without reducing authentication to a collection of disconnected custom handlers.\u003C/p\u003E\n\u003Cp\u003E\u003Cstrong\u003EExtensible does not mean every possible flow is already implemented.\u003C/strong\u003E Available modes, integrations, and capabilities should be evaluated against the version you are using; the documentation for each feature describes its current behavior and limitations.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022when-should-you-choose-ultimateauth\u0022\u003EWhen Should You Choose UltimateAuth?\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth is especially relevant when:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EYou need quickly-installed, consistent, secure and easy-to-use auth in your .NET application.\u003C/li\u003E\n\u003Cli\u003EYour .NET application has\u2014or will have\u2014\u003Cstrong\u003Emore than one client type\u003C/strong\u003E.\u003C/li\u003E\n\u003Cli\u003EYou need \u003Cstrong\u003Edevice-aware session management\u003C/strong\u003E, not only token generation.\u003C/li\u003E\n\u003Cli\u003EYou are designing a \u003Cstrong\u003Emulti-tenant\u003C/strong\u003E system with clear authentication boundaries.\u003C/li\u003E\n\u003Cli\u003EYou want \u003Cstrong\u003Eauthentication and authorization\u003C/strong\u003E to follow a consistent architectural model.\u003C/li\u003E\n\u003Cli\u003EYou need control over \u003Cstrong\u003Erefresh, logout, revocation, and credential lifecycle\u003C/strong\u003E.\u003C/li\u003E\n\u003Cli\u003EYou value a \u003Cstrong\u003Emodular, self-hosted, open-source\u003C/strong\u003E foundation you can understand and extend.\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EAlso for a small application with minimal authentication needs, UltimateAuth brings the simplest possible solution.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022open-source-first\u0022\u003EOpen Source First\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth is built with a simple commitment: \u003Cstrong\u003Eself-hosted authentication should remain accessible\u003C/strong\u003E. The framework is open source, and self-hosting is intended to remain free. A hosted offering may provide an operational alternative, not a separate tier of authentication features.\u003C/p\u003E\n\u003Cp\u003EThe ambition is to build a framework that developers can adopt, inspect, contribute to, and trust\u2014not merely a service they must depend on.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022where-to-go-next\u0022\u003EWhere to Go Next\u003C/h2\u003E\n\u003Cp\u003EYou do not need to understand every internal component before getting started.\u003C/p\u003E\n\u003Cp\u003E\u003Cstrong\u003EContinue to Quick Start\u003C/strong\u003E to set up your first UltimateAuth application. Then explore \u003Ca href=\u0022../auth-flows/auth-flows.md\u0022\u003EAuth Flows\u003C/a\u003E to understand how the pieces work together.\u003C/p\u003E\n\u003Cp\u003E\u003Cstrong\u003EThe idea to remember:\u003C/strong\u003E UltimateAuth unifies authentication concepts without erasing their differences. One framework, a consistent lifecycle, and room for the security model your application actually needs.\u003C/p\u003E\n", "Headings": [ { "Id": "what-is-ultimateauth", @@ -9,58 +9,58 @@ "Level": 0 }, { - "Id": "what-makes-ultimateauth-different", - "Text": "What Makes UltimateAuth Different?", + "Id": "unified-does-not-mean-one-size-fits-all", + "Text": "Unified Does Not Mean One-Size-Fits-All", "Level": 0 }, { - "Id": "session-is-a-first-class-concept", - "Text": "\uD83D\uDD39 Session is a First-Class Concept", - "Level": 1 + "Id": "authentication-is-more-than-issuing-a-token", + "Text": "Authentication Is More Than Issuing a Token", + "Level": 0 }, { - "Id": "flow-based-not-token-based", - "Text": "\uD83D\uDD39 Flow-Based, Not Token-Based", - "Level": 1 + "Id": "sessions-are-first-class", + "Text": "Sessions Are First-Class", + "Level": 0 }, { - "Id": "built-for-blazor-and-modern-clients", - "Text": "\uD83D\uDD39 Built for Blazor and Modern Clients", - "Level": 1 + "Id": "flow-driven-by-design", + "Text": "Flow-Driven by Design", + "Level": 0 }, { - "Id": "runtime-aware-authentication", - "Text": "\uD83D\uDD39 Runtime-Aware Authentication", - "Level": 1 + "Id": "tenant-aware-from-the-request-boundary", + "Text": "Tenant-Aware from the Request Boundary", + "Level": 0 }, { - "Id": "what-problems-it-solves", - "Text": "What Problems It Solves", + "Id": "built-for-different-clients-not-just-different-screens", + "Text": "Built for Different Clients, Not Just Different Screens", "Level": 0 }, { - "Id": "multiple-client-types", - "Text": "\uD83D\uDD39 Multiple Client Types", - "Level": 1 + "Id": "security-is-a-lifecycle-too", + "Text": "Security Is a Lifecycle, Too", + "Level": 0 }, { - "Id": "session-vs-token-confusion", - "Text": "\uD83D\uDD39 Session vs Token Confusion", - "Level": 1 + "Id": "modular-and-extensible", + "Text": "Modular and Extensible", + "Level": 0 }, { - "Id": "secure-session-management", - "Text": "\uD83D\uDD39 Secure Session Management", - "Level": 1 + "Id": "when-should-you-choose-ultimateauth", + "Text": "When Should You Choose UltimateAuth?", + "Level": 0 }, { - "Id": "complex-auth-flows", - "Text": "\uD83D\uDD39 Complex Auth Flows", - "Level": 1 + "Id": "open-source-first", + "Text": "Open Source First", + "Level": 0 }, { - "Id": "when-to-use-ultimateauth", - "Text": "When to Use UltimateAuth", + "Id": "where-to-go-next", + "Text": "Where to Go Next", "Level": 0 } ] diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/getting-started/quickstart.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/getting-started/quickstart.json index 49dd73b0..c2877c46 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/getting-started/quickstart.json +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/getting-started/quickstart.json @@ -1,7 +1,7 @@ { "Slug": "getting-started/quickstart", "Title": "QuickStart", - "Html": "\n\u003Cp\u003EIn this guide, you will set up UltimateAuth in a few minutes and perform your \u003Cstrong\u003Efirst login\u003C/strong\u003E.\u003C/p\u003E\n\u003Chr /\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022create-a-project\u0022\u003E1. Create a Project\u003C/h2\u003E\n\u003Cp\u003EStart by creating a new Blazor app:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-bash\u0022\u003Edotnet new blazorserver -n UltimateAuthDemo\ncd UltimateAuthDemo\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022install-packages\u0022\u003E2. Install Packages\u003C/h2\u003E\n\u003Cp\u003EInstall the required UltimateAuth packages:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Edotnet add package CodeBeam.UltimateAuth.Client.Blazor\ndotnet add package CodeBeam.UltimateAuth.InMemory.Bundle\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022configure-services\u0022\u003E3. Configure Services\u003C/h2\u003E\n\u003Cp\u003EUpdate your Program.cs:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Ebuilder.Services\n .AddUltimateAuthServer()\n .AddUltimateAuthInMemory();\n\nbuilder.Services\n .AddUltimateAuthClientBlazor();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022configure-middleware\u0022\u003E4. Configure Middleware\u003C/h2\u003E\n\u003Cp\u003EIn \u003Ccode\u003EProgram.cs\u003C/code\u003E\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eapp.UseUltimateAuthWithAspNetCore();\napp.MapUltimateAuthEndpoints();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022enable-blazor-integration\u0022\u003E5. Enable Blazor Integration\u003C/h2\u003E\n\u003Cp\u003EIn \u003Ccode\u003EProgram.cs\u003C/code\u003E\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eapp.MapRazorComponents\u0026lt;App\u0026gt;()\n .AddInteractiveServerRenderMode() // or webassembly (depends on your application type)\n .AddUltimateAuthRoutes(UAuthAssemblies.BlazorClient());\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022add-uauth-script\u0022\u003E6. Add UAuth Script\u003C/h2\u003E\n\u003Cp\u003EAdd this to \u003Ccode\u003EApp.razor\u003C/code\u003E or \u003Ccode\u003Eindex.html\u003C/code\u003E:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003E\u0026lt;script src=\u0026quot;_content/CodeBeam.UltimateAuth.Client.Blazor/uauth.min.js\u0026quot;\u0026gt;\u0026lt;/script\u0026gt;\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022configure-application-lifecycle\u0022\u003E7. Configure Application Lifecycle\u003C/h2\u003E\n\u003Cp\u003EReplace \u003Ccode\u003ERoutes.razor\u003C/code\u003E with this code:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003E\u0026lt;UAuthApp UseBuiltInRouter=\u0026quot;true\u0026quot; AppAssembly=\u0026quot;typeof(Program).Assembly\u0026quot; DefaultLayout=\u0026quot;typeof(Layout.MainLayout)\u0026quot; /\u0026gt;\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022recommended-setup-optional\u0022\u003E8. Recommended Setup (Optional)\u003C/h2\u003E\n\u003Cp\u003EAdd these for better experience:\u003C/p\u003E\n\u003Cp\u003EFor login page (Use this only once in your application)\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003E@attribute [UAuthLoginPage]\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EFor protected pages\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003E@attribute [UAuthAuthorize]\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EFor any page that you use UltimateAuth features like AuthState etc.\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003E@inherits UAuthPageBase\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022seed-data-for-quickstart-optional\u0022\u003E9. Seed Data For QuickStart (Optional)\u003C/h2\u003E\n\u003Cp\u003EThis code creates admin and user users with same password and admin role.\u003C/p\u003E\n\u003Cp\u003EFor in memory\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Ebuilder.Services.AddUltimateAuthSampleSeed();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EFor entity framework core:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Ebuilder.Services.AddScopedUltimateAuthSampleSeed();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EIn pipeline configuration\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eif (app.Environment.IsDevelopment())\n{\n await app.SeedUltimateAuthAsync();\n}\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022perform-your-first-login\u0022\u003E10. Perform Your First Login\u003C/h2\u003E\n\u003Cp\u003EExample using IUAuthClient:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003E[Inject] IUAuthClient UAuthClient { get; set; } = null!;\n\nprivate async Task Login()\n{\n await UAuthClient.Flows.LoginAsync(new LoginRequest\n {\n Identifier = \u0026quot;admin\u0026quot;,\n Secret = \u0026quot;admin\u0026quot;\n });\n}\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022thats-it\u0022\u003E\uD83C\uDF89 That\u2019s It\u003C/h2\u003E\n\u003Cp\u003EYou now have a working authentication system with:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession-based authentication\u003C/li\u003E\n\u003Cli\u003EAutomatic client detection\u003C/li\u003E\n\u003Cli\u003EBuilt-in login flow\u003C/li\u003E\n\u003Cli\u003ESecure session handling\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022what-just-happened\u0022\u003EWhat Just Happened?\u003C/h2\u003E\n\u003Cp\u003EWhen you logged in:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EA session (with root and chain) was created on the server,\u003C/li\u003E\n\u003Cli\u003EYour client received an authentication grant (cookie or token),\u003C/li\u003E\n\u003Cli\u003EUltimateAuth established your auth state automatically.\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 You didn\u2019t manage cookies, tokens, or redirects manually.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022next-steps\u0022\u003ENext Steps\u003C/h2\u003E\n\u003Cp\u003EDiscover the setup for real world applications with entity framework core.\u003C/p\u003E\n", + "Html": "\n\u003Cp\u003EIn this guide, you will set up UltimateAuth in a few minutes and perform your \u003Cstrong\u003Efirst login\u003C/strong\u003E.\u003C/p\u003E\n\u003Chr /\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022create-a-project\u0022\u003E1. Create a Project\u003C/h2\u003E\n\u003Cp\u003EStart by creating a new Blazor app:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-bash\u0022\u003Edotnet new blazorserver -n UltimateAuthDemo\ncd UltimateAuthDemo\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022install-packages\u0022\u003E2. Install Packages\u003C/h2\u003E\n\u003Cp\u003EInstall the required UltimateAuth packages:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Edotnet add package CodeBeam.UltimateAuth.Client.Blazor\ndotnet add package CodeBeam.UltimateAuth.InMemory.Bundle\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022configure-services\u0022\u003E3. Configure Services\u003C/h2\u003E\n\u003Cp\u003EUpdate your Program.cs:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Ebuilder.Services\n .AddUltimateAuthServer()\n .AddUltimateAuthInMemory();\n\nbuilder.Services\n .AddUltimateAuthClientBlazor();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022configure-middleware\u0022\u003E4. Configure Middleware\u003C/h2\u003E\n\u003Cp\u003EIn \u003Ccode\u003EProgram.cs\u003C/code\u003E\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eapp.UseUltimateAuthWithAspNetCore();\napp.MapUltimateAuthEndpoints();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022enable-blazor-integration\u0022\u003E5. Enable Blazor Integration\u003C/h2\u003E\n\u003Cp\u003EIn \u003Ccode\u003EProgram.cs\u003C/code\u003E\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eapp.MapRazorComponents\u0026lt;App\u0026gt;()\n .AddInteractiveServerRenderMode() // or webassembly (depends on your application type)\n .AddUltimateAuthRoutes(UAuthAssemblies.BlazorClient());\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022add-uauth-script\u0022\u003E6. Add UAuth Script\u003C/h2\u003E\n\u003Cp\u003EAdd this to \u003Ccode\u003EApp.razor\u003C/code\u003E or \u003Ccode\u003Eindex.html\u003C/code\u003E:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003E\u0026lt;script src=\u0026quot;_content/CodeBeam.UltimateAuth.Client.Blazor/uauth.min.js\u0026quot;\u0026gt;\u0026lt;/script\u0026gt;\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022configure-application-lifecycle\u0022\u003E7. Configure Application Lifecycle\u003C/h2\u003E\n\u003Cp\u003EReplace \u003Ccode\u003ERoutes.razor\u003C/code\u003E with this code:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003E\u0026lt;UAuthApp UseBuiltInRouter=\u0026quot;true\u0026quot; AppAssembly=\u0026quot;typeof(Program).Assembly\u0026quot; DefaultLayout=\u0026quot;typeof(Layout.MainLayout)\u0026quot; /\u0026gt;\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022recommended-setup-optional\u0022\u003E8. Recommended Setup (Optional)\u003C/h2\u003E\n\u003Cp\u003EAdd these for better experience:\u003C/p\u003E\n\u003Cp\u003EFor login page (Use this only once in your application)\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003E@attribute [UAuthLoginPage]\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EFor protected pages\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003E@attribute [UAuthAuthorize]\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EFor any page that you use UltimateAuth features like AuthState etc.\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003E@inherits UAuthPageBase\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022seed-data-for-quickstart-optional\u0022\u003E9. Seed Data For QuickStart (Optional)\u003C/h2\u003E\n\u003Cp\u003EThis code creates admin and user users with same password and admin role.\u003C/p\u003E\n\u003Cp\u003EFor in memory\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Ebuilder.Services.AddUltimateAuthSampleSeed();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EFor entity framework core, use scoped services:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Ebuilder.Services.AddScopedUltimateAuthSampleSeed();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EIn pipeline configuration\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eif (app.Environment.IsDevelopment())\n{\n await app.SeedUltimateAuthAsync();\n}\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022perform-your-first-login\u0022\u003E10. Perform Your First Login\u003C/h2\u003E\n\u003Cp\u003EExample using IUAuthClient:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003E[Inject] IUAuthClient UAuthClient { get; set; } = null!;\n\nprivate async Task Login()\n{\n await UAuthClient.Flows.LoginAsync(new LoginRequest\n {\n Identifier = \u0026quot;admin\u0026quot;,\n Secret = \u0026quot;admin\u0026quot;\n });\n}\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022thats-it\u0022\u003E\uD83C\uDF89 That\u2019s It\u003C/h2\u003E\n\u003Cp\u003EYou now have a working authentication system with:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession-based authentication\u003C/li\u003E\n\u003Cli\u003EAutomatic client detection\u003C/li\u003E\n\u003Cli\u003EBuilt-in login flow\u003C/li\u003E\n\u003Cli\u003ESecure session handling\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022what-just-happened\u0022\u003EWhat Just Happened?\u003C/h2\u003E\n\u003Cp\u003EWhen you logged in:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EA session (with root and chain) was created on the server,\u003C/li\u003E\n\u003Cli\u003EYour client received an authentication grant (cookie or token),\u003C/li\u003E\n\u003Cli\u003EUltimateAuth established your auth state automatically.\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 You didn\u2019t manage cookies, tokens, or redirects manually.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022next-steps\u0022\u003ENext Steps\u003C/h2\u003E\n\u003Cp\u003EDiscover the setup for real world applications with entity framework core.\u003C/p\u003E\n", "Headings": [ { "Id": "create-a-project", diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm/DocIndexEntry.cs b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm/DocIndexEntry.cs new file mode 100644 index 00000000..9ddb56eb --- /dev/null +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm/DocIndexEntry.cs @@ -0,0 +1,3 @@ +ο»Ώnamespace CodeBeam.UltimateAuth.Docs.Wasm; + +public sealed record DocIndexEntry(string Title, string Slug); diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm/Program.cs b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm/Program.cs index 01e3ec41..47e16c65 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm/Program.cs +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm/Program.cs @@ -1,7 +1,10 @@ +using CodeBeam.UltimateAuth.Docs.Wasm; using CodeBeam.UltimateAuth.Docs.Wasm.Client.Pages; using CodeBeam.UltimateAuth.Docs.Wasm.Components; using MudBlazor.Services; using MudExtensions.Services; +using System.Text.Json; +using System.Xml.Linq; var builder = WebApplication.CreateBuilder(args); @@ -35,4 +38,48 @@ .AddInteractiveWebAssemblyRenderMode() .AddAdditionalAssemblies(typeof(CodeBeam.UltimateAuth.Docs.Wasm.Client._Imports).Assembly); +app.MapGet("/sitemap.xml", async (IWebHostEnvironment env) => +{ + var file = env.WebRootFileProvider.GetFileInfo("docs/docs-index.json"); + + if (!file.Exists) + return Results.NotFound(); + + using var stream = file.CreateReadStream(); + + var pages = await JsonSerializer.DeserializeAsync>( + stream, + new JsonSerializerOptions + { + PropertyNameCaseInsensitive = true + }) ?? []; + + const string baseUrl = "https://ultimateauth.com"; + + XNamespace ns = "http://www.sitemaps.org/schemas/sitemap/0.9"; + + var urls = new List + { + $"{baseUrl}/", + $"{baseUrl}/docs" + }; + + urls.AddRange(pages.Where(x => !string.IsNullOrWhiteSpace(x.Slug)).Select(x => $"{baseUrl}/docs/{x.Slug.Trim('/')}")); + + var document = new XDocument( + new XDeclaration("1.0", "UTF-8", null), + new XElement( + ns + "urlset", + urls.Distinct().Select(url => + new XElement( + ns + "url", + new XElement(ns + "loc", url) + ) + ) + ) + ); + + return Results.Text(document.ToString(), "application/xml"); +}); + app.Run(); diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm/wwwroot/robots.txt b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm/wwwroot/robots.txt new file mode 100644 index 00000000..67e29b06 --- /dev/null +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm/wwwroot/robots.txt @@ -0,0 +1,4 @@ +ο»ΏUser-agent: * +Allow: / + +Sitemap: https://ultimateauth.com/sitemap.xml \ No newline at end of file From c501240cae36df559fa7816a4c77ea77e3d6a088 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20Can=20Karag=C3=B6z?= Date: Tue, 13 Oct 2026 18:08:23 +0300 Subject: [PATCH 4/7] Docs Content Static Serve --- .../Pages/DocsContent.razor | 15 ++++++++++++++- .../CodeBeam.UltimateAuth.Docs.Wasm/Program.cs | 12 ++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsContent.razor b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsContent.razor index d39df8c7..417ce5b2 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsContent.razor +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsContent.razor @@ -4,6 +4,13 @@ @inject NavigationManager Nav @inject IJSRuntime JS +@if (_loading) +{ +
+ +
+} + @if (_notFound) { @@ -98,8 +105,14 @@ else if (_doc is not null) ComputePrevNext(); } } - catch + // catch + // { + // _notFound = true; + // } + catch (Exception ex) { + Console.WriteLine($"[Docs SSR] Failed to load '{slug}': {ex}"); + _notFound = true; } diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm/Program.cs b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm/Program.cs index 47e16c65..11c9d7fd 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm/Program.cs +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm/Program.cs @@ -1,6 +1,7 @@ using CodeBeam.UltimateAuth.Docs.Wasm; using CodeBeam.UltimateAuth.Docs.Wasm.Client.Pages; using CodeBeam.UltimateAuth.Docs.Wasm.Components; +using Microsoft.AspNetCore.Components; using MudBlazor.Services; using MudExtensions.Services; using System.Text.Json; @@ -15,6 +16,17 @@ builder.Services.AddMudExtensions(); builder.Services.AddHttpClient(); + +builder.Services.AddScoped(sp => +{ + var navigation = sp.GetRequiredService(); + + return new HttpClient + { + BaseAddress = new Uri(navigation.BaseUri) + }; +}); + builder.Services.AddScoped(); var app = builder.Build(); From 8fccfd8b9b41c00d04f47aaea0b2386dc740c592 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20Can=20Karag=C3=B6z?= Date: Tue, 13 Oct 2026 19:11:48 +0300 Subject: [PATCH 5/7] SSR to Interactive Switch Smoothness Improve --- .../Layout/MainLayout.razor | 2 +- .../Layout/MainLayout.razor.cs | 5 ++ .../Pages/DocsContent.razor | 61 ++++++++++--- .../Pages/DocsSidebar.razor | 86 ++++++++++++++++--- .../Program.cs | 21 +++++ 5 files changed, 150 insertions(+), 25 deletions(-) diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Layout/MainLayout.razor b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Layout/MainLayout.razor index 4fe38e2d..cd7609ba 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Layout/MainLayout.razor +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Layout/MainLayout.razor @@ -19,7 +19,7 @@
- Docs + Docs Samples Donate
diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Layout/MainLayout.razor.cs b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Layout/MainLayout.razor.cs index 2f73f6f5..f737b8a0 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Layout/MainLayout.razor.cs +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Layout/MainLayout.razor.cs @@ -36,4 +36,9 @@ public void SetVisionOverlay(bool value) Background = "#0C1618" }, }; + + public void DocsForceLoad() + { + Nav.NavigateTo("/docs", forceLoad: true); + } } diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsContent.razor b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsContent.razor index 417ce5b2..fa5fbf24 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsContent.razor +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsContent.razor @@ -3,6 +3,8 @@ @inject HttpClient Http @inject NavigationManager Nav @inject IJSRuntime JS +@implements IDisposable +@inject PersistentComponentState PersistentState @if (_loading) { @@ -13,10 +15,20 @@ @if (_notFound) { - - - Document not found. - + + + + Page not found + + + + The documentation page you're looking for doesn't exist. + + + + Back to documentation + + } else if (_doc is not null) @@ -72,11 +84,17 @@ else if (_doc is not null) [Parameter] public string? Slug { get; set; } + private PersistingComponentStateSubscription _subscription; private DocPage? _doc; private bool _loading = true; private bool _notFound; private ElementReference _contentRef; + protected override void OnInitialized() + { + _subscription = PersistentState.RegisterOnPersisting(PersistDocAsync); + } + protected override async Task OnParametersSetAsync() { _loading = true; @@ -92,7 +110,14 @@ else if (_doc is not null) try { - _doc = await Http.GetFromJsonAsync($"docs/{slug}.json"); + if (PersistentState.TryTakeFromJson($"docs-page:{Slug}", out var cachedDoc) && cachedDoc is not null) + { + _doc = cachedDoc; + } + else + { + _doc = await Http.GetFromJsonAsync($"docs/{slug}.json"); + } if (_doc == null) { @@ -105,14 +130,13 @@ else if (_doc is not null) ComputePrevNext(); } } - // catch - // { - // _notFound = true; - // } + catch (HttpRequestException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound) + { + _notFound = true; + } catch (Exception ex) { - Console.WriteLine($"[Docs SSR] Failed to load '{slug}': {ex}"); - + Console.WriteLine($"[Docs SSR] {slug}: {ex.Message}"); _notFound = true; } @@ -127,6 +151,16 @@ else if (_doc is not null) } } + private Task PersistDocAsync() + { + if (_doc is not null) + { + PersistentState.PersistAsJson($"docs-page:{Slug}", _doc); + } + + return Task.CompletedTask; + } + private sealed class DocPage { public string Slug { get; set; } = ""; @@ -200,4 +234,9 @@ else if (_doc is not null) if (index >= 0 && index < _nav.Count - 1) _next = _nav[index + 1]; } + + public void Dispose() + { + _subscription.Dispose(); + } } diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsSidebar.razor b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsSidebar.razor index 3d4e23a9..cce9c3d4 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsSidebar.razor +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsSidebar.razor @@ -1,5 +1,8 @@ ο»Ώ@inject HttpClient Http @inject NavigationManager Nav +@using System.Net.Http.Json +@implements IDisposable +@inject PersistentComponentState PersistentState
@if (!Inline) @@ -12,7 +15,18 @@ - @if (_groups == null) + @if (!RendererInfo.IsInteractive) + { + + } + + @if (_loadFailed) + { + + Navigation could not be loaded. + + } + else if (_groups is null) { } @@ -36,26 +50,67 @@ @code { private Dictionary>? _groups; private bool _expanded = false; + private bool _loadFailed; + + private PersistingComponentStateSubscription _subscription; + + private string SidebarStateKey => Inline ? "docs-sidebar-inline" : "docs-sidebar-default"; [Parameter] public bool Inline { get; set; } - protected override async Task OnAfterRenderAsync(bool firstRender) + protected override void OnInitialized() { - if (!firstRender) - return; + _subscription = PersistentState.RegisterOnPersisting(PersistSidebarAsync); + } + + protected override async Task OnInitializedAsync() + { + try + { + List items; + + if (!PersistentState.TryTakeFromJson>(SidebarStateKey, out var cachedItems) || cachedItems is null) + { + items = await Http.GetFromJsonAsync>("docs/docs-index.json") ?? []; + } + else + { + items = cachedItems; + } + + _items = items; - var items = await Http.GetFromJsonAsync>("/docs/docs-index.json") ?? []; + _groups = items + .GroupBy(x => x.Group) + .OrderBy(g => g.Min(x => x.GroupOrder)) + .ToDictionary( + g => g.Key, + g => g.OrderBy(x => x.Order).ToList() + ); + } + catch (HttpRequestException ex) + { + Console.WriteLine($"Docs sidebar failed: {ex.Message}"); + _loadFailed = true; + } + catch (OperationCanceledException ex) + { + Console.WriteLine($"Docs sidebar request canceled: {ex.Message}"); + _loadFailed = true; + } + } - _groups = items - .GroupBy(x => x.Group) - .OrderBy(g => g.Min(x => x.GroupOrder)) - .ToDictionary( - g => g.Key, - g => g.OrderBy(x => x.Order).ToList() - ); + private List? _items; - StateHasChanged(); + private Task PersistSidebarAsync() + { + if (_items is not null) + { + PersistentState.PersistAsJson(SidebarStateKey, _items); + } + + return Task.CompletedTask; } private string GetItemClass(DocIndexItem item) @@ -76,6 +131,11 @@ .ToTitleCase(group.Replace("-", " ")); } + public void Dispose() + { + _subscription.Dispose(); + } + private class DocIndexItem { public string Title { get; set; } = ""; diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm/Program.cs b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm/Program.cs index 11c9d7fd..82a4cab5 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm/Program.cs +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm/Program.cs @@ -45,6 +45,27 @@ app.UseAntiforgery(); +app.Use(async (context, next) => +{ + var path = context.Request.Path.Value; + + if (path is not null && + path.StartsWith("/docs/", StringComparison.OrdinalIgnoreCase) && + path.EndsWith(".json", StringComparison.OrdinalIgnoreCase)) + { + var asset = app.Environment.WebRootFileProvider + .GetFileInfo(path.TrimStart('/')); + + if (!asset.Exists) + { + context.Response.StatusCode = StatusCodes.Status404NotFound; + return; + } + } + + await next(context); +}); + app.MapStaticAssets(); app.MapRazorComponents() .AddInteractiveWebAssemblyRenderMode() From e0c8070e67dc1cf3d9b39bd5e697c13f181bd27e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20Can=20Karag=C3=B6z?= Date: Tue, 13 Oct 2026 21:14:41 +0300 Subject: [PATCH 6/7] Docs Loading --- .../Layout/MainLayout.razor | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Layout/MainLayout.razor b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Layout/MainLayout.razor index cd7609ba..bf52e093 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Layout/MainLayout.razor +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Layout/MainLayout.razor @@ -25,14 +25,20 @@
-
+
+
- - - + + + + + + + + +
From 8bf50ab66648199657f550a67eb6ae6147c976c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20Can=20Karag=C3=B6z?= Date: Tue, 13 Oct 2026 22:38:04 +0300 Subject: [PATCH 7/7] Design Improvements --- docs/content/getting-started/ecosystem.md | 86 ++++---- .../Layout/MainLayout.razor | 54 +++-- .../Layout/MainLayout.razor.cs | 53 ++++- .../Pages/DocsContent.razor | 7 - .../Pages/DocsSidebar.razor | 20 +- .../Pages/DocsToc.razor | 3 +- .../docs/getting-started/ecosystem.json | 114 ++++++++--- .../wwwroot/app.css | 192 +++++++++++++++++- 8 files changed, 410 insertions(+), 119 deletions(-) diff --git a/docs/content/getting-started/ecosystem.md b/docs/content/getting-started/ecosystem.md index 3e1d1aae..953dfcad 100644 --- a/docs/content/getting-started/ecosystem.md +++ b/docs/content/getting-started/ecosystem.md @@ -79,9 +79,9 @@ The internal architecture is modular, while the installation experience is delib --- -# Core +## Core -## `CodeBeam.UltimateAuth.Core` +#### `CodeBeam.UltimateAuth.Core` Core is the foundation of the entire UltimateAuth platform. @@ -105,7 +105,7 @@ Most application developers do not need to install Core directly. It is normally --- -# Server +## Server The Server side is the authority that executes UltimateAuth authentication and identity behavior. @@ -127,7 +127,7 @@ Other essential server capabilities, including authentication, sessions, and tok This gives UltimateAuth a modular internal architecture without forcing that complexity onto application developers. -## Do I install `CodeBeam.UltimateAuth.Server` directly? +### Do I install `CodeBeam.UltimateAuth.Server` directly? For normal application setup, **no**. @@ -156,7 +156,7 @@ This is an important part of the UltimateAuth design: --- -# Server Bundles +## Server Bundles Bundles are server-side composition packages. @@ -164,9 +164,9 @@ They collect the UltimateAuth server capabilities and the implementations requir You normally choose **one server bundle**. -## Reference Bundle +### Reference Bundle -### `CodeBeam.UltimateAuth.Reference.Bundle` +#### `CodeBeam.UltimateAuth.Reference.Bundle` The Reference Bundle provides the recommended UltimateAuth server composition without selecting a persistence implementation for your application. @@ -188,9 +188,9 @@ This is the lowest-level recommended server composition for applications that wa It is also an important extensibility point: UltimateAuth provides a recommended architecture without requiring applications to use one particular persistence technology. -## InMemory Bundle +### InMemory Bundle -### `CodeBeam.UltimateAuth.InMemory.Bundle` +#### `CodeBeam.UltimateAuth.InMemory.Bundle` The InMemory Bundle provides a complete server setup backed by UltimateAuth's in-memory implementations. @@ -213,9 +213,9 @@ InMemory.Bundle This is the easiest way to start an UltimateAuth server. -## Entity Framework Core Bundle +### Entity Framework Core Bundle -### `CodeBeam.UltimateAuth.EntityFrameworkCore.Bundle` +#### `CodeBeam.UltimateAuth.EntityFrameworkCore.Bundle` The Entity Framework Core Bundle provides the complete server composition using UltimateAuth's EF Core persistence implementations. @@ -235,7 +235,7 @@ The bundle performs that composition. --- -# Why Bundles Matter +### Why Bundles Matter A highly modular framework can easily make the developer experience worse if every application has to understand and install its internal package graph. @@ -271,7 +271,7 @@ You should not need to understand the complete internal dependency graph before --- -# Client +## Client The Client side has a different packaging model. @@ -295,7 +295,7 @@ dotnet add package CodeBeam.UltimateAuth.Client.Blazor A future MAUI integration can follow the same model without changing the underlying UltimateAuth Client architecture. -## `CodeBeam.UltimateAuth.Client` +### `CodeBeam.UltimateAuth.Client` `CodeBeam.UltimateAuth.Client` is the shared client foundation. @@ -307,7 +307,7 @@ Most applications should therefore install their platform-specific client packag --- -## `CodeBeam.UltimateAuth.Client.Blazor` +### `CodeBeam.UltimateAuth.Client.Blazor` `CodeBeam.UltimateAuth.Client.Blazor` is the concrete UltimateAuth client integration for Blazor. @@ -341,13 +341,13 @@ This model keeps the shared Client architecture independent from Blazor while st --- -# Client Compatibility Packages +### Client Compatibility Packages Not every client-side package represents a complete client implementation. Some packages exist to bridge UltimateAuth with a host framework. -## `CodeBeam.UltimateAuth.Client.AspNetCore` +#### `CodeBeam.UltimateAuth.Client.AspNetCore` `CodeBeam.UltimateAuth.Client.AspNetCore` is a lightweight ASP.NET Core compatibility layer for UltimateAuth client applications. @@ -359,11 +359,11 @@ It can enable compatibility with ASP.NET Core features such as `[Authorize]`, bu --- -# Application Composition +## Application Composition Because Server and Client are independent platform sides, different applications can compose UltimateAuth differently. -## Server Only +### Server Only Typical examples include: @@ -377,7 +377,7 @@ Application +-- Server Bundle ``` -## Client Only +### Client Only Typical examples include: @@ -393,7 +393,7 @@ Application +---- remote UltimateAuth Server / UAuthHub ``` -## Server + Client +### Server + Client Some applications host UltimateAuth and consume its client API in the same application. @@ -411,7 +411,7 @@ The two sides remain architecturally distinct even when deployed in the same pro --- -# Plugin Domains +## Plugin Domains UltimateAuth extends its identity platform through **Plugin Domains**. @@ -425,7 +425,7 @@ A Plugin Domain is more than a single assembly. It is a capability area with con This structure allows a domain to remain replaceable without requiring applications to redesign the rest of UltimateAuth. -## Anatomy of a Plugin Domain +### Anatomy of a Plugin Domain A typical Plugin Domain follows this model: @@ -448,7 +448,7 @@ Each layer has a distinct purpose. --- -## Contracts +### Contracts Example: @@ -472,7 +472,7 @@ This allows another implementation to integrate with UltimateAuth without depend --- -## Domain / Runtime +### Domain / Runtime Example: @@ -486,7 +486,7 @@ It operates against abstractions rather than requiring one persistence implement --- -## Reference Implementations +### Reference Implementations Example: @@ -518,7 +518,7 @@ This pattern gives advanced applications extensibility without making simple app --- -## Persistence Implementations +### Persistence Implementations Plugin Domains can provide persistence-specific packages independently. @@ -542,9 +542,9 @@ Their separation exists primarily to preserve architectural modularity and repla --- -# Current Plugin Domains +## Current Plugin Domains -## Users +### Users The Users domain owns user lifecycle and user-management capabilities. @@ -558,7 +558,7 @@ CodeBeam.UltimateAuth.Users.InMemory CodeBeam.UltimateAuth.Users.EntityFrameworkCore ``` -## Credentials +### Credentials The Credentials domain owns credential-related capabilities and credential management. @@ -572,7 +572,7 @@ CodeBeam.UltimateAuth.Credentials.InMemory CodeBeam.UltimateAuth.Credentials.EntityFrameworkCore ``` -## Authorization +### Authorization The Authorization domain provides UltimateAuth authorization capabilities. @@ -590,7 +590,7 @@ The same architectural pattern makes these capabilities independently evolvable --- -# Essential Server Capabilities +## Essential Server Capabilities Authentication, Sessions, and Tokens are slightly different from Plugin Domains. @@ -642,17 +642,17 @@ The selected Server bundle composes the required implementations for the applica --- -# Policies and Security +## Policies and Security Some capabilities are deliberately isolated even though they support multiple areas of the platform. -## Policies +### Policies `CodeBeam.UltimateAuth.Policies` contains policy infrastructure used to keep security and application decisions explicit and extensible. Policies are part of the server architecture rather than application-specific transport logic. -## Security Implementations +### Security Implementations Security algorithms can also live behind focused implementations. @@ -666,7 +666,7 @@ Keeping security implementations separate avoids coupling the entire platform to --- -# Modularity Without an Incomplete Framework +## Modularity Without an Incomplete Framework UltimateAuth's modularity has an important design goal: @@ -692,7 +692,7 @@ A customization should be local to the capability being replaced rather than req --- -# Replaceability by Design +## Replaceability by Design UltimateAuth is intentionally built from small architectural boundaries. @@ -736,11 +736,11 @@ Security-sensitive extension points still remain subject to UltimateAuth's secur --- -# Choosing Packages +## Choosing Packages For most developers, package selection should be simple. -## Blazor Server with InMemory persistence +### Blazor Server with InMemory persistence ```bash dotnet add package CodeBeam.UltimateAuth.InMemory.Bundle @@ -749,14 +749,14 @@ dotnet add package CodeBeam.UltimateAuth.Client.Blazor The application both hosts UltimateAuth and consumes the Blazor client integration. -## Blazor Server with Entity Framework Core +### Blazor Server with Entity Framework Core ```bash dotnet add package CodeBeam.UltimateAuth.EntityFrameworkCore.Bundle dotnet add package CodeBeam.UltimateAuth.Client.Blazor ``` -## Custom persistence +### Custom persistence Start from: @@ -766,7 +766,7 @@ dotnet add package CodeBeam.UltimateAuth.Reference.Bundle and provide the required persistence implementations for your architecture. -## Standalone Blazor WebAssembly +### Standalone Blazor WebAssembly Install the client integration: @@ -778,7 +778,7 @@ The authentication authority lives in a remote UltimateAuth Server or UAuthHub. --- -# The Rule of Thumb +## The Rule of Thumb You generally do not need to assemble UltimateAuth package-by-package. diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Layout/MainLayout.razor b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Layout/MainLayout.razor index bf52e093..943d43cf 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Layout/MainLayout.razor +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Layout/MainLayout.razor @@ -13,33 +13,51 @@
+
-
- -
+ @if (RendererInfo.IsInteractive) + { +
+
+ +
-
- Docs - Samples - Donate -
+
+ Docs + Samples + Donate +
+
+ } + else + { + + }
- +
- - - - - - - - - + @if (RendererInfo.IsInteractive) + { +
+ + + +
+ } + else + { + + }
+
diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Layout/MainLayout.razor.cs b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Layout/MainLayout.razor.cs index f737b8a0..e0d8de51 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Layout/MainLayout.razor.cs +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Layout/MainLayout.razor.cs @@ -1,19 +1,20 @@ -ο»Ώusing MudBlazor; +ο»Ώusing Microsoft.AspNetCore.Components; +using Microsoft.JSInterop; +using MudBlazor; namespace CodeBeam.UltimateAuth.Docs.Wasm.Client.Layout; public partial class MainLayout { + private const string ThemeStorageKey = "ua-theme"; + + [Inject] + private IJSRuntime JS { get; set; } = default!; + private bool _drawerOpen = false; private bool _isDarkMode = true; private bool _visionOverlay = false; - public void SetVisionOverlay(bool value) - { - _visionOverlay = value; - StateHasChanged(); - } - MudTheme _uauthTheme = new MudTheme() { PaletteLight = new PaletteLight() @@ -37,8 +38,42 @@ public void SetVisionOverlay(bool value) }, }; - public void DocsForceLoad() + protected override async Task OnAfterRenderAsync(bool firstRender) { - Nav.NavigateTo("/docs", forceLoad: true); + if (!firstRender) + return; + + try + { + var savedTheme = await JS.InvokeAsync("localStorage.getItem", ThemeStorageKey); + + if (savedTheme is "dark" or "light") + { + _isDarkMode = savedTheme == "dark"; + await InvokeAsync(StateHasChanged); + } + } + catch (JSException) + { + + } + } + + public void SetVisionOverlay(bool value) + { + _visionOverlay = value; + StateHasChanged(); + } + + private async Task SaveThemeAsync() + { + try + { + await JS.InvokeVoidAsync("localStorage.setItem", ThemeStorageKey, _isDarkMode ? "dark" : "light"); + } + catch (JSException) + { + + } } } diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsContent.razor b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsContent.razor index fa5fbf24..0f80a41e 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsContent.razor +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsContent.razor @@ -6,13 +6,6 @@ @implements IDisposable @inject PersistentComponentState PersistentState -@if (_loading) -{ -
- -
-} - @if (_notFound) { diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsSidebar.razor b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsSidebar.razor index cce9c3d4..b8d750aa 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsSidebar.razor +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsSidebar.razor @@ -7,9 +7,13 @@
@if (!Inline) { - - UAuth Docs - + +
+ UltimateAuth + DOCUMENTATION +
+ @* UAuth Docs *@ +
} @@ -28,18 +32,18 @@ } else if (_groups is null) { - + } else { - - Overview + + Overview @foreach (var group in _groups) { - + @foreach (var item in group.Value) { - @item.Title + @item.Title } } diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsToc.razor b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsToc.razor index 700b47d5..93cdf54d 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsToc.razor +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsToc.razor @@ -7,7 +7,7 @@ return; } - + On this page @@ -70,5 +70,6 @@ public void Dispose() { PageState.Changed -= OnStateChanged; + _ref?.Dispose(); } } diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/getting-started/ecosystem.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/getting-started/ecosystem.json index 4377a1f8..63f4e9f8 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/getting-started/ecosystem.json +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/getting-started/ecosystem.json @@ -1,7 +1,7 @@ { "Slug": "getting-started/ecosystem", "Title": "UltimateAuth Ecosystem", - "Html": "\n\u003Cp\u003EUltimateAuth is designed as a modular authentication and identity platform for .NET.\u003C/p\u003E\n\u003Cp\u003EThe platform has three primary foundations:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003E UltimateAuth\n |\n Core\n |\n \u002B------------\u002B------------\u002B\n | |\n Server Client\n | |\n Server composition Client integration\n through bundles for the application\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\u003Cstrong\u003ECore\u003C/strong\u003E defines the common language and foundational abstractions of the platform.\u003C/p\u003E\n\u003Cp\u003E\u003Cstrong\u003EServer\u003C/strong\u003E hosts and executes authentication and identity capabilities.\u003C/p\u003E\n\u003Cp\u003E\u003Cstrong\u003EClient\u003C/strong\u003E provides the application-facing model used by applications to interact with those capabilities.\u003C/p\u003E\n\u003Cp\u003EAn application does not necessarily need both Server and Client. Depending on its role, it may use:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EServer only\u003C/li\u003E\n\u003Cli\u003EClient only\u003C/li\u003E\n\u003Cli\u003EServer and Client together\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EThis separation is intentional. UltimateAuth is designed so that application topology does not dictate a monolithic package structure.\u003C/p\u003E\n\u003Chr /\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022platform-at-a-glance\u0022\u003EPlatform at a Glance\u003C/h2\u003E\n\u003Cp\u003EA useful way to think about UltimateAuth is:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EUltimateAuth\n|\n\u002B-- Core\n|\n\u002B-- Server\n| |\n| \u002B-- Essential server capabilities\n| | \u002B-- Authentication\n| | \u002B-- Sessions\n| | \u002B-- Tokens\n| | \u002B-- Policies\n| |\n| \u002B-- Plugin Domains\n| | \u002B-- Users\n| | \u002B-- Credentials\n| | \u002B-- Authorization\n| |\n| \u002B-- Implementations\n| \u002B-- Reference\n| \u002B-- InMemory\n| \u002B-- Entity Framework Core\n|\n\u002B-- Client\n |\n \u002B-- Client.Blazor\n \u002B-- Client.AspNetCore compatibility\n \u002B-- Future client integrations\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EThe important distinction is that these packages are not intended to make installation complicated.\u003C/p\u003E\n\u003Cp\u003EThe internal architecture is modular, while the installation experience is deliberately composed around \u003Cstrong\u003Eserver bundles\u003C/strong\u003E and \u003Cstrong\u003Eclient-specific packages\u003C/strong\u003E.\u003C/p\u003E\n\u003Chr /\u003E\n\u003Ch1 id=\u0022core\u0022\u003ECore\u003C/h1\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022codebeam.ultimateauth.core\u0022\u003E\u003Ccode\u003ECodeBeam.UltimateAuth.Core\u003C/code\u003E\u003C/h2\u003E\n\u003Cp\u003ECore is the foundation of the entire UltimateAuth platform.\u003C/p\u003E\n\u003Cp\u003EIt contains the shared primitives, domain concepts, contracts, abstractions, value objects, configuration foundations, and common infrastructure required by the rest of UltimateAuth.\u003C/p\u003E\n\u003Cp\u003EConcepts such as UltimateAuth\u0027s identity and session model originate from this layer.\u003C/p\u003E\n\u003Cp\u003ECore is intentionally independent from a particular application type or persistence technology.\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003E Core\n |\n \u002B--------\u002B--------\u002B\n | |\n Server Client\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EServer and Client share the same platform vocabulary because both are built on Core.\u003C/p\u003E\n\u003Cp\u003EMost application developers do not need to install Core directly. It is normally brought into the application through the appropriate Server bundle or Client integration package.\u003C/p\u003E\n\u003Chr /\u003E\n\u003Ch1 id=\u0022server\u0022\u003EServer\u003C/h1\u003E\n\u003Cp\u003EThe Server side is the authority that executes UltimateAuth authentication and identity behavior.\u003C/p\u003E\n\u003Cp\u003EIt is responsible for the server-side runtime, including authentication flows, security decisions, orchestration, endpoints, middleware integration, and the coordination of server capabilities.\u003C/p\u003E\n\u003Cp\u003EAt source level, \u003Ccode\u003ECodeBeam.UltimateAuth.Server\u003C/code\u003E is the central server project and depends on major server domains such as:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003ECodeBeam.UltimateAuth.Server\n|\n\u002B-- Core\n\u002B-- Authorization\n\u002B-- Credentials\n\u002B-- Users\n\u002B-- Policies\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EOther essential server capabilities, including authentication, sessions, and tokens, are kept in focused projects rather than being merged into one oversized server assembly.\u003C/p\u003E\n\u003Cp\u003EThis gives UltimateAuth a modular internal architecture without forcing that complexity onto application developers.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022do-i-install-codebeam.ultimateauth.server-directly\u0022\u003EDo I install \u003Ccode\u003ECodeBeam.UltimateAuth.Server\u003C/code\u003E directly?\u003C/h2\u003E\n\u003Cp\u003EFor normal application setup, \u003Cstrong\u003Eno\u003C/strong\u003E.\u003C/p\u003E\n\u003Cp\u003EThe recommended server installation unit is a \u003Cstrong\u003EBundle\u003C/strong\u003E.\u003C/p\u003E\n\u003Cp\u003EA bundle represents a complete server composition for a particular infrastructure strategy.\u003C/p\u003E\n\u003Cp\u003EInstead of manually selecting all server packages and implementations, choose the bundle that matches your application.\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003E Your Server\n |\n Bundle\n |\n \u002B---------------\u002B---------------\u002B\n | | |\n Server Domains Infrastructure\n | | |\n Runtime Reference Persistence\n behavior providers\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EThis is an important part of the UltimateAuth design:\u003C/p\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003EThe framework remains modular internally, while common server configurations remain simple to install.\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Chr /\u003E\n\u003Ch1 id=\u0022server-bundles\u0022\u003EServer Bundles\u003C/h1\u003E\n\u003Cp\u003EBundles are server-side composition packages.\u003C/p\u003E\n\u003Cp\u003EThey collect the UltimateAuth server capabilities and the implementations required for a particular configuration into a single installation unit.\u003C/p\u003E\n\u003Cp\u003EYou normally choose \u003Cstrong\u003Eone server bundle\u003C/strong\u003E.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022reference-bundle\u0022\u003EReference Bundle\u003C/h2\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022codebeam.ultimateauth.reference.bundle\u0022\u003E\u003Ccode\u003ECodeBeam.UltimateAuth.Reference.Bundle\u003C/code\u003E\u003C/h3\u003E\n\u003Cp\u003EThe Reference Bundle provides the recommended UltimateAuth server composition without selecting a persistence implementation for your application.\u003C/p\u003E\n\u003Cp\u003EUse it when you want UltimateAuth\u0027s reference behavior but provide your own persistence infrastructure.\u003C/p\u003E\n\u003Cp\u003EConceptually:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EReference.Bundle\n|\n\u002B-- Server\n\u002B-- UltimateAuth reference implementations\n\u002B-- Required server capabilities\n|\n\u002B-- Persistence: supplied by your application\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EThis is the lowest-level recommended server composition for applications that want to own their storage implementation.\u003C/p\u003E\n\u003Cp\u003EIt is also an important extensibility point: UltimateAuth provides a recommended architecture without requiring applications to use one particular persistence technology.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022inmemory-bundle\u0022\u003EInMemory Bundle\u003C/h2\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022codebeam.ultimateauth.inmemory.bundle\u0022\u003E\u003Ccode\u003ECodeBeam.UltimateAuth.InMemory.Bundle\u003C/code\u003E\u003C/h3\u003E\n\u003Cp\u003EThe InMemory Bundle provides a complete server setup backed by UltimateAuth\u0027s in-memory implementations.\u003C/p\u003E\n\u003Cp\u003EIt is primarily intended for:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Edevelopment\u003C/li\u003E\n\u003Cli\u003EQuick Start\u003C/li\u003E\n\u003Cli\u003Esamples\u003C/li\u003E\n\u003Cli\u003Eautomated tests\u003C/li\u003E\n\u003Cli\u003Eevaluation\u003C/li\u003E\n\u003Cli\u003Eprototypes\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EInMemory.Bundle\n|\n\u002B-- Server\n\u002B-- Reference implementations\n\u002B-- InMemory persistence implementations\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EThis is the easiest way to start an UltimateAuth server.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022entity-framework-core-bundle\u0022\u003EEntity Framework Core Bundle\u003C/h2\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022codebeam.ultimateauth.entityframeworkcore.bundle\u0022\u003E\u003Ccode\u003ECodeBeam.UltimateAuth.EntityFrameworkCore.Bundle\u003C/code\u003E\u003C/h3\u003E\n\u003Cp\u003EThe Entity Framework Core Bundle provides the complete server composition using UltimateAuth\u0027s EF Core persistence implementations.\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EEntityFrameworkCore.Bundle\n|\n\u002B-- Server\n\u002B-- Reference implementations\n\u002B-- EF Core persistence implementations\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EFor applications that use EF Core for persistent UltimateAuth data, this is normally the server package to install.\u003C/p\u003E\n\u003Cp\u003EThe developer does not need to manually assemble the individual Users, Credentials, Authorization, Sessions, Tokens, and other persistence packages.\u003C/p\u003E\n\u003Cp\u003EThe bundle performs that composition.\u003C/p\u003E\n\u003Chr /\u003E\n\u003Ch1 id=\u0022why-bundles-matter\u0022\u003EWhy Bundles Matter\u003C/h1\u003E\n\u003Cp\u003EA highly modular framework can easily make the developer experience worse if every application has to understand and install its internal package graph.\u003C/p\u003E\n\u003Cp\u003EUltimateAuth deliberately separates \u003Cstrong\u003Earchitecture modularity\u003C/strong\u003E from \u003Cstrong\u003Einstallation complexity\u003C/strong\u003E.\u003C/p\u003E\n\u003Cp\u003EInternally:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EServer\n \u002B Users\n \u002B Credentials\n \u002B Authorization\n \u002B Authentication\n \u002B Sessions\n \u002B Tokens\n \u002B Policies\n \u002B persistence implementations\n \u002B reference implementations\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EFrom the application:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-bash\u0022\u003Edotnet add package CodeBeam.UltimateAuth.EntityFrameworkCore.Bundle\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EThis gives UltimateAuth two useful properties at the same time:\u003C/p\u003E\n\u003Col\u003E\n\u003Cli\u003Eindividual capabilities remain independently replaceable and evolvable;\u003C/li\u003E\n\u003Cli\u003Ethe common path remains simple.\u003C/li\u003E\n\u003C/ol\u003E\n\u003Cp\u003EYou should not need to understand the complete internal dependency graph before building your first application.\u003C/p\u003E\n\u003Chr /\u003E\n\u003Ch1 id=\u0022client\u0022\u003EClient\u003C/h1\u003E\n\u003Cp\u003EThe Client side has a different packaging model.\u003C/p\u003E\n\u003Cp\u003EThere are \u003Cstrong\u003Eno Client bundles\u003C/strong\u003E.\u003C/p\u003E\n\u003Cp\u003EInstead, applications install the client package designed for their application model.\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003E Client Foundation\n |\n \u002B-------------\u002B-------------\u002B\n | | |\n Blazor MAUI Future\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EFor example, a Blazor application installs:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-bash\u0022\u003Edotnet add package CodeBeam.UltimateAuth.Client.Blazor\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EA future MAUI integration can follow the same model without changing the underlying UltimateAuth Client architecture.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022codebeam.ultimateauth.client\u0022\u003E\u003Ccode\u003ECodeBeam.UltimateAuth.Client\u003C/code\u003E\u003C/h2\u003E\n\u003Cp\u003E\u003Ccode\u003ECodeBeam.UltimateAuth.Client\u003C/code\u003E is the shared client foundation.\u003C/p\u003E\n\u003Cp\u003EIt contains common client contracts, abstractions, flow APIs, state concepts, and runtime-independent client behavior.\u003C/p\u003E\n\u003Cp\u003EIt should be thought of as the foundation on which concrete UltimateAuth client integrations are built.\u003C/p\u003E\n\u003Cp\u003EMost applications should therefore install their platform-specific client package rather than installing \u003Ccode\u003ECodeBeam.UltimateAuth.Client\u003C/code\u003E directly.\u003C/p\u003E\n\u003Chr /\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022codebeam.ultimateauth.client.blazor\u0022\u003E\u003Ccode\u003ECodeBeam.UltimateAuth.Client.Blazor\u003C/code\u003E\u003C/h2\u003E\n\u003Cp\u003E\u003Ccode\u003ECodeBeam.UltimateAuth.Client.Blazor\u003C/code\u003E is the concrete UltimateAuth client integration for Blazor.\u003C/p\u003E\n\u003Cp\u003EIt builds on the Client foundation and adds the Blazor-specific runtime and UI integration required by Blazor applications.\u003C/p\u003E\n\u003Cp\u003EThis includes capabilities such as:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u003Ccode\u003EUAuthApp\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003E\u003Ccode\u003EUAuthLoginForm\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003E\u003Ccode\u003EUAuthStateView\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003Eauthentication state integration\u003C/li\u003E\n\u003Cli\u003EBlazor authorization integration\u003C/li\u003E\n\u003Cli\u003Erouting integration\u003C/li\u003E\n\u003Cli\u003Eauthentication flow components\u003C/li\u003E\n\u003Cli\u003EJavaScript transport integration\u003C/li\u003E\n\u003Cli\u003Eclient lifecycle coordination\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EConceptually:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003ECore\n |\nClient\n |\nClient.Blazor\n |\nYour Blazor Application\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EThis model keeps the shared Client architecture independent from Blazor while still giving Blazor developers one natural package to install.\u003C/p\u003E\n\u003Chr /\u003E\n\u003Ch1 id=\u0022client-compatibility-packages\u0022\u003EClient Compatibility Packages\u003C/h1\u003E\n\u003Cp\u003ENot every client-side package represents a complete client implementation.\u003C/p\u003E\n\u003Cp\u003ESome packages exist to bridge UltimateAuth with a host framework.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022codebeam.ultimateauth.client.aspnetcore\u0022\u003E\u003Ccode\u003ECodeBeam.UltimateAuth.Client.AspNetCore\u003C/code\u003E\u003C/h2\u003E\n\u003Cp\u003E\u003Ccode\u003ECodeBeam.UltimateAuth.Client.AspNetCore\u003C/code\u003E is a lightweight ASP.NET Core compatibility layer for UltimateAuth client applications.\u003C/p\u003E\n\u003Cp\u003EIt allows client-oriented applications to integrate with ASP.NET Core authentication and authorization infrastructure without introducing the full UltimateAuth Server runtime.\u003C/p\u003E\n\u003Cp\u003EThis is particularly useful for hosting models such as Blazor Web App with Interactive WebAssembly.\u003C/p\u003E\n\u003Cp\u003EIt can enable compatibility with ASP.NET Core features such as \u003Ccode\u003E[Authorize]\u003C/code\u003E, but it does \u003Cstrong\u003Enot\u003C/strong\u003E implement authentication itself and does not turn the application into an UltimateAuth Server.\u003C/p\u003E\n\u003Chr /\u003E\n\u003Ch1 id=\u0022application-composition\u0022\u003EApplication Composition\u003C/h1\u003E\n\u003Cp\u003EBecause Server and Client are independent platform sides, different applications can compose UltimateAuth differently.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022server-only\u0022\u003EServer Only\u003C/h2\u003E\n\u003Cp\u003ETypical examples include:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Ededicated authentication servers\u003C/li\u003E\n\u003Cli\u003Ebackend services hosting UltimateAuth\u003C/li\u003E\n\u003Cli\u003Eserver-side identity infrastructure\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EApplication\n|\n\u002B-- Server Bundle\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022client-only\u0022\u003EClient Only\u003C/h2\u003E\n\u003Cp\u003ETypical examples include:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Estandalone Blazor WebAssembly\u003C/li\u003E\n\u003Cli\u003Efuture MAUI applications\u003C/li\u003E\n\u003Cli\u003Eapplications using a remote UAuthHub\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EApplication\n|\n\u002B-- Client.Blazor\n |\n \u002B---- remote UltimateAuth Server / UAuthHub\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022server-client\u0022\u003EServer \u002B Client\u003C/h2\u003E\n\u003Cp\u003ESome applications host UltimateAuth and consume its client API in the same application.\u003C/p\u003E\n\u003Cp\u003EBlazor Server is a common example.\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EBlazor Server Application\n|\n\u002B-- Server Bundle\n|\n\u002B-- Client.Blazor\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EThe two sides remain architecturally distinct even when deployed in the same process.\u003C/p\u003E\n\u003Chr /\u003E\n\u003Ch1 id=\u0022plugin-domains\u0022\u003EPlugin Domains\u003C/h1\u003E\n\u003Cp\u003EUltimateAuth extends its identity platform through \u003Cstrong\u003EPlugin Domains\u003C/strong\u003E.\u003C/p\u003E\n\u003Cp\u003EThe current primary Plugin Domains are:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EUsers\u003C/li\u003E\n\u003Cli\u003ECredentials\u003C/li\u003E\n\u003Cli\u003EAuthorization\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EA Plugin Domain is more than a single assembly. It is a capability area with contracts, runtime behavior, recommended implementations, and infrastructure implementations.\u003C/p\u003E\n\u003Cp\u003EThis structure allows a domain to remain replaceable without requiring applications to redesign the rest of UltimateAuth.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022anatomy-of-a-plugin-domain\u0022\u003EAnatomy of a Plugin Domain\u003C/h2\u003E\n\u003Cp\u003EA typical Plugin Domain follows this model:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EPlugin Domain\n|\n\u002B-- Contracts\n|\n\u002B-- Domain / Runtime\n|\n\u002B-- Reference\n|\n\u002B-- Persistence\n |\n \u002B-- InMemory\n \u002B-- EntityFrameworkCore\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EEach layer has a distinct purpose.\u003C/p\u003E\n\u003Chr /\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022contracts\u0022\u003EContracts\u003C/h2\u003E\n\u003Cp\u003EExample:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003ECodeBeam.UltimateAuth.Users.Contracts\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EContracts define the public boundary of the domain. Server and Client can communicate through same contracts without requiring one to depend on the other.\u003C/p\u003E\n\u003Cp\u003EThey are intentionally kept close to Core and represent the smallest dependency surface for applications or extensions that only need to interact with the domain contract.\u003C/p\u003E\n\u003Cp\u003EDepending on the domain, contracts can contain concepts such as:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Erequests\u003C/li\u003E\n\u003Cli\u003Eresults\u003C/li\u003E\n\u003Cli\u003Epublic models\u003C/li\u003E\n\u003Cli\u003Eabstractions\u003C/li\u003E\n\u003Cli\u003Edomain-facing contracts\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EThis allows another implementation to integrate with UltimateAuth without depending on the entire built-in implementation.\u003C/p\u003E\n\u003Chr /\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022domain-runtime\u0022\u003EDomain / Runtime\u003C/h2\u003E\n\u003Cp\u003EExample:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003ECodeBeam.UltimateAuth.Users\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EThe main domain project provides the runtime and application behavior of that Plugin Domain.\u003C/p\u003E\n\u003Cp\u003EIt operates against abstractions rather than requiring one persistence implementation.\u003C/p\u003E\n\u003Chr /\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022reference-implementations\u0022\u003EReference Implementations\u003C/h2\u003E\n\u003Cp\u003EExample:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003ECodeBeam.UltimateAuth.Users.Reference\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EReference packages contain the implementation UltimateAuth recommends as the standard baseline for the domain.\u003C/p\u003E\n\u003Cp\u003EThe distinction between \u003Cstrong\u003Econtract\u003C/strong\u003E and \u003Cstrong\u003Ereference implementation\u003C/strong\u003E is intentional.\u003C/p\u003E\n\u003Cp\u003EUltimateAuth does not leave extensibility points empty and require every developer to design critical identity behavior from scratch.\u003C/p\u003E\n\u003Cp\u003EInstead, the platform provides:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EContract\n |\n \u002B---- UltimateAuth Reference Implementation\n |\n \u002B---- Your Custom Implementation\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EFor the common path, use the UltimateAuth reference implementation.\u003C/p\u003E\n\u003Cp\u003EFor specialized requirements, replace the appropriate implementation while preserving the surrounding platform contract and security boundaries.\u003C/p\u003E\n\u003Cp\u003EThis pattern gives advanced applications extensibility without making simple applications incomplete.\u003C/p\u003E\n\u003Chr /\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022persistence-implementations\u0022\u003EPersistence Implementations\u003C/h2\u003E\n\u003Cp\u003EPlugin Domains can provide persistence-specific packages independently.\u003C/p\u003E\n\u003Cp\u003EFor example:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EUsers\n|\n\u002B-- Users.Contracts\n\u002B-- Users\n\u002B-- Users.Reference\n\u002B-- Users.InMemory\n\u002B-- Users.EntityFrameworkCore\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EThe same pattern applies to other Plugin Domains where appropriate.\u003C/p\u003E\n\u003Cp\u003EA developer using a server bundle normally does not need to install these packages individually. The selected bundle composes the appropriate persistence strategy.\u003C/p\u003E\n\u003Cp\u003ETheir separation exists primarily to preserve architectural modularity and replaceability.\u003C/p\u003E\n\u003Chr /\u003E\n\u003Ch1 id=\u0022current-plugin-domains\u0022\u003ECurrent Plugin Domains\u003C/h1\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022users\u0022\u003EUsers\u003C/h2\u003E\n\u003Cp\u003EThe Users domain owns user lifecycle and user-management capabilities.\u003C/p\u003E\n\u003Cp\u003EIts package family includes:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003ECodeBeam.UltimateAuth.Users.Contracts\nCodeBeam.UltimateAuth.Users\nCodeBeam.UltimateAuth.Users.Reference\nCodeBeam.UltimateAuth.Users.InMemory\nCodeBeam.UltimateAuth.Users.EntityFrameworkCore\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022credentials\u0022\u003ECredentials\u003C/h2\u003E\n\u003Cp\u003EThe Credentials domain owns credential-related capabilities and credential management.\u003C/p\u003E\n\u003Cp\u003EIts package family includes:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003ECodeBeam.UltimateAuth.Credentials.Contracts\nCodeBeam.UltimateAuth.Credentials\nCodeBeam.UltimateAuth.Credentials.Reference\nCodeBeam.UltimateAuth.Credentials.InMemory\nCodeBeam.UltimateAuth.Credentials.EntityFrameworkCore\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022authorization\u0022\u003EAuthorization\u003C/h2\u003E\n\u003Cp\u003EThe Authorization domain provides UltimateAuth authorization capabilities.\u003C/p\u003E\n\u003Cp\u003EIts package family includes:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003ECodeBeam.UltimateAuth.Authorization.Contracts\nCodeBeam.UltimateAuth.Authorization\nCodeBeam.UltimateAuth.Authorization.Reference\nCodeBeam.UltimateAuth.Authorization.InMemory\nCodeBeam.UltimateAuth.Authorization.EntityFrameworkCore\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EThe same architectural pattern makes these capabilities independently evolvable while keeping their public boundaries explicit.\u003C/p\u003E\n\u003Chr /\u003E\n\u003Ch1 id=\u0022essential-server-capabilities\u0022\u003EEssential Server Capabilities\u003C/h1\u003E\n\u003Cp\u003EAuthentication, Sessions, and Tokens are slightly different from Plugin Domains.\u003C/p\u003E\n\u003Cp\u003EThey are fundamental parts of an UltimateAuth Server and are not treated as optional identity plugins in the same sense as Users, Credentials, or Authorization.\u003C/p\u003E\n\u003Cp\u003EHowever, they are still separated into focused projects.\u003C/p\u003E\n\u003Cp\u003EConceptually:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003E UltimateAuth Server\n |\n \u002B--------------------\u002B--------------------\u002B\n | | |\n Authentication Sessions Tokens\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EWhy separate them if the Server needs them?\u003C/p\u003E\n\u003Cp\u003EBecause \u003Cstrong\u003Erequired does not have to mean monolithic\u003C/strong\u003E.\u003C/p\u003E\n\u003Cp\u003EKeeping these capabilities isolated:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eprevents the central Server project from becoming an oversized implementation assembly;\u003C/li\u003E\n\u003Cli\u003Ekeeps responsibilities and dependency boundaries explicit;\u003C/li\u003E\n\u003Cli\u003Eallows implementations to evolve independently;\u003C/li\u003E\n\u003Cli\u003Emakes persistence providers independently composable;\u003C/li\u003E\n\u003Cli\u003Ekeeps replacement and testing boundaries smaller.\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EThey can be thought of as \u003Cstrong\u003Emodular server subsystems\u003C/strong\u003E rather than full Plugin Domains.\u003C/p\u003E\n\u003Cp\u003EFor example, persistence implementations can still exist independently:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EAuthentication\n\u002B-- Authentication.InMemory\n\u002B-- Authentication.EntityFrameworkCore\n\nSessions\n\u002B-- Sessions.InMemory\n\u002B-- Sessions.EntityFrameworkCore\n\nTokens\n\u002B-- Tokens.InMemory\n\u002B-- Tokens.EntityFrameworkCore\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EThe selected Server bundle composes the required implementations for the application.\u003C/p\u003E\n\u003Chr /\u003E\n\u003Ch1 id=\u0022policies-and-security\u0022\u003EPolicies and Security\u003C/h1\u003E\n\u003Cp\u003ESome capabilities are deliberately isolated even though they support multiple areas of the platform.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022policies\u0022\u003EPolicies\u003C/h2\u003E\n\u003Cp\u003E\u003Ccode\u003ECodeBeam.UltimateAuth.Policies\u003C/code\u003E contains policy infrastructure used to keep security and application decisions explicit and extensible.\u003C/p\u003E\n\u003Cp\u003EPolicies are part of the server architecture rather than application-specific transport logic.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022security-implementations\u0022\u003ESecurity Implementations\u003C/h2\u003E\n\u003Cp\u003ESecurity algorithms can also live behind focused implementations.\u003C/p\u003E\n\u003Cp\u003EFor example:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003ECodeBeam.UltimateAuth.Security.Argon2\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EKeeping security implementations separate avoids coupling the entire platform to one concrete algorithm or provider.\u003C/p\u003E\n\u003Chr /\u003E\n\u003Ch1 id=\u0022modularity-without-an-incomplete-framework\u0022\u003EModularity Without an Incomplete Framework\u003C/h1\u003E\n\u003Cp\u003EUltimateAuth\u0027s modularity has an important design goal:\u003C/p\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003EExtensibility should not require developers to build the missing half of the framework themselves.\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Cp\u003EEvery major extensibility boundary should have a practical UltimateAuth-provided path.\u003C/p\u003E\n\u003Cp\u003EThe common model is:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003E UltimateAuth Contract\n |\n \u002B-----------\u002B-----------\u002B\n | |\n Reference Path Custom Path\n | |\n Works out of the box Replace when needed\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EThis means the platform can provide strong defaults and reference implementations while still allowing advanced applications to replace individual capabilities.\u003C/p\u003E\n\u003Cp\u003EA customization should be local to the capability being replaced rather than requiring the authentication architecture to be rewritten.\u003C/p\u003E\n\u003Chr /\u003E\n\u003Ch1 id=\u0022replaceability-by-design\u0022\u003EReplaceability by Design\u003C/h1\u003E\n\u003Cp\u003EUltimateAuth is intentionally built from small architectural boundaries.\u003C/p\u003E\n\u003Cp\u003EThe goal is not modularity for its own sake.\u003C/p\u003E\n\u003Cp\u003EThe goal is to reduce the cost of change.\u003C/p\u003E\n\u003Cp\u003EAn application may start with:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EEntityFrameworkCore.Bundle\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003Ewhile still having clearly separated domains and infrastructure underneath.\u003C/p\u003E\n\u003Cp\u003EIf a specialized application later needs a custom implementation for one capability, the architecture already has a boundary for it.\u003C/p\u003E\n\u003Cp\u003EThis provides a different model from choosing between two extremes:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003ESimple but monolithic\n OR\nFlexible but difficult to configure\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EUltimateAuth aims for:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003E Simple installation\n \u002B\n Modular internals\n \u002B\n Reference implementations\n \u002B\n Explicit contracts\n =\n Low-cost customization\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003ESecurity-sensitive extension points still remain subject to UltimateAuth\u0027s security invariants and authority boundaries. Replaceability is intended to enable integration and specialization, not to bypass required security decisions.\u003C/p\u003E\n\u003Chr /\u003E\n\u003Ch1 id=\u0022choosing-packages\u0022\u003EChoosing Packages\u003C/h1\u003E\n\u003Cp\u003EFor most developers, package selection should be simple.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022blazor-server-with-inmemory-persistence\u0022\u003EBlazor Server with InMemory persistence\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-bash\u0022\u003Edotnet add package CodeBeam.UltimateAuth.InMemory.Bundle\ndotnet add package CodeBeam.UltimateAuth.Client.Blazor\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EThe application both hosts UltimateAuth and consumes the Blazor client integration.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022blazor-server-with-entity-framework-core\u0022\u003EBlazor Server with Entity Framework Core\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-bash\u0022\u003Edotnet add package CodeBeam.UltimateAuth.EntityFrameworkCore.Bundle\ndotnet add package CodeBeam.UltimateAuth.Client.Blazor\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022custom-persistence\u0022\u003ECustom persistence\u003C/h2\u003E\n\u003Cp\u003EStart from:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-bash\u0022\u003Edotnet add package CodeBeam.UltimateAuth.Reference.Bundle\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003Eand provide the required persistence implementations for your architecture.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022standalone-blazor-webassembly\u0022\u003EStandalone Blazor WebAssembly\u003C/h2\u003E\n\u003Cp\u003EInstall the client integration:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-bash\u0022\u003Edotnet add package CodeBeam.UltimateAuth.Client.Blazor\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EThe authentication authority lives in a remote UltimateAuth Server or UAuthHub.\u003C/p\u003E\n\u003Chr /\u003E\n\u003Ch1 id=\u0022the-rule-of-thumb\u0022\u003EThe Rule of Thumb\u003C/h1\u003E\n\u003Cp\u003EYou generally do not need to assemble UltimateAuth package-by-package.\u003C/p\u003E\n\u003Cp\u003EFor the \u003Cstrong\u003EServer\u003C/strong\u003E:\u003C/p\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003EChoose the Bundle that matches your infrastructure.\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Cp\u003EFor the \u003Cstrong\u003EClient\u003C/strong\u003E:\u003C/p\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003EChoose the Client package that matches your application platform.\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003ESERVER\n\u0026quot;What infrastructure do I use?\u0026quot;\n |\n \u002B-- Reference.Bundle\n \u002B-- InMemory.Bundle\n \u002B-- EntityFrameworkCore.Bundle\n\n\nCLIENT\n\u0026quot;What kind of application am I building?\u0026quot;\n |\n \u002B-- Client.Blazor\n \u002B-- Client.Maui (future)\n \u002B-- ...\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EThe detailed package graph exists so that UltimateAuth remains extensible.\u003C/p\u003E\n\u003Cp\u003EThe bundles and client integrations exist so that you do not have to manage that graph for ordinary applications.\u003C/p\u003E\n\u003Chr /\u003E\n\u003Ch1 id=\u0022next\u0022\u003ENext\u003C/h1\u003E\n\u003Cp\u003EYou now have the mental model needed to understand the rest of the UltimateAuth documentation:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003ECore\n |\n \u002B-- Server --\u0026gt; choose a Bundle\n |\n \u002B-- Client --\u0026gt; choose your platform integration\n\nServer\n |\n \u002B-- Essential server subsystems\n |\n \u002B-- Plugin Domains\n |\n \u002B-- Contracts\n \u002B-- Runtime\n \u002B-- Reference implementations\n \u002B-- Persistence implementations\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EContinue with the \u003Ca href=\u0022./quickstart.md\u0022\u003EQuick Start\u003C/a\u003E to build your first UltimateAuth application.\u003C/p\u003E\n\u003Cp\u003EFor persistent storage, UAuthHub, standalone WebAssembly, Resource API, and other deployment models, continue with the \u003Ca href=\u0022./real-world-setup.md\u0022\u003EReal-World Setup\u003C/a\u003E.\u003C/p\u003E\n", + "Html": "\n\u003Cp\u003EUltimateAuth is designed as a modular authentication and identity platform for .NET.\u003C/p\u003E\n\u003Cp\u003EThe platform has three primary foundations:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003E UltimateAuth\n |\n Core\n |\n \u002B------------\u002B------------\u002B\n | |\n Server Client\n | |\n Server composition Client integration\n through bundles for the application\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\u003Cstrong\u003ECore\u003C/strong\u003E defines the common language and foundational abstractions of the platform.\u003C/p\u003E\n\u003Cp\u003E\u003Cstrong\u003EServer\u003C/strong\u003E hosts and executes authentication and identity capabilities.\u003C/p\u003E\n\u003Cp\u003E\u003Cstrong\u003EClient\u003C/strong\u003E provides the application-facing model used by applications to interact with those capabilities.\u003C/p\u003E\n\u003Cp\u003EAn application does not necessarily need both Server and Client. Depending on its role, it may use:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EServer only\u003C/li\u003E\n\u003Cli\u003EClient only\u003C/li\u003E\n\u003Cli\u003EServer and Client together\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EThis separation is intentional. UltimateAuth is designed so that application topology does not dictate a monolithic package structure.\u003C/p\u003E\n\u003Chr /\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022platform-at-a-glance\u0022\u003EPlatform at a Glance\u003C/h2\u003E\n\u003Cp\u003EA useful way to think about UltimateAuth is:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EUltimateAuth\n|\n\u002B-- Core\n|\n\u002B-- Server\n| |\n| \u002B-- Essential server capabilities\n| | \u002B-- Authentication\n| | \u002B-- Sessions\n| | \u002B-- Tokens\n| | \u002B-- Policies\n| |\n| \u002B-- Plugin Domains\n| | \u002B-- Users\n| | \u002B-- Credentials\n| | \u002B-- Authorization\n| |\n| \u002B-- Implementations\n| \u002B-- Reference\n| \u002B-- InMemory\n| \u002B-- Entity Framework Core\n|\n\u002B-- Client\n |\n \u002B-- Client.Blazor\n \u002B-- Client.AspNetCore compatibility\n \u002B-- Future client integrations\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EThe important distinction is that these packages are not intended to make installation complicated.\u003C/p\u003E\n\u003Cp\u003EThe internal architecture is modular, while the installation experience is deliberately composed around \u003Cstrong\u003Eserver bundles\u003C/strong\u003E and \u003Cstrong\u003Eclient-specific packages\u003C/strong\u003E.\u003C/p\u003E\n\u003Chr /\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022core\u0022\u003ECore\u003C/h2\u003E\n\u003Ch4 id=\u0022codebeam.ultimateauth.core\u0022\u003E\u003Ccode\u003ECodeBeam.UltimateAuth.Core\u003C/code\u003E\u003C/h4\u003E\n\u003Cp\u003ECore is the foundation of the entire UltimateAuth platform.\u003C/p\u003E\n\u003Cp\u003EIt contains the shared primitives, domain concepts, contracts, abstractions, value objects, configuration foundations, and common infrastructure required by the rest of UltimateAuth.\u003C/p\u003E\n\u003Cp\u003EConcepts such as UltimateAuth\u0027s identity and session model originate from this layer.\u003C/p\u003E\n\u003Cp\u003ECore is intentionally independent from a particular application type or persistence technology.\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003E Core\n |\n \u002B--------\u002B--------\u002B\n | |\n Server Client\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EServer and Client share the same platform vocabulary because both are built on Core.\u003C/p\u003E\n\u003Cp\u003EMost application developers do not need to install Core directly. It is normally brought into the application through the appropriate Server bundle or Client integration package.\u003C/p\u003E\n\u003Chr /\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022server\u0022\u003EServer\u003C/h2\u003E\n\u003Cp\u003EThe Server side is the authority that executes UltimateAuth authentication and identity behavior.\u003C/p\u003E\n\u003Cp\u003EIt is responsible for the server-side runtime, including authentication flows, security decisions, orchestration, endpoints, middleware integration, and the coordination of server capabilities.\u003C/p\u003E\n\u003Cp\u003EAt source level, \u003Ccode\u003ECodeBeam.UltimateAuth.Server\u003C/code\u003E is the central server project and depends on major server domains such as:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003ECodeBeam.UltimateAuth.Server\n|\n\u002B-- Core\n\u002B-- Authorization\n\u002B-- Credentials\n\u002B-- Users\n\u002B-- Policies\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EOther essential server capabilities, including authentication, sessions, and tokens, are kept in focused projects rather than being merged into one oversized server assembly.\u003C/p\u003E\n\u003Cp\u003EThis gives UltimateAuth a modular internal architecture without forcing that complexity onto application developers.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022do-i-install-codebeam.ultimateauth.server-directly\u0022\u003EDo I install \u003Ccode\u003ECodeBeam.UltimateAuth.Server\u003C/code\u003E directly?\u003C/h3\u003E\n\u003Cp\u003EFor normal application setup, \u003Cstrong\u003Eno\u003C/strong\u003E.\u003C/p\u003E\n\u003Cp\u003EThe recommended server installation unit is a \u003Cstrong\u003EBundle\u003C/strong\u003E.\u003C/p\u003E\n\u003Cp\u003EA bundle represents a complete server composition for a particular infrastructure strategy.\u003C/p\u003E\n\u003Cp\u003EInstead of manually selecting all server packages and implementations, choose the bundle that matches your application.\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003E Your Server\n |\n Bundle\n |\n \u002B---------------\u002B---------------\u002B\n | | |\n Server Domains Infrastructure\n | | |\n Runtime Reference Persistence\n behavior providers\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EThis is an important part of the UltimateAuth design:\u003C/p\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003EThe framework remains modular internally, while common server configurations remain simple to install.\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Chr /\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022server-bundles\u0022\u003EServer Bundles\u003C/h2\u003E\n\u003Cp\u003EBundles are server-side composition packages.\u003C/p\u003E\n\u003Cp\u003EThey collect the UltimateAuth server capabilities and the implementations required for a particular configuration into a single installation unit.\u003C/p\u003E\n\u003Cp\u003EYou normally choose \u003Cstrong\u003Eone server bundle\u003C/strong\u003E.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022reference-bundle\u0022\u003EReference Bundle\u003C/h3\u003E\n\u003Ch4 id=\u0022codebeam.ultimateauth.reference.bundle\u0022\u003E\u003Ccode\u003ECodeBeam.UltimateAuth.Reference.Bundle\u003C/code\u003E\u003C/h4\u003E\n\u003Cp\u003EThe Reference Bundle provides the recommended UltimateAuth server composition without selecting a persistence implementation for your application.\u003C/p\u003E\n\u003Cp\u003EUse it when you want UltimateAuth\u0027s reference behavior but provide your own persistence infrastructure.\u003C/p\u003E\n\u003Cp\u003EConceptually:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EReference.Bundle\n|\n\u002B-- Server\n\u002B-- UltimateAuth reference implementations\n\u002B-- Required server capabilities\n|\n\u002B-- Persistence: supplied by your application\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EThis is the lowest-level recommended server composition for applications that want to own their storage implementation.\u003C/p\u003E\n\u003Cp\u003EIt is also an important extensibility point: UltimateAuth provides a recommended architecture without requiring applications to use one particular persistence technology.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022inmemory-bundle\u0022\u003EInMemory Bundle\u003C/h3\u003E\n\u003Ch4 id=\u0022codebeam.ultimateauth.inmemory.bundle\u0022\u003E\u003Ccode\u003ECodeBeam.UltimateAuth.InMemory.Bundle\u003C/code\u003E\u003C/h4\u003E\n\u003Cp\u003EThe InMemory Bundle provides a complete server setup backed by UltimateAuth\u0027s in-memory implementations.\u003C/p\u003E\n\u003Cp\u003EIt is primarily intended for:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Edevelopment\u003C/li\u003E\n\u003Cli\u003EQuick Start\u003C/li\u003E\n\u003Cli\u003Esamples\u003C/li\u003E\n\u003Cli\u003Eautomated tests\u003C/li\u003E\n\u003Cli\u003Eevaluation\u003C/li\u003E\n\u003Cli\u003Eprototypes\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EInMemory.Bundle\n|\n\u002B-- Server\n\u002B-- Reference implementations\n\u002B-- InMemory persistence implementations\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EThis is the easiest way to start an UltimateAuth server.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022entity-framework-core-bundle\u0022\u003EEntity Framework Core Bundle\u003C/h3\u003E\n\u003Ch4 id=\u0022codebeam.ultimateauth.entityframeworkcore.bundle\u0022\u003E\u003Ccode\u003ECodeBeam.UltimateAuth.EntityFrameworkCore.Bundle\u003C/code\u003E\u003C/h4\u003E\n\u003Cp\u003EThe Entity Framework Core Bundle provides the complete server composition using UltimateAuth\u0027s EF Core persistence implementations.\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EEntityFrameworkCore.Bundle\n|\n\u002B-- Server\n\u002B-- Reference implementations\n\u002B-- EF Core persistence implementations\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EFor applications that use EF Core for persistent UltimateAuth data, this is normally the server package to install.\u003C/p\u003E\n\u003Cp\u003EThe developer does not need to manually assemble the individual Users, Credentials, Authorization, Sessions, Tokens, and other persistence packages.\u003C/p\u003E\n\u003Cp\u003EThe bundle performs that composition.\u003C/p\u003E\n\u003Chr /\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022why-bundles-matter\u0022\u003EWhy Bundles Matter\u003C/h3\u003E\n\u003Cp\u003EA highly modular framework can easily make the developer experience worse if every application has to understand and install its internal package graph.\u003C/p\u003E\n\u003Cp\u003EUltimateAuth deliberately separates \u003Cstrong\u003Earchitecture modularity\u003C/strong\u003E from \u003Cstrong\u003Einstallation complexity\u003C/strong\u003E.\u003C/p\u003E\n\u003Cp\u003EInternally:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EServer\n \u002B Users\n \u002B Credentials\n \u002B Authorization\n \u002B Authentication\n \u002B Sessions\n \u002B Tokens\n \u002B Policies\n \u002B persistence implementations\n \u002B reference implementations\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EFrom the application:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-bash\u0022\u003Edotnet add package CodeBeam.UltimateAuth.EntityFrameworkCore.Bundle\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EThis gives UltimateAuth two useful properties at the same time:\u003C/p\u003E\n\u003Col\u003E\n\u003Cli\u003Eindividual capabilities remain independently replaceable and evolvable;\u003C/li\u003E\n\u003Cli\u003Ethe common path remains simple.\u003C/li\u003E\n\u003C/ol\u003E\n\u003Cp\u003EYou should not need to understand the complete internal dependency graph before building your first application.\u003C/p\u003E\n\u003Chr /\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022client\u0022\u003EClient\u003C/h2\u003E\n\u003Cp\u003EThe Client side has a different packaging model.\u003C/p\u003E\n\u003Cp\u003EThere are \u003Cstrong\u003Eno Client bundles\u003C/strong\u003E.\u003C/p\u003E\n\u003Cp\u003EInstead, applications install the client package designed for their application model.\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003E Client Foundation\n |\n \u002B-------------\u002B-------------\u002B\n | | |\n Blazor MAUI Future\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EFor example, a Blazor application installs:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-bash\u0022\u003Edotnet add package CodeBeam.UltimateAuth.Client.Blazor\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EA future MAUI integration can follow the same model without changing the underlying UltimateAuth Client architecture.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022codebeam.ultimateauth.client\u0022\u003E\u003Ccode\u003ECodeBeam.UltimateAuth.Client\u003C/code\u003E\u003C/h3\u003E\n\u003Cp\u003E\u003Ccode\u003ECodeBeam.UltimateAuth.Client\u003C/code\u003E is the shared client foundation.\u003C/p\u003E\n\u003Cp\u003EIt contains common client contracts, abstractions, flow APIs, state concepts, and runtime-independent client behavior.\u003C/p\u003E\n\u003Cp\u003EIt should be thought of as the foundation on which concrete UltimateAuth client integrations are built.\u003C/p\u003E\n\u003Cp\u003EMost applications should therefore install their platform-specific client package rather than installing \u003Ccode\u003ECodeBeam.UltimateAuth.Client\u003C/code\u003E directly.\u003C/p\u003E\n\u003Chr /\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022codebeam.ultimateauth.client.blazor\u0022\u003E\u003Ccode\u003ECodeBeam.UltimateAuth.Client.Blazor\u003C/code\u003E\u003C/h3\u003E\n\u003Cp\u003E\u003Ccode\u003ECodeBeam.UltimateAuth.Client.Blazor\u003C/code\u003E is the concrete UltimateAuth client integration for Blazor.\u003C/p\u003E\n\u003Cp\u003EIt builds on the Client foundation and adds the Blazor-specific runtime and UI integration required by Blazor applications.\u003C/p\u003E\n\u003Cp\u003EThis includes capabilities such as:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u003Ccode\u003EUAuthApp\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003E\u003Ccode\u003EUAuthLoginForm\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003E\u003Ccode\u003EUAuthStateView\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003Eauthentication state integration\u003C/li\u003E\n\u003Cli\u003EBlazor authorization integration\u003C/li\u003E\n\u003Cli\u003Erouting integration\u003C/li\u003E\n\u003Cli\u003Eauthentication flow components\u003C/li\u003E\n\u003Cli\u003EJavaScript transport integration\u003C/li\u003E\n\u003Cli\u003Eclient lifecycle coordination\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EConceptually:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003ECore\n |\nClient\n |\nClient.Blazor\n |\nYour Blazor Application\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EThis model keeps the shared Client architecture independent from Blazor while still giving Blazor developers one natural package to install.\u003C/p\u003E\n\u003Chr /\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022client-compatibility-packages\u0022\u003EClient Compatibility Packages\u003C/h3\u003E\n\u003Cp\u003ENot every client-side package represents a complete client implementation.\u003C/p\u003E\n\u003Cp\u003ESome packages exist to bridge UltimateAuth with a host framework.\u003C/p\u003E\n\u003Ch4 id=\u0022codebeam.ultimateauth.client.aspnetcore\u0022\u003E\u003Ccode\u003ECodeBeam.UltimateAuth.Client.AspNetCore\u003C/code\u003E\u003C/h4\u003E\n\u003Cp\u003E\u003Ccode\u003ECodeBeam.UltimateAuth.Client.AspNetCore\u003C/code\u003E is a lightweight ASP.NET Core compatibility layer for UltimateAuth client applications.\u003C/p\u003E\n\u003Cp\u003EIt allows client-oriented applications to integrate with ASP.NET Core authentication and authorization infrastructure without introducing the full UltimateAuth Server runtime.\u003C/p\u003E\n\u003Cp\u003EThis is particularly useful for hosting models such as Blazor Web App with Interactive WebAssembly.\u003C/p\u003E\n\u003Cp\u003EIt can enable compatibility with ASP.NET Core features such as \u003Ccode\u003E[Authorize]\u003C/code\u003E, but it does \u003Cstrong\u003Enot\u003C/strong\u003E implement authentication itself and does not turn the application into an UltimateAuth Server.\u003C/p\u003E\n\u003Chr /\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022application-composition\u0022\u003EApplication Composition\u003C/h2\u003E\n\u003Cp\u003EBecause Server and Client are independent platform sides, different applications can compose UltimateAuth differently.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022server-only\u0022\u003EServer Only\u003C/h3\u003E\n\u003Cp\u003ETypical examples include:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Ededicated authentication servers\u003C/li\u003E\n\u003Cli\u003Ebackend services hosting UltimateAuth\u003C/li\u003E\n\u003Cli\u003Eserver-side identity infrastructure\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EApplication\n|\n\u002B-- Server Bundle\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022client-only\u0022\u003EClient Only\u003C/h3\u003E\n\u003Cp\u003ETypical examples include:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Estandalone Blazor WebAssembly\u003C/li\u003E\n\u003Cli\u003Efuture MAUI applications\u003C/li\u003E\n\u003Cli\u003Eapplications using a remote UAuthHub\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EApplication\n|\n\u002B-- Client.Blazor\n |\n \u002B---- remote UltimateAuth Server / UAuthHub\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022server-client\u0022\u003EServer \u002B Client\u003C/h3\u003E\n\u003Cp\u003ESome applications host UltimateAuth and consume its client API in the same application.\u003C/p\u003E\n\u003Cp\u003EBlazor Server is a common example.\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EBlazor Server Application\n|\n\u002B-- Server Bundle\n|\n\u002B-- Client.Blazor\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EThe two sides remain architecturally distinct even when deployed in the same process.\u003C/p\u003E\n\u003Chr /\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022plugin-domains\u0022\u003EPlugin Domains\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth extends its identity platform through \u003Cstrong\u003EPlugin Domains\u003C/strong\u003E.\u003C/p\u003E\n\u003Cp\u003EThe current primary Plugin Domains are:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EUsers\u003C/li\u003E\n\u003Cli\u003ECredentials\u003C/li\u003E\n\u003Cli\u003EAuthorization\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EA Plugin Domain is more than a single assembly. It is a capability area with contracts, runtime behavior, recommended implementations, and infrastructure implementations.\u003C/p\u003E\n\u003Cp\u003EThis structure allows a domain to remain replaceable without requiring applications to redesign the rest of UltimateAuth.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022anatomy-of-a-plugin-domain\u0022\u003EAnatomy of a Plugin Domain\u003C/h3\u003E\n\u003Cp\u003EA typical Plugin Domain follows this model:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EPlugin Domain\n|\n\u002B-- Contracts\n|\n\u002B-- Domain / Runtime\n|\n\u002B-- Reference\n|\n\u002B-- Persistence\n |\n \u002B-- InMemory\n \u002B-- EntityFrameworkCore\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EEach layer has a distinct purpose.\u003C/p\u003E\n\u003Chr /\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022contracts\u0022\u003EContracts\u003C/h3\u003E\n\u003Cp\u003EExample:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003ECodeBeam.UltimateAuth.Users.Contracts\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EContracts define the public boundary of the domain. Server and Client can communicate through same contracts without requiring one to depend on the other.\u003C/p\u003E\n\u003Cp\u003EThey are intentionally kept close to Core and represent the smallest dependency surface for applications or extensions that only need to interact with the domain contract.\u003C/p\u003E\n\u003Cp\u003EDepending on the domain, contracts can contain concepts such as:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Erequests\u003C/li\u003E\n\u003Cli\u003Eresults\u003C/li\u003E\n\u003Cli\u003Epublic models\u003C/li\u003E\n\u003Cli\u003Eabstractions\u003C/li\u003E\n\u003Cli\u003Edomain-facing contracts\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EThis allows another implementation to integrate with UltimateAuth without depending on the entire built-in implementation.\u003C/p\u003E\n\u003Chr /\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022domain-runtime\u0022\u003EDomain / Runtime\u003C/h3\u003E\n\u003Cp\u003EExample:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003ECodeBeam.UltimateAuth.Users\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EThe main domain project provides the runtime and application behavior of that Plugin Domain.\u003C/p\u003E\n\u003Cp\u003EIt operates against abstractions rather than requiring one persistence implementation.\u003C/p\u003E\n\u003Chr /\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022reference-implementations\u0022\u003EReference Implementations\u003C/h3\u003E\n\u003Cp\u003EExample:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003ECodeBeam.UltimateAuth.Users.Reference\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EReference packages contain the implementation UltimateAuth recommends as the standard baseline for the domain.\u003C/p\u003E\n\u003Cp\u003EThe distinction between \u003Cstrong\u003Econtract\u003C/strong\u003E and \u003Cstrong\u003Ereference implementation\u003C/strong\u003E is intentional.\u003C/p\u003E\n\u003Cp\u003EUltimateAuth does not leave extensibility points empty and require every developer to design critical identity behavior from scratch.\u003C/p\u003E\n\u003Cp\u003EInstead, the platform provides:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EContract\n |\n \u002B---- UltimateAuth Reference Implementation\n |\n \u002B---- Your Custom Implementation\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EFor the common path, use the UltimateAuth reference implementation.\u003C/p\u003E\n\u003Cp\u003EFor specialized requirements, replace the appropriate implementation while preserving the surrounding platform contract and security boundaries.\u003C/p\u003E\n\u003Cp\u003EThis pattern gives advanced applications extensibility without making simple applications incomplete.\u003C/p\u003E\n\u003Chr /\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022persistence-implementations\u0022\u003EPersistence Implementations\u003C/h3\u003E\n\u003Cp\u003EPlugin Domains can provide persistence-specific packages independently.\u003C/p\u003E\n\u003Cp\u003EFor example:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EUsers\n|\n\u002B-- Users.Contracts\n\u002B-- Users\n\u002B-- Users.Reference\n\u002B-- Users.InMemory\n\u002B-- Users.EntityFrameworkCore\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EThe same pattern applies to other Plugin Domains where appropriate.\u003C/p\u003E\n\u003Cp\u003EA developer using a server bundle normally does not need to install these packages individually. The selected bundle composes the appropriate persistence strategy.\u003C/p\u003E\n\u003Cp\u003ETheir separation exists primarily to preserve architectural modularity and replaceability.\u003C/p\u003E\n\u003Chr /\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022current-plugin-domains\u0022\u003ECurrent Plugin Domains\u003C/h2\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022users\u0022\u003EUsers\u003C/h3\u003E\n\u003Cp\u003EThe Users domain owns user lifecycle and user-management capabilities.\u003C/p\u003E\n\u003Cp\u003EIts package family includes:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003ECodeBeam.UltimateAuth.Users.Contracts\nCodeBeam.UltimateAuth.Users\nCodeBeam.UltimateAuth.Users.Reference\nCodeBeam.UltimateAuth.Users.InMemory\nCodeBeam.UltimateAuth.Users.EntityFrameworkCore\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022credentials\u0022\u003ECredentials\u003C/h3\u003E\n\u003Cp\u003EThe Credentials domain owns credential-related capabilities and credential management.\u003C/p\u003E\n\u003Cp\u003EIts package family includes:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003ECodeBeam.UltimateAuth.Credentials.Contracts\nCodeBeam.UltimateAuth.Credentials\nCodeBeam.UltimateAuth.Credentials.Reference\nCodeBeam.UltimateAuth.Credentials.InMemory\nCodeBeam.UltimateAuth.Credentials.EntityFrameworkCore\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022authorization\u0022\u003EAuthorization\u003C/h3\u003E\n\u003Cp\u003EThe Authorization domain provides UltimateAuth authorization capabilities.\u003C/p\u003E\n\u003Cp\u003EIts package family includes:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003ECodeBeam.UltimateAuth.Authorization.Contracts\nCodeBeam.UltimateAuth.Authorization\nCodeBeam.UltimateAuth.Authorization.Reference\nCodeBeam.UltimateAuth.Authorization.InMemory\nCodeBeam.UltimateAuth.Authorization.EntityFrameworkCore\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EThe same architectural pattern makes these capabilities independently evolvable while keeping their public boundaries explicit.\u003C/p\u003E\n\u003Chr /\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022essential-server-capabilities\u0022\u003EEssential Server Capabilities\u003C/h2\u003E\n\u003Cp\u003EAuthentication, Sessions, and Tokens are slightly different from Plugin Domains.\u003C/p\u003E\n\u003Cp\u003EThey are fundamental parts of an UltimateAuth Server and are not treated as optional identity plugins in the same sense as Users, Credentials, or Authorization.\u003C/p\u003E\n\u003Cp\u003EHowever, they are still separated into focused projects.\u003C/p\u003E\n\u003Cp\u003EConceptually:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003E UltimateAuth Server\n |\n \u002B--------------------\u002B--------------------\u002B\n | | |\n Authentication Sessions Tokens\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EWhy separate them if the Server needs them?\u003C/p\u003E\n\u003Cp\u003EBecause \u003Cstrong\u003Erequired does not have to mean monolithic\u003C/strong\u003E.\u003C/p\u003E\n\u003Cp\u003EKeeping these capabilities isolated:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eprevents the central Server project from becoming an oversized implementation assembly;\u003C/li\u003E\n\u003Cli\u003Ekeeps responsibilities and dependency boundaries explicit;\u003C/li\u003E\n\u003Cli\u003Eallows implementations to evolve independently;\u003C/li\u003E\n\u003Cli\u003Emakes persistence providers independently composable;\u003C/li\u003E\n\u003Cli\u003Ekeeps replacement and testing boundaries smaller.\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EThey can be thought of as \u003Cstrong\u003Emodular server subsystems\u003C/strong\u003E rather than full Plugin Domains.\u003C/p\u003E\n\u003Cp\u003EFor example, persistence implementations can still exist independently:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EAuthentication\n\u002B-- Authentication.InMemory\n\u002B-- Authentication.EntityFrameworkCore\n\nSessions\n\u002B-- Sessions.InMemory\n\u002B-- Sessions.EntityFrameworkCore\n\nTokens\n\u002B-- Tokens.InMemory\n\u002B-- Tokens.EntityFrameworkCore\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EThe selected Server bundle composes the required implementations for the application.\u003C/p\u003E\n\u003Chr /\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022policies-and-security\u0022\u003EPolicies and Security\u003C/h2\u003E\n\u003Cp\u003ESome capabilities are deliberately isolated even though they support multiple areas of the platform.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022policies\u0022\u003EPolicies\u003C/h3\u003E\n\u003Cp\u003E\u003Ccode\u003ECodeBeam.UltimateAuth.Policies\u003C/code\u003E contains policy infrastructure used to keep security and application decisions explicit and extensible.\u003C/p\u003E\n\u003Cp\u003EPolicies are part of the server architecture rather than application-specific transport logic.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022security-implementations\u0022\u003ESecurity Implementations\u003C/h3\u003E\n\u003Cp\u003ESecurity algorithms can also live behind focused implementations.\u003C/p\u003E\n\u003Cp\u003EFor example:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003ECodeBeam.UltimateAuth.Security.Argon2\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EKeeping security implementations separate avoids coupling the entire platform to one concrete algorithm or provider.\u003C/p\u003E\n\u003Chr /\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022modularity-without-an-incomplete-framework\u0022\u003EModularity Without an Incomplete Framework\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth\u0027s modularity has an important design goal:\u003C/p\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003EExtensibility should not require developers to build the missing half of the framework themselves.\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Cp\u003EEvery major extensibility boundary should have a practical UltimateAuth-provided path.\u003C/p\u003E\n\u003Cp\u003EThe common model is:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003E UltimateAuth Contract\n |\n \u002B-----------\u002B-----------\u002B\n | |\n Reference Path Custom Path\n | |\n Works out of the box Replace when needed\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EThis means the platform can provide strong defaults and reference implementations while still allowing advanced applications to replace individual capabilities.\u003C/p\u003E\n\u003Cp\u003EA customization should be local to the capability being replaced rather than requiring the authentication architecture to be rewritten.\u003C/p\u003E\n\u003Chr /\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022replaceability-by-design\u0022\u003EReplaceability by Design\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth is intentionally built from small architectural boundaries.\u003C/p\u003E\n\u003Cp\u003EThe goal is not modularity for its own sake.\u003C/p\u003E\n\u003Cp\u003EThe goal is to reduce the cost of change.\u003C/p\u003E\n\u003Cp\u003EAn application may start with:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EEntityFrameworkCore.Bundle\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003Ewhile still having clearly separated domains and infrastructure underneath.\u003C/p\u003E\n\u003Cp\u003EIf a specialized application later needs a custom implementation for one capability, the architecture already has a boundary for it.\u003C/p\u003E\n\u003Cp\u003EThis provides a different model from choosing between two extremes:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003ESimple but monolithic\n OR\nFlexible but difficult to configure\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EUltimateAuth aims for:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003E Simple installation\n \u002B\n Modular internals\n \u002B\n Reference implementations\n \u002B\n Explicit contracts\n =\n Low-cost customization\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003ESecurity-sensitive extension points still remain subject to UltimateAuth\u0027s security invariants and authority boundaries. Replaceability is intended to enable integration and specialization, not to bypass required security decisions.\u003C/p\u003E\n\u003Chr /\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022choosing-packages\u0022\u003EChoosing Packages\u003C/h2\u003E\n\u003Cp\u003EFor most developers, package selection should be simple.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022blazor-server-with-inmemory-persistence\u0022\u003EBlazor Server with InMemory persistence\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-bash\u0022\u003Edotnet add package CodeBeam.UltimateAuth.InMemory.Bundle\ndotnet add package CodeBeam.UltimateAuth.Client.Blazor\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EThe application both hosts UltimateAuth and consumes the Blazor client integration.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022blazor-server-with-entity-framework-core\u0022\u003EBlazor Server with Entity Framework Core\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-bash\u0022\u003Edotnet add package CodeBeam.UltimateAuth.EntityFrameworkCore.Bundle\ndotnet add package CodeBeam.UltimateAuth.Client.Blazor\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022custom-persistence\u0022\u003ECustom persistence\u003C/h3\u003E\n\u003Cp\u003EStart from:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-bash\u0022\u003Edotnet add package CodeBeam.UltimateAuth.Reference.Bundle\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003Eand provide the required persistence implementations for your architecture.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022standalone-blazor-webassembly\u0022\u003EStandalone Blazor WebAssembly\u003C/h3\u003E\n\u003Cp\u003EInstall the client integration:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-bash\u0022\u003Edotnet add package CodeBeam.UltimateAuth.Client.Blazor\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EThe authentication authority lives in a remote UltimateAuth Server or UAuthHub.\u003C/p\u003E\n\u003Chr /\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022the-rule-of-thumb\u0022\u003EThe Rule of Thumb\u003C/h2\u003E\n\u003Cp\u003EYou generally do not need to assemble UltimateAuth package-by-package.\u003C/p\u003E\n\u003Cp\u003EFor the \u003Cstrong\u003EServer\u003C/strong\u003E:\u003C/p\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003EChoose the Bundle that matches your infrastructure.\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Cp\u003EFor the \u003Cstrong\u003EClient\u003C/strong\u003E:\u003C/p\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003EChoose the Client package that matches your application platform.\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003ESERVER\n\u0026quot;What infrastructure do I use?\u0026quot;\n |\n \u002B-- Reference.Bundle\n \u002B-- InMemory.Bundle\n \u002B-- EntityFrameworkCore.Bundle\n\n\nCLIENT\n\u0026quot;What kind of application am I building?\u0026quot;\n |\n \u002B-- Client.Blazor\n \u002B-- Client.Maui (future)\n \u002B-- ...\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EThe detailed package graph exists so that UltimateAuth remains extensible.\u003C/p\u003E\n\u003Cp\u003EThe bundles and client integrations exist so that you do not have to manage that graph for ordinary applications.\u003C/p\u003E\n\u003Chr /\u003E\n\u003Ch1 id=\u0022next\u0022\u003ENext\u003C/h1\u003E\n\u003Cp\u003EYou now have the mental model needed to understand the rest of the UltimateAuth documentation:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003ECore\n |\n \u002B-- Server --\u0026gt; choose a Bundle\n |\n \u002B-- Client --\u0026gt; choose your platform integration\n\nServer\n |\n \u002B-- Essential server subsystems\n |\n \u002B-- Plugin Domains\n |\n \u002B-- Contracts\n \u002B-- Runtime\n \u002B-- Reference implementations\n \u002B-- Persistence implementations\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EContinue with the \u003Ca href=\u0022./quickstart.md\u0022\u003EQuick Start\u003C/a\u003E to build your first UltimateAuth application.\u003C/p\u003E\n\u003Cp\u003EFor persistent storage, UAuthHub, standalone WebAssembly, Resource API, and other deployment models, continue with the \u003Ca href=\u0022./real-world-setup.md\u0022\u003EReal-World Setup\u003C/a\u003E.\u003C/p\u003E\n", "Headings": [ { "Id": "platform-at-a-glance", @@ -9,143 +9,193 @@ "Level": 0 }, { - "Id": "codebeam.ultimateauth.core", - "Text": "CodeBeam.UltimateAuth.Core", + "Id": "core", + "Text": "Core", + "Level": 0 + }, + { + "Id": "server", + "Text": "Server", "Level": 0 }, { "Id": "do-i-install-codebeam.ultimateauth.server-directly", "Text": "Do I install CodeBeam.UltimateAuth.Server directly?", - "Level": 0 + "Level": 1 }, { - "Id": "reference-bundle", - "Text": "Reference Bundle", + "Id": "server-bundles", + "Text": "Server Bundles", "Level": 0 }, { - "Id": "codebeam.ultimateauth.reference.bundle", - "Text": "CodeBeam.UltimateAuth.Reference.Bundle", + "Id": "reference-bundle", + "Text": "Reference Bundle", "Level": 1 }, { "Id": "inmemory-bundle", "Text": "InMemory Bundle", - "Level": 0 - }, - { - "Id": "codebeam.ultimateauth.inmemory.bundle", - "Text": "CodeBeam.UltimateAuth.InMemory.Bundle", "Level": 1 }, { "Id": "entity-framework-core-bundle", "Text": "Entity Framework Core Bundle", - "Level": 0 + "Level": 1 }, { - "Id": "codebeam.ultimateauth.entityframeworkcore.bundle", - "Text": "CodeBeam.UltimateAuth.EntityFrameworkCore.Bundle", + "Id": "why-bundles-matter", + "Text": "Why Bundles Matter", "Level": 1 }, + { + "Id": "client", + "Text": "Client", + "Level": 0 + }, { "Id": "codebeam.ultimateauth.client", "Text": "CodeBeam.UltimateAuth.Client", - "Level": 0 + "Level": 1 }, { "Id": "codebeam.ultimateauth.client.blazor", "Text": "CodeBeam.UltimateAuth.Client.Blazor", - "Level": 0 + "Level": 1 }, { - "Id": "codebeam.ultimateauth.client.aspnetcore", - "Text": "CodeBeam.UltimateAuth.Client.AspNetCore", + "Id": "client-compatibility-packages", + "Text": "Client Compatibility Packages", + "Level": 1 + }, + { + "Id": "application-composition", + "Text": "Application Composition", "Level": 0 }, { "Id": "server-only", "Text": "Server Only", - "Level": 0 + "Level": 1 }, { "Id": "client-only", "Text": "Client Only", - "Level": 0 + "Level": 1 }, { "Id": "server-client", "Text": "Server \u002B Client", + "Level": 1 + }, + { + "Id": "plugin-domains", + "Text": "Plugin Domains", "Level": 0 }, { "Id": "anatomy-of-a-plugin-domain", "Text": "Anatomy of a Plugin Domain", - "Level": 0 + "Level": 1 }, { "Id": "contracts", "Text": "Contracts", - "Level": 0 + "Level": 1 }, { "Id": "domain-runtime", "Text": "Domain / Runtime", - "Level": 0 + "Level": 1 }, { "Id": "reference-implementations", "Text": "Reference Implementations", - "Level": 0 + "Level": 1 }, { "Id": "persistence-implementations", "Text": "Persistence Implementations", + "Level": 1 + }, + { + "Id": "current-plugin-domains", + "Text": "Current Plugin Domains", "Level": 0 }, { "Id": "users", "Text": "Users", - "Level": 0 + "Level": 1 }, { "Id": "credentials", "Text": "Credentials", - "Level": 0 + "Level": 1 }, { "Id": "authorization", "Text": "Authorization", + "Level": 1 + }, + { + "Id": "essential-server-capabilities", + "Text": "Essential Server Capabilities", + "Level": 0 + }, + { + "Id": "policies-and-security", + "Text": "Policies and Security", "Level": 0 }, { "Id": "policies", "Text": "Policies", - "Level": 0 + "Level": 1 }, { "Id": "security-implementations", "Text": "Security Implementations", + "Level": 1 + }, + { + "Id": "modularity-without-an-incomplete-framework", + "Text": "Modularity Without an Incomplete Framework", + "Level": 0 + }, + { + "Id": "replaceability-by-design", + "Text": "Replaceability by Design", + "Level": 0 + }, + { + "Id": "choosing-packages", + "Text": "Choosing Packages", "Level": 0 }, { "Id": "blazor-server-with-inmemory-persistence", "Text": "Blazor Server with InMemory persistence", - "Level": 0 + "Level": 1 }, { "Id": "blazor-server-with-entity-framework-core", "Text": "Blazor Server with Entity Framework Core", - "Level": 0 + "Level": 1 }, { "Id": "custom-persistence", "Text": "Custom persistence", - "Level": 0 + "Level": 1 }, { "Id": "standalone-blazor-webassembly", "Text": "Standalone Blazor WebAssembly", + "Level": 1 + }, + { + "Id": "the-rule-of-thumb", + "Text": "The Rule of Thumb", "Level": 0 } ] diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm/wwwroot/app.css b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm/wwwroot/app.css index 9811aa15..1af0b457 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm/wwwroot/app.css +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm/wwwroot/app.css @@ -69,7 +69,7 @@ h1:focus { .ua-appbar-grid { display: grid; - grid-template-columns: 1fr auto 1fr; + grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); align-items: center; width: 100%; } @@ -79,6 +79,8 @@ h1:focus { align-items: center; justify-content: flex-start; gap: 4px; + min-width: 0; + width: 100%; } .ua-center { @@ -95,6 +97,8 @@ h1:focus { display: flex; justify-content: flex-end; align-items: center; + min-width: 0; + width: 100%; } .ua-gradient { @@ -198,6 +202,192 @@ h1:focus { z-index: 9999; } +.ua-controls-enter { + display: flex; + align-items: center; + animation: ua-fade-in 450ms ease-out both; +} + +.ua-right-controls { + justify-content: flex-end; +} + +/* Responsive loading line */ +.ua-nav-shimmer { + display: flex; + align-items: center; + width: clamp(90px, 22vw, 460px); + max-width: 100%; + height: 40px; + padding: 0 12px; +} + +.ua-nav-shimmer-right { + margin-left: auto; +} + +/* Track */ +.ua-nav-shimmer-track { + position: relative; + display: block; + width: 100%; + height: 2px; + border-radius: 999px; + background: color-mix( in srgb, var(--mud-palette-primary) 18%, transparent ); +} + + /* Moving neon glow */ + .ua-nav-shimmer-track::after { + content: ""; + position: absolute; + top: 0; + left: 0; + width: 38%; + height: 100%; + border-radius: inherit; + background: linear-gradient( 90deg, transparent 0%, var(--mud-palette-primary) 50%, transparent 100% ); + filter: drop-shadow( 0 0 5px var(--mud-palette-primary) ); + animation: ua-shimmer 1.8s ease-in-out infinite; + } + +.ua-nav-shimmer-right .ua-nav-shimmer-track::after { + animation-delay: -0.9s; +} + +@keyframes ua-shimmer { + from { + transform: translateX(-100%); + } + + to { + transform: translateX(165%); + } +} + +@keyframes ua-fade-in { + from { + opacity: 0; + transform: translateY(4px); + } + + to { + opacity: 1; + transform: translateY(0); + } +} + +@media (prefers-reduced-motion: reduce) { + .ua-controls-enter, + .ua-nav-shimmer-track::after { + animation: none; + } +} + + +/* Sidebar container */ +.ua-docs-sidebar { + min-height: 20vh; + max-height: 90vh; + overflow-y: auto; + scrollbar-width: thin; + scrollbar-color: var(--mud-palette-lines-default) transparent; +} + +/* Header */ +.ua-docs-header { + padding: 8px 8px 12px; +} + +.ua-docs-brand { + display: flex; + flex-direction: column; + gap: 2px; +} + + .ua-docs-brand .mud-typography { + font-weight: 650; + letter-spacing: -0.025em; + } + + .ua-docs-brand span { + font-size: 10px; + font-weight: 600; + letter-spacing: 0.13em; + color: var(--mud-palette-text-secondary); + } + +/* Navigation */ +.ua-docs-nav { + padding: 4px 0; +} + + /* Compact navigation links */ + .ua-docs-nav .mud-nav-link { + display: flex; + align-items: center; + min-height: 40px; + padding-top: 0; + padding-bottom: 0; + font-size: 0.875rem; + line-height: 1.35; + border-radius: 6px; + transition: background-color 160ms ease, color 160ms ease; + } + + /* Group headers */ + .ua-docs-nav .ua-docs-group > .mud-nav-link { + margin: 0; + min-height: 44px; + padding-top: 0; + padding-bottom: 0; + display: flex; + align-items: center; + font-size: 0.78rem; + font-weight: 650; + letter-spacing: 0.035em; + color: var(--mud-palette-text-secondary); + } + + /* Nested links */ + .ua-docs-nav .ua-docs-item { + font-weight: 400; + color: var(--mud-palette-text-secondary); + } + + /* Selected page */ + .ua-docs-nav .mud-nav-link.active { + background: color-mix( in srgb, var(--mud-palette-primary) 9%, transparent ); + color: var(--mud-palette-text-primary); + font-weight: 600; + box-shadow: inset 3px 0 0 var(--mud-palette-primary); + } + + /* Hover */ + .ua-docs-nav .mud-nav-link:not(.active):hover { + background: var(--mud-palette-action-default-hover); + color: var(--mud-palette-text-primary); + } + + /* Keyboard navigation */ + .ua-docs-nav .mud-nav-link:focus-visible { + outline: 2px solid var(--mud-palette-primary); + outline-offset: -2px; + } + +/* Accessibility */ +@media (prefers-reduced-motion: reduce) { + .ua-docs-nav .mud-nav-link { + transition: none; + } +} + +.ua-docs-toc { + max-height: calc(100dvh - 100px); + overflow-y: auto; + overscroll-behavior: contain; + scrollbar-width: thin; + scrollbar-color: var(--mud-palette-lines-default) transparent; +}