diff --git a/README.md b/README.md index c1f1e024..67c0b52a 100644 --- a/README.md +++ b/README.md @@ -121,50 +121,48 @@ Traditional auth solutions struggle here β€” UltimateAuth embraces it. --- # πŸš€ Quick Start -> ⏱ Takes ~2 minutes to get started +> ⏱ Get started in minutes > > **This Quick Start uses a Blazor Server application with in-memory persistence.** It is intentionally designed as the simplest path to a working UltimateAuth application. > For Entity Framework Core, Blazor WebAssembly, Blazor Web App, Resource API, persistent storage, and other real-world configurations, see the [Real-World Setup guide](https://github.com/CodeBeamOrg/UltimateAuth/blob/dev/docs/content/getting-started/real-world-setup.md). -### 1) Install UltimateAuth +UltimateAuth also provides ready-to-use login components, authentication state management, session handling, and a unified client API. + +> **This Quick Start provides a simplified integration overview.** +> For a complete, copy-paste-ready Blazor Server application, +> see the [Full Quick Start Guide](https://ultimateauth.com/docs/getting-started/quickstart) +> or +> [GitHub Quick Start Page](https://github.com/CodeBeamOrg/UltimateAuth/blob/dev/docs/content/getting-started/quickstart.md). + +### 1. Install ```bash -dotnet add package CodeBeam.UltimateAuth.InMemory.Bundle dotnet add package CodeBeam.UltimateAuth.Client.Blazor +dotnet add package CodeBeam.UltimateAuth.InMemory.Bundle ``` -### 2) Configure UltimateAuth - -Register UltimateAuth in `Program.cs`: +### 2. Register services ```csharp -// Server registration -builder.Services - .AddUltimateAuthServer() +builder.Services.AddUltimateAuthServer() .AddUltimateAuthInMemory(); -// Client registration builder.Services.AddUltimateAuthClientBlazor(); ``` - **Usage by application type:** -- **Blazor Server App** β†’ Use both Server and Client registrations +- **Blazor Server App / UAuthHub (Auth Server)** β†’ Use both Server and Client registrations - **Blazor WASM / MAUI** β†’ Use Client only -- **UAuthHub (Auth Server) / Resource API** β†’ Use Server only +- **Resource API** β†’ Use own ResourceApi registration -### 3) Configure the Application Pipeline -Add the UltimateAuth middleware and endpoints: +### 3. Configure the pipeline ```csharp -// app.UseHttpsRedirection(); -// app.UseStaticFiles(); - -app.UseUltimateAuthWithAspNetCore(); // Includes UseAuthentication() and UseAuthorization() -// Place Antiforgery or something else before endpoint registration if needed +app.UseUltimateAuthWithAspNetCore(); +app.UseAntiforgery(); app.MapUltimateAuthEndpoints(); app.MapRazorComponents() @@ -172,7 +170,7 @@ app.MapRazorComponents() .AddUltimateAuthRoutes(UAuthAssemblies.BlazorClient()); ``` -### 4) Add UAuthApp +### 4. Add UAuthApp UltimateAuth uses `UAuthApp` as the root integration point for its client authentication state and Blazor lifecycle. Replace the default router in your `App.razor` or `Routes.razor` with: @@ -191,58 +189,61 @@ Replace the default router in your `App.razor` or `Routes.razor` with: ``` -`UAuthApp` can provide the built-in router, authentication state, and UltimateAuth client lifecycle integration for your component tree. - -> Need full control over routing? - -> UAuthApp also supports applications that provide their own Blazor Router. See the Blazor Routing guide for advanced routing configuration. - -### 5) Add the UltimateAuth Client Script +### 5. Add the UltimateAuth Client Script Place this in `App.razor` or `index.html` in your Blazor client application: ```html ``` -### 6) Optional: Blazor Usings -Add this in `_Imports.razor`: -```csharp -@using CodeBeam.UltimateAuth.Client.Blazor -``` +### 6. Create Your Login Page -### 7) Optional: Add Sample Data -For the fastest way to try auth process, install the UltimateAuth sample seed package: +Change `Components/Pages/Home.razor`: -```bash -dotnet add package CodeBeam.UltimateAuth.Sample.Seed +```razor +@page "/" +@page "/login" +@attribute [UAuthLoginPage] +@inherits UAuthPageBase + +
+

Sign in

+ +
+ + +
+ +
+ + +
+ + +
+
+ +@code { + private string _identifier = ""; + private string _password = ""; +} ``` -Register the development seed: - -```bash -builder.Services.AddUltimateAuthSampleSeed(); -``` +### 7. Protect your pages -Then seed the application during development: +```razor +@page "/dashboard" +@attribute [UAuthAuthorize] +@inherits UAuthPageBase -```csharp -if (app.Environment.IsDevelopment()) -{ - await app.SeedUltimateAuthAsync(); -} +

Dashboard

+

Welcome, @AuthState?.Identity?.DisplayName!

``` -The development seed includes ready-to-use accounts: - -| Identifier | Secret | -|------------|----------| -| `admin` | `admin` | -| `user` | `user` | - -You can use these credentials to test the auth flows immediately. +The examples above provide a simplified overview of UltimateAuth integration and are not intended to be a complete, copy-paste-ready application. -> Development only: Sample users and credentials are intended for evaluation and local development. Do not use them in production. +For a **complete, step-by-step setup with all required configuration, components, and sample data**, follow the full Quick Start guide. -### βœ… You're Ready +**[⚑ Complete Quick Start β€” Build a Working Application](https://ultimateauth.com/docs/getting-started/quickstart)** --- diff --git a/docs/content/_groups.json b/docs/content/_groups.json index bae8f92e..e702badb 100644 --- a/docs/content/_groups.json +++ b/docs/content/_groups.json @@ -2,8 +2,9 @@ "getting-started": 1, "fundamentals": 2, "auth-flows": 3, - "plugin-domains": 4, - "client": 5, - "configuration": 6, - "security": 7 + "sessions-devices": 4, + "plugin-domains": 5, + "client": 6, + "configuration": 7, + "security": 8 } \ No newline at end of file diff --git a/docs/content/auth-flows/device-management.md b/docs/content/auth-flows/device-management.md deleted file mode 100644 index a2184b8f..00000000 --- a/docs/content/auth-flows/device-management.md +++ /dev/null @@ -1,166 +0,0 @@ ---- -title: Device Management -order: 7 -group: auth-flows ---- - - -# πŸ“± Device Management - -In UltimateAuth, devices are not an afterthought. - -πŸ‘‰ They are a **first-class concept** - -## 🧠 Why Device Matters - -Most authentication systems ignore devices. - -- A user logs in -- A token is issued -- Everything is treated the same - -πŸ‘‰ This breaks down when you need: - -- Multi-device control -- Session visibility -- Security enforcement - -πŸ‘‰ UltimateAuth solves this with **device-aware authentication** - -## 🧩 Core Concept: Chain = Device - -In UltimateAuth: - -πŸ‘‰ A **SessionChain represents a device** - -``` -Device β†’ Chain β†’ Sessions -``` - -Each chain: - -- Is bound to a device -- Groups sessions -- Tracks activity - -πŸ‘‰ A device is not inferred β€” it is explicitly modeled - -## πŸ”— What Defines a Device? - -A chain includes: - -- DeviceId -- Platform (web, mobile, etc.) -- Operating System -- Browser -- IP (optional binding) - -πŸ‘‰ This forms a **device fingerprint** - -## πŸ”„ Device Lifecycle - -### 1️⃣ First Login - -- New device detected -- New chain is created - -### 2️⃣ Subsequent Logins - -- Same device β†’ reuse chain -- New device β†’ new chain - -πŸ‘‰ Device continuity is preserved - -### 3️⃣ Activity (Touch) - -- Chain `LastSeenAt` updated -- `TouchCount` increases - -πŸ‘‰ Tracks real usage - -### 4️⃣ Token Rotation - -- Session changes -- Chain remains -- `RotationCount` increases - -πŸ‘‰ Device identity stays stable - -### 5️⃣ Logout - -- Session removed -- Chain remains - -πŸ‘‰ Device still trusted - -### 6️⃣ Revoke - -- Chain invalidated -- All sessions removed - -πŸ‘‰ Device trust is reset - -
- -## πŸ” Security Model - -### πŸ”— Device Binding - -Sessions and tokens are tied to: - -- Chain -- Device context - -πŸ‘‰ Prevents cross-device reuse - -### πŸ” Rotation Tracking - -Chains track: - -- RotationCount -- TouchCount - -πŸ‘‰ Enables anomaly detection - -### 🚨 Revoke Cascade - -If a device is compromised: - -- Entire chain can be revoked -- All sessions invalidated - -πŸ‘‰ Immediate containment - -
- -## βš™οΈ Configuration - -Device behavior is configurable via session options: - -- Max chains per user -- Max sessions per chain -- Platform-based limits -- Device mismatch behavior - -πŸ‘‰ Fine-grained control for enterprise scenarios - -
- -## 🧠 Mental Model - -If you remember one thing: - -πŸ‘‰ Device = Chain -πŸ‘‰ Not just metadata - -## πŸ“Œ Key Takeaways - -- Devices are explicitly modeled -- Each device has its own chain -- Sessions belong to chains -- Security is enforced per device -- Logout and revoke operate on device scope - -## ➑️ Next Step - -Continue to **Configuration & Extensibility** diff --git a/docs/content/auth-flows/index.md b/docs/content/auth-flows/overview.md similarity index 98% rename from docs/content/auth-flows/index.md rename to docs/content/auth-flows/overview.md index 8a508958..a3565f06 100644 --- a/docs/content/auth-flows/index.md +++ b/docs/content/auth-flows/overview.md @@ -1,5 +1,5 @@ --- -title: Auth Flows +title: Auth-Flows Overview order: 1 group: auth-flows --- @@ -246,4 +246,4 @@ This is the larger goal of UltimateAuth's unified modelβ€”not to make every auth ## Next step -Start with [Login Flow](./login-flow.md) to see how UltimateAuth turns validated credentials into controlled authenticated access. +Start with [Login Flow](/docs/auth-flows/login) to see how UltimateAuth turns validated credentials into controlled authenticated access. diff --git a/docs/content/auth-flows/session-lifecycle.md b/docs/content/auth-flows/session-lifecycle.md deleted file mode 100644 index 6f79a04c..00000000 --- a/docs/content/auth-flows/session-lifecycle.md +++ /dev/null @@ -1,201 +0,0 @@ ---- -title: Session Lifecycle -order: 5 -group: auth-flows ---- - - -# 🧬 Session Lifecycle -UltimateAuth is built around a structured session model. - -πŸ‘‰ Authentication is not a token -πŸ‘‰ It is a **hierarchical session system** - -## 🧠 Core Model - -UltimateAuth defines three core entities: -``` -Root β†’ Chain β†’ Session -``` - -### πŸ”Ή Root (Identity Authority) -- One per user -- Represents global authentication state -- Holds security version - -πŸ‘‰ Root defines **who the user is** - -### πŸ“± Chain (Device Context) -- One per device -- Represents a device-bound identity context -- Tracks activity (LastSeenAt) -- Manages session rotation and touch - -πŸ‘‰ A chain is a **trusted device boundary** - -### πŸ”‘ Session (Authentication Instance) -- Created on login -- Represents a single authentication event -- Has expiration and revocation state - -πŸ‘‰ A session is a **proof of authentication** - -
- -## πŸ”— Relationship -``` -User -└── Root -└── Chain (Device) -└── Session (Instance) -``` - -πŸ‘‰ Each level adds more specificity: - -- Root β†’ identity -- Chain β†’ device -- Session β†’ login instance - -
- -## πŸ”„ Lifecycle Overview - -### 1️⃣ Creation (Login) -When a user logs in: - -- Root is created (if not exists) -- Chain is resolved or created -- Session is issued - -### 2️⃣ Active Usage -During normal operation: - -- Session is validated -- Chain `LastSeenAt` is updated (touch) -- Sliding expiration may apply - -πŸ‘‰ Activity updates the **chain**, not just the session - -### 3️⃣ Refresh -Depending on mode: - -#### Session-Based (PureOpaque) -- Session remains -- Chain is touched - -#### Token-Based (Hybrid / JWT) -- Session continues -- Tokens are rotated -- Chain rotation count increases - -πŸ‘‰ Chain tracks behavior: - -- RotationCount -- TouchCount - -### 4️⃣ Expiration -A session may expire due to: - -- Lifetime expiration -- Idle timeout -- Absolute expiration - -πŸ‘‰ Expired β‰  revoked - -### 5️⃣ Revocation -Revocation can occur at multiple levels: - -#### Session Revocation -- Single session invalidated - -#### Chain Revocation -- All sessions on device invalidated -- Device trust reset - ---- - -#### Root Revocation - -- All chains and sessions invalidated -- Security version increased - -πŸ‘‰ Revocation is irreversible - -
- -## πŸ” Security Model - -### πŸ”’ Security Versioning -Each root has: -- `SecurityVersion` - -Each session stores: -- `SecurityVersionAtCreation` - ---- - -πŸ‘‰ If mismatch: - -```text -Session becomes invalid -``` - -### πŸ”— Device Binding -Each chain is tied to: - -- DeviceId -- Platform -- OS -- Browser - -πŸ‘‰ Prevents cross-device misuse - -### πŸ” Rotation Tracking -Chains track: - -- RotationCount -- TouchCount - -πŸ‘‰ Enables: - -- replay detection -- anomaly tracking - -### βš™οΈ Lifecycle Configuration -Session behavior is configurable: - -⏱ Lifetime -- Default session duration - -πŸ”„ Sliding Expiration -- Extends session on activity - -πŸ’€ Idle Timeout -- Invalidates inactive sessions - -πŸ“± Device Limits -- Max chains per user -- Max sessions per chain - -πŸ‘‰ These are defined via UAuthSessionOptions - -
- -## 🧠 Mental Model -If you remember one thing: - -πŸ‘‰ Authentication is a living structure -πŸ‘‰ Not a static token - -## πŸ“Œ Key Takeaways -- Sessions are part of a hierarchy -- Device (chain) is a first-class concept -- Root controls global security -- Sessions are short-lived proofs -- Chains manage lifecycle and activity -- Revocation operates at multiple levels - -## ➑️ Next Step - -Continue to Token Behavior - diff --git a/docs/content/auth-flows/token-behavior.md b/docs/content/auth-flows/token-behavior.md deleted file mode 100644 index 18c76126..00000000 --- a/docs/content/auth-flows/token-behavior.md +++ /dev/null @@ -1,169 +0,0 @@ ---- -title: Token Behavior -order: 6 -group: auth-flows ---- - - -# 🎟 Token Behavior -In UltimateAuth, tokens are not the foundation of authentication. - -πŸ‘‰ They are **derived artifacts of a session** - -## 🧠 Rethinking Tokens -In traditional systems: - -- Token = identity -- Token = authentication - -In UltimateAuth: - -πŸ‘‰ Session = identity -πŸ‘‰ Token = transport mechanism - -> Tokens do not define identity -> β†’ Sessions do - -## 🧩 Token Types -UltimateAuth supports two main token types: - -### πŸ”Ή Opaque Tokens -- Random, non-decodable values -- Stored and validated on the server -- Typically reference a session - -πŸ‘‰ Used in: - -- PureOpaque -- Hybrid - -### πŸ”Ή JWT (JSON Web Tokens) -- Self-contained tokens -- Include claims and metadata -- Signed and verifiable without server lookup - -πŸ‘‰ Used in: - -- SemiHybrid -- PureJwt - -
- -## βš–οΈ Mode-Based Behavior -Token behavior depends on the authentication mode: - -| Mode | Access Token | Refresh Token | Behavior | -|-------------|---------------|----------------|---------------------------| -| PureOpaque | ❌ | ❌ | Session-only | -| Hybrid | βœ” (opaque/JWT)| βœ” | Session + token | -| SemiHybrid | βœ” (JWT) | βœ” | JWT + session metadata | -| PureJwt | βœ” (JWT) | βœ” | Fully stateless | - -πŸ‘‰ UltimateAuth selects behavior automatically - -
- -## πŸ”‘ Access Tokens -Access tokens represent: - -πŸ‘‰ A **temporary access grant** - -### Characteristics -- Short-lived -- Mode-dependent format -- May contain session reference (`sid`) -- May include claims - -### Important -Access token is NOT the source of truth. - -πŸ‘‰ It reflects session state, not replaces it - -## πŸ”„ Refresh Tokens -Refresh tokens represent: - -πŸ‘‰ A **continuation mechanism** - -### Characteristics -- Long-lived -- Stored as hashed values -- Bound to session and optionally chain -- Rotated on use - -### Lifecycle -Issued β†’ Used β†’ Replaced β†’ Revoked - -πŸ‘‰ Old tokens are invalidated on rotation - -
- -## πŸ” Security Model - -### πŸ” Rotation -Each refresh: - -- Invalidates previous token -- Issues a new token - -πŸ‘‰ Prevents replay attacks - -### 🚨 Reuse Detection -If a token is reused: - -- Chain may be revoked -- Session may be revoked - -πŸ‘‰ Strong forward security - -### πŸ”— Session Binding -Refresh tokens include: - -- SessionId -- ChainId (optional) - -πŸ‘‰ Prevents cross-context usage - -### πŸ”’ Hashed Storage -Tokens are: - -- Never stored as plaintext -- Hashed using secure algorithms - -πŸ‘‰ Reduces breach impact - -
- -## πŸ”„ Token Issuance -Tokens are issued during: - -- Login -- Refresh - -### Access Token -- May be opaque or JWT -- Includes identity and optional session reference - -### Refresh Token -- Always opaque -- Persisted in secure store -- Used only for rotation - -
- -## 🧠 Mental Model -If you remember one thing: - -πŸ‘‰ Tokens are not identity -πŸ‘‰ They are projections of a session - -## πŸ“Œ Key Takeaways -- Session is the source of truth -- Tokens are optional and mode-dependent -- Opaque tokens require server validation -- JWT tokens allow stateless access -- Refresh tokens enable secure continuation -- Token rotation ensures forward security - -## ➑️ Next Step - -Continue to Device Management diff --git a/docs/content/client-sdk/authentication.md b/docs/content/client-sdk/authentication.md new file mode 100644 index 00000000..88ab5fb8 --- /dev/null +++ b/docs/content/client-sdk/authentication.md @@ -0,0 +1,1270 @@ +--- +title: Authentication +order: 2 +group: client +--- + +# Authentication + +The UltimateAuth Client SDK provides a high-level API for authentication workflows, including login, PKCE, session validation, refresh, logout, and device-specific session revocation. + +These operations are exposed through `UAuthClient.Flows`, which implements the `IFlowClient` contract. + +This guide shows how to use the authentication client in application code, handle structured results, configure interactive behavior, and choose the correct flow for different client environments. + +## 1. Accessing the Authentication Client + +In a Blazor component, inject `IUAuthClient`: + +```razor +@using CodeBeam.UltimateAuth.Client +@inject IUAuthClient UAuthClient +``` + +Authentication operations are available through the `Flows` property. + +```csharp +await UAuthClient.Flows.RefreshAsync(); +await UAuthClient.Flows.ValidateAsync(); +await UAuthClient.Flows.LogoutAsync(); +``` + +The relevant methods are defined by `IFlowClient`. + +| Method | Purpose | +|---|---| +| `LoginAsync` | Submit a direct login flow | +| `TryLoginAsync` | Attempt login with configurable try/commit behavior | +| `BeginPkceAsync` | Start a PKCE authentication flow | +| `CompletePkceLoginAsync` | Complete a PKCE login | +| `TryCompletePkceLoginAsync` | Complete PKCE with structured try/commit behavior | +| `RefreshAsync` | Refresh or continue authentication | +| `ValidateAsync` | Validate current authentication state | +| `LogoutAsync` | Log out the current session | +| `LogoutMyDeviceAsync` | Revoke a selected device-associated chain | +| `LogoutMyOtherDevicesAsync` | Revoke other devices while retaining the current one | +| `LogoutAllMyDevicesAsync` | Revoke all devices for the current user | + +Administrative device logout operations are also available and covered later in this guide. + +## 2. Choosing the Correct Login Flow + +Before implementing login, determine whether your application is a trusted server-hosted client or a public client. + +| Client environment | Recommended flow | +|---|---| +| Blazor Server | Direct login or supported interactive login flow | +| UAuthHub | Server-hosted authentication flow | +| Blazor WebAssembly | PKCE | +| Browser SPA | PKCE | +| Public native application | PKCE | + +Public clients must not rely on embedding a secret or trusting browser-executed code to protect credentials. + +UltimateAuth restricts direct credential posting based on the configured client profile and login options. + +For public clients, use the PKCE flow instead of enabling direct credential posting. + +## 3. Direct Login + +`LoginAsync` performs a direct login submission. + +```csharp +var request = new LoginRequest +{ + Identifier = "user@example.com", + Secret = "password" +}; + +await UAuthClient.Flows.LoginAsync(request); +``` + +The method returns `Task`, not a structured login result. + +It submits the login payload through the configured navigation-based transport. + +Depending on the hosting environment, this may initiate navigation or redirect behavior. + +### LoginRequest Properties + +`LoginRequest` supports the following properties: + +| Property | Purpose | +|---|---| +| `Identifier` | Login identifier | +| `Secret` | Credential secret | +| `CredentialType` | Credential type; defaults to `Password` | +| `Metadata` | Optional request metadata | +| `RequestTokens` | Hint to request tokens when supported | +| `PreviewReceipt` | Optional receipt associated with a prior login preview | + +Example: + +```csharp +var request = new LoginRequest +{ + Identifier = email, + Secret = password, + CredentialType = CredentialType.Password, + RequestTokens = true +}; + +await UAuthClient.Flows.LoginAsync(request); +``` + +`RequestTokens` is a hint, not a guarantee that tokens will be issued. The server's authentication mode and policy remain authoritative. + +### Redirect After Login + +Provide an explicit return URL when necessary: + +```csharp +await UAuthClient.Flows.LoginAsync(request, returnUrl: "/dashboard"); +``` + +The direct login flow resolves its return URL in the following order: + +1. The explicit `returnUrl` argument. +2. `Login.ReturnUrl` option. +3. `DefaultReturnUrl` option. + +For example, client options can specify a default: + +```csharp +options.Login.ReturnUrl = "/dashboard"; +``` + +The return URL is part of the login workflow. It should not be treated as an authorization grant. + +Applications and servers must still enforce their redirect validation rules. + +## 4. Interactive Login with TryLoginAsync + +For interactive forms, `TryLoginAsync` provides structured feedback before or during the authentication commit process. + +```csharp +var result = await UAuthClient.Flows.TryLoginAsync( + new LoginRequest + { + Identifier = email, + Secret = password + }, + UAuthSubmitMode.TryAndCommit); +``` + +This is generally the more useful API for a login form that needs to display validation feedback. + +### Submit Modes + +`UAuthSubmitMode` defines three submission behaviors. + +| Mode | Behavior | +|---|---| +| `TryOnly` | Execute the try operation and return its result without committing authentication | +| `DirectCommit` | Submit directly to the commit endpoint without a separate try operation | +| `TryAndCommit` | Try first, then commit when the try operation succeeds | + +These modes control client workflow, not whether the server validates authentication. + +**DirectCommit does not bypass credential verification or server security requirements.** + +### TryOnly: Validate Without Login + +Use `TryOnly` when you want structured feedback without establishing an authenticated session. + +```csharp +var result = await UAuthClient.Flows.TryLoginAsync( + new LoginRequest + { + Identifier = email, + Secret = password + }, + UAuthSubmitMode.TryOnly); + +if (result.IsSuccess) +{ + // The try operation succeeded. + // Authentication has not been committed. +} +else +{ + // Display the appropriate feedback. +} +``` + +This can be useful for a custom interactive workflow. + +A successful try result must not be interpreted as an authenticated application session. + +### TryAndCommit: Interactive Login + +Use `TryAndCommit` to combine structured validation with authentication commit. + +```csharp +var result = await UAuthClient.Flows.TryLoginAsync( + new LoginRequest + { + Identifier = email, + Secret = password + }, + UAuthSubmitMode.TryAndCommit, + returnUrl: "/dashboard"); + +if (!result.IsSuccess) +{ + // Show validation feedback. +} +``` + +When the try stage succeeds, the client proceeds to the commit operation through its configured transport. + +This can involve navigation, so application code should not assume that execution always continues within the same component instance. + +### DirectCommit: Single Commit Submission + +```csharp +await UAuthClient.Flows.TryLoginAsync( + new LoginRequest + { + Identifier = email, + Secret = password + }, + UAuthSubmitMode.DirectCommit); +``` + +In this mode, the SDK submits the commit operation directly. + +The method returns a `TryLoginResult` with `IsSuccess = true` after the navigation submission completes, but this is not a structured server-side authentication success confirmation. + +If you need structured credential validation feedback, prefer `TryAndCommit`. + +## 5. Handling Login Results + +`TryLoginAsync` returns `TryLoginResult`. + +Its available properties include: + +| Property | Meaning | +|---|---| +| `IsSuccess` | Whether the try operation succeeded | +| `Reason` | Optional authentication failure reason | +| `RemainingAttempts` | Remaining attempts, when provided | +| `LockoutUntilUtc` | Lockout expiration, when provided | +| `RequiresMfa` | Whether additional authentication is required | +| `PreviewReceipt` | Optional receipt for subsequent flow handling | + +Example: + +```csharp +var result = await UAuthClient.Flows.TryLoginAsync(request, UAuthSubmitMode.TryOnly); + +if (!result.IsSuccess) +{ + if (result.LockoutUntilUtc is not null) + { + errorMessage = $"Login temporarily unavailable until {result.LockoutUntilUtc:u}"; + } + else + { + errorMessage = "Unable to sign in."; + } + + return; +} + +if (result.RequiresMfa) +{ + // Continue with the supported additional authentication flow. +} +``` + +Do not expose sensitive internal authentication details unnecessarily. + +For public-facing login forms, generic failure messages may be preferable to displaying every failure reason. + +### Handling Transport and Protocol Errors + +A structured authentication failure is different from a network or protocol failure. + +For example: + +```csharp +try +{ + var result = await UAuthClient.Flows.TryLoginAsync(request, UAuthSubmitMode.TryAndCommit); + + if (!result.IsSuccess) + { + errorMessage = "Unable to sign in."; + } +} +catch (UAuthTransportException) +{ + errorMessage = "Authentication service is unavailable."; +} +catch (UAuthProtocolException) +{ + errorMessage = "An authentication protocol error occurred."; +} +``` + +Use the corresponding UltimateAuth client error namespace when referencing these exception types. + +A failed authentication attempt should normally be handled through the structured try result where supported. Transport and protocol exceptions indicate different failure categories. + +## 6. PKCE Authentication + +PKCE provides a proof mechanism for authorization-code-based authentication flows. + +UltimateAuth supports PKCE using an S256 code challenge. + +The SDK generates a cryptographically random verifier, derives its challenge, and coordinates the authorization flow. + +Public clients such as Blazor WebAssembly should use PKCE rather than direct credential posting. + +### Start PKCE + +```csharp +await UAuthClient.Flows.BeginPkceAsync(); +``` + +You can provide a return URL: + +```csharp +await UAuthClient.Flows.BeginPkceAsync(returnUrl: "/dashboard"); +``` + +The client performs the following operations: + +1. Generates a PKCE code verifier. +2. Derives an S256 code challenge. +3. Sends the authorization request. +4. Receives an authorization code. +5. Invokes the configured authorization callback, if any. +6. Navigates to the configured UAuthHub login flow when automatic redirection is enabled. + +The authorization code and verifier are used later to complete the flow. + +### Configure PKCE + +The relevant client options include: + +```csharp +options.Pkce.Enabled = true; +options.Pkce.AutoRedirect = true; +options.Pkce.ReturnUrl = "/dashboard"; +``` + +PKCE is enabled by default in `UAuthClientPkceLoginFlowOptions`. + +Automatic redirection is also enabled by default. + +### Observe Authorization + +You can register a callback that runs after the authorization code is received and before the automatic Hub redirect. + +```csharp +options.Pkce.OnAuthorized = async response => +{ + // Observe the authorization response. + await Task.CompletedTask; +}; +``` + +This callback is useful for application-specific coordination. + +Do not log authorization codes or expose them to unrelated components. + +### Disable Automatic Redirection + +```csharp +options.Pkce.AutoRedirect = false; +``` + +With this configuration, `BeginPkceAsync` still performs the authorization request and invokes `OnAuthorized`, but it does not automatically navigate to the Hub login endpoint. + +The application becomes responsible for coordinating the remaining flow. + +This is an advanced integration option. + +A custom implementation must preserve the authorization code, verifier, and relevant context securely and complete the flow using the supported protocol. + +## 7. Complete PKCE Login + +`CompletePkceLoginAsync` submits the PKCE completion request. + +```csharp +var request = new PkceCompleteRequest +{ + AuthorizationCode = authorizationCode, + CodeVerifier = codeVerifier, + Identifier = email, + Secret = password, + ReturnUrl = "/dashboard" +}; + +await UAuthClient.Flows.CompletePkceLoginAsync(request); +``` + +The request model includes: + +| Property | Purpose | +|---|---| +| `AuthorizationCode` | Code obtained from the authorization step | +| `CodeVerifier` | Original PKCE verifier | +| `Identifier` | Login identifier | +| `Secret` | Login credential | +| `ReturnUrl` | Optional post-login destination | +| `HubSessionId` | Optional Hub session context | + +The authorization code and verifier must correspond to the same PKCE flow. + +Do not generate a new verifier when completing an existing authorization. + +### Interactive PKCE Completion + +For structured feedback, use `TryCompletePkceLoginAsync`. + +```csharp +var result = await UAuthClient.Flows.TryCompletePkceLoginAsync( + request, + UAuthSubmitMode.TryAndCommit); + +if (!result.IsSuccess) +{ + // Display PKCE login feedback. +} +``` + +The same submit modes are supported: + +- `TryOnly` +- `DirectCommit` +- `TryAndCommit` + +### Try PKCE Without Commit + +```csharp +var result = await UAuthClient.Flows.TryCompletePkceLoginAsync(request, UAuthSubmitMode.TryOnly); +``` + +This returns a `TryPkceLoginResult` without committing a successful login. + +The result includes: + +```csharp +result.IsSuccess; +result.Reason; +result.RemainingAttempts; +result.LockoutUntilUtc; +result.RequiresMfa; +result.RetryWithNewPkce; +``` + +### RetryWithNewPkce + +`RetryWithNewPkce` indicates that the client should begin a new PKCE authorization sequence rather than continuing with the existing authorization material. + +For example: + +```csharp +if (result.RetryWithNewPkce) +{ + await UAuthClient.Flows.BeginPkceAsync("/login"); + return; +} +``` + +This is useful when the current PKCE context can no longer be used. + +The application should not repeatedly resubmit the same invalid authorization code and verifier. + +## 8. Direct Credential Posting Restrictions + +UltimateAuth restricts direct credential posting according to the configured client profile. + +The client option is: + +```csharp +options.Login.AllowCredentialPost = false; +``` + +This is the default. + +If the current profile is not permitted to post credentials directly and the option has not enabled that capability, direct login methods throw an `InvalidOperationException`. + +For example, attempting direct login from a public client may produce an error indicating that PKCE is required. + +### Trusted Server-Hosted Clients + +The option can be explicitly configured for a trusted server-hosted integration where direct credential posting is appropriate: + +```csharp +options.Login.AllowCredentialPost = true; +``` + +This must not be used to bypass PKCE requirements in a public browser application. + +**Do not enable direct credential posting in production Blazor WebAssembly or SPA clients.** + +A client profile is an integration descriptor, not cryptographic proof that the caller is trusted. + +Server-side security requirements remain authoritative. + +## 9. Refresh Authentication + +`RefreshAsync` requests continuation of the current authentication state. + +```csharp +var result = await UAuthClient.Flows.RefreshAsync(); +``` + +The method returns `RefreshResult`. + +```csharp +public sealed record RefreshResult +{ + public bool IsSuccess { get; init; } + public int Status { get; init; } + public RefreshOutcome Outcome { get; init; } +} +``` + +Refresh behavior depends on the authentication mode. + +A refresh operation may update session activity, rotate authentication material, or determine that reauthentication is required. + +### Handling Refresh Results + +```csharp +var result = await UAuthClient.Flows.RefreshAsync(); + +if (result.IsSuccess) +{ + // Authentication continuation succeeded. +} +else if (result.Outcome == RefreshOutcome.ReauthRequired) +{ + // The current authentication can no longer continue. + // Direct the user to the appropriate login flow. +} +else +{ + // Handle another unsuccessful refresh result. +} +``` + +A `401 Unauthorized` refresh response is handled as a normal reauthentication outcome. + +It returns: + +```csharp +new RefreshResult +{ + IsSuccess = false, + Status = 401, + Outcome = RefreshOutcome.ReauthRequired +}; +``` + +Network and unexpected protocol failures can still produce exceptions. + +### Refresh Outcomes + +`RefreshOutcome` defines the following values: + +| Outcome | Meaning | +|---|---| +| `None` | No specific semantic outcome | +| `Success` | Generic successful refresh | +| `NoOp` | No refresh state change was necessary | +| `Touched` | Applicable session activity was updated | +| `Rotated` | Rotation occurred | +| `ReauthRequired` | Authentication continuation is no longer permitted | + +For example: + +```csharp +var result = await UAuthClient.Flows.RefreshAsync(); + +if (!result.IsSuccess) +{ + return; +} + +switch (result.Outcome) +{ + case RefreshOutcome.Rotated: + // Authentication material was rotated. + break; + + case RefreshOutcome.Touched: + // Applicable session activity was updated. + break; + + case RefreshOutcome.NoOp: + // Successful refresh and no state change was required. + break; + + case RefreshOutcome.Success: + // Refresh succeeded without a more specific outcome. + break; +} +``` + +### Detailed Refresh Diagnostics + +The server may return a generic success outcome rather than exposing the internal refresh action. + +Where supported, detailed refresh outcomes can be enabled through server diagnostics: + +```csharp +builder.Services.AddUltimateAuthServer(options => +{ + options.Diagnostics.EnableRefreshDetails = true; +}); +``` + +This setting is intended for diagnostics and observability. + +Applications should not require a specific successful outcome such as `Rotated` or `Touched` to consider refresh successful. + +Use `IsSuccess` for the primary success decision. + +### Manual and Automatic Refresh + +`RefreshAsync` has an optional `isAuto` argument. + +```csharp +// Manual refresh +await UAuthClient.Flows.RefreshAsync(); + +// Refresh initiated by automatic coordination +await UAuthClient.Flows.RefreshAsync(isAuto: true); +``` + +The parameter distinguishes automatic refresh activity for client diagnostics. + +It does not select a different authentication mode or override the server's refresh policy. + +## 10. Automatic Refresh Configuration + +UltimateAuth exposes client-side automatic refresh options through `AutoRefresh`. + +```csharp +options.AutoRefresh.Enabled = true; +options.AutoRefresh.Interval = TimeSpan.FromMinutes(5); +``` + +`Enabled` controls background refresh coordination. + +`Interval` specifies the interval used for background refresh attempts. + +The effective defaults and registration behavior depend on the client integration. The options documentation identifies Blazor Server as the environment where background refresh is enabled by default. + +### Automatic Refresh Is Not a Security Guarantee + +Automatic refresh is a client-side convenience mechanism. + +It does not guarantee that a session will remain valid indefinitely. + +Authentication may still end because of: + +- Expiration. +- Revocation. +- Account security changes. +- Invalid refresh credentials. +- Server-side continuation restrictions. + +The client must remain prepared to handle `ReauthRequired`. + +## 11. Validate Current Authentication + +Use `ValidateAsync` to ask the server to validate the current authentication state. + +```csharp +var result = await UAuthClient.Flows.ValidateAsync(); + +if (result.IsValid) +{ + // Current authentication is valid. +} +else +{ + // Authentication is not currently valid. +} +``` + +The method returns `AuthValidationResult`. + +Its properties include: + +| Property | Purpose | +|---|---| +| `State` | Current session state | +| `IsValid` | Whether the state is `Active` | +| `Snapshot` | Optional authentication state snapshot | +| `ChainId` | Optional associated session chain | +| `RootId` | Optional session root | +| `BoundDeviceId` | Optional bound device identifier | + +### Inspect Session Context + +```csharp +var result = await UAuthClient.Flows.ValidateAsync(); + +if (result.IsValid && result.ChainId is { } chainId) +{ + // Use chainId for supported session-management operations. +} +``` + +Not every authentication mode necessarily provides all session hierarchy fields. + +Treat optional values as optional. + +### ValidateAsync Versus UI Authentication State + +`ValidateAsync` performs an explicit server validation operation. + +It should not be called on every UI render merely to determine whether a component should display authenticated content. + +For UI authentication state, use the supported UltimateAuth state management integration, including `IUAuthStateManager` where appropriate. + +Explicit validation is useful when an application needs to check current server-recognized authentication state. + +## 12. Logout + +`LogoutAsync` performs the current logout flow. + +```csharp +await UAuthClient.Flows.LogoutAsync(); +``` + +The method uses the configured logout endpoint and navigation transport. + +Depending on the client profile and configuration, logout may involve navigation and authentication state changes. + +### Logout Button + +A simple Blazor example: + +```razor +@using CodeBeam.UltimateAuth.Client +@inject IUAuthClient UAuthClient + + + +@code { + private async Task LogoutAsync() + { + await UAuthClient.Flows.LogoutAsync(); + } +} +``` + +Logout is distinct from clearing local UI state. + +Removing a local authentication indicator does not necessarily revoke authoritative server-managed authentication state. + +Use the supported logout flow to terminate authentication. + +## 13. Logout a Specific Device + +UltimateAuth supports revoking the session chain associated with a particular device context. + +The relevant method is: + +```csharp +LogoutMyDeviceAsync(LogoutDeviceRequest request) +``` + +`LogoutDeviceRequest` requires a `SessionChainId`. + +```csharp +var request = new LogoutDeviceRequest +{ + ChainId = chainId +}; + +var result = await UAuthClient.Flows.LogoutMyDeviceAsync(request); + +if (result.IsSuccess) +{ + // The requested device logout operation succeeded. +} +``` + +This operation targets a session chain, not an arbitrary individual session ID. + +### Current Device Behavior + +The returned `RevokeResult` can indicate whether the affected chain is the current chain. + +When the operation succeeds and `CurrentChain` is true, the client publishes a logout-related state event. + +This allows the application to react when the currently authenticated device context has been revoked. + +## 14. Logout Other Devices + +Use `LogoutMyOtherDevicesAsync` to revoke other device-associated sessions while retaining the current one. + +```csharp +var result = await UAuthClient.Flows.LogoutMyOtherDevicesAsync(); + +if (result.IsSuccess) +{ + // Other devices were logged out. +} +``` + +This is useful for account security pages that provide a **Log out other devices** action. + +The current authentication context is intended to remain active. + +The server remains responsible for identifying the current session or chain and enforcing the appropriate revocation scope. + +## 15. Logout All Devices + +Use `LogoutAllMyDevicesAsync` to revoke all applicable sessions for the current user. + +```csharp +var result = await UAuthClient.Flows.LogoutAllMyDevicesAsync(); + +if (result.IsSuccess) +{ + // All applicable device sessions were revoked. +} +``` + +On success, the client publishes a logout-related state event. + +This operation is useful when a user suspects account compromise or wants to terminate existing authentication sessions across devices. + +In JWT-based modes, previously issued access tokens may remain cryptographically valid until expiration unless additional revocation enforcement is configured. + +## 16. Administrative Device Logout + +UltimateAuth also exposes administrative session revocation operations. + +These methods act on a specified user and require appropriate server-side authorization. + +### Logout One User Device + +```csharp +var result = await UAuthClient.Flows.LogoutUserDeviceAsync( + userKey, + new LogoutDeviceRequest + { + ChainId = chainId + }); + +if (!result.IsSuccess) +{ + // Handle the failure. +} +``` + +### Logout Other Devices for a User + +```csharp +var result = await UAuthClient.Flows.LogoutUserOtherDevicesAsync(userKey, request); +``` + +Here, `request` is a `LogoutOtherDevicesRequest` identifying the chain to retain. + +### Logout All Devices for a User + +```csharp +var result = await UAuthClient.Flows.LogoutAllUserDevicesAsync(userKey); +``` + +Administrative methods do not grant administrative privileges by themselves. + +The server must authorize the actor and validate the target user and requested revocation scope. + +These operations are particularly relevant to administration panels and account security workflows. + +## 17. Building Login Forms with UAuthLoginForm + +UltimateAuth provides `UAuthLoginForm`, a reusable Blazor component that handles authentication form submission without requiring developers to implement the underlying flow orchestration themselves. + +The component supports password-based login, PKCE completion through UAuthHub, configurable submission modes, structured validation feedback, and customizable UI content. + +**You design the login interface. UltimateAuth handles the authentication submission workflow.** + +### Basic Usage + +A login form can be implemented with the following component: + +```razor +@using CodeBeam.UltimateAuth.Client.Blazor + + + + + + + +@code { + private string email = ""; + private string password = ""; +} +``` + +The component automatically uses `TryAndCommit` as its default submission mode. + +When submitted, it: + +1. Validates that the identifier and secret are present. +2. Creates the appropriate authentication request. +3. Calls the UltimateAuth client flow. +4. Performs the configured try/commit behavior. +5. Exposes structured results through `OnTryResult`. + +The example assumes that UltimateAuth's Blazor services have already been registered and that the hosting environment permits the selected login flow. + +### Customizing the Login UI + +`UAuthLoginForm` uses `ChildContent`, allowing developers to use standard HTML elements or their preferred Blazor component library. + +For example, with MudBlazor: + +```razor +@using CodeBeam.UltimateAuth.Client.Blazor +@using CodeBeam.UltimateAuth.Client + + + + Sign in + + + + Sign in + + @if (!string.IsNullOrWhiteSpace(error)) + { + @error + } + + + +@code { + private string email = ""; + private string password = ""; + private string? error; + + private Task HandleLoginResult(IUAuthTryResult result) + { + error = result.IsSuccess ? null : "Unable to sign in."; + return Task.CompletedTask; + } +} +``` + +The form controls remain under your control. + +UltimateAuth does not require a particular visual design, CSS framework, or component library. + +Because the component renders an HTML form internally, place the submit button inside its child content and avoid nesting another HTML form within it. + +### Submission Modes + +Use `SubmitMode` to control how the authentication attempt is executed. + +```razor + + ... + +``` + +| Mode | Behavior | +|---|---| +| `TryOnly` | Validates the authentication attempt without committing login | +| `TryAndCommit` | Attempts authentication and commits on success | +| `DirectCommit` | Submits directly to the commit flow | + +`TryAndCommit` is the default. + +For interactive login forms that need validation feedback, it is generally the recommended choice. + +`DirectCommit` does not invoke `OnTryResult`, because it does not perform the structured try operation. + +### Handling Authentication Results + +The component exposes an `OnTryResult` callback: + +```razor + + ... + +``` + +The callback receives `IUAuthTryResult`, allowing the same component to report password login and PKCE completion results. + +```csharp +private Task HandleResult(IUAuthTryResult result) +{ + if (!result.IsSuccess) + { + errorMessage = "Authentication failed."; + } + + return Task.CompletedTask; +} +``` + +For flow-specific details, inspect the concrete result type: + +```csharp +private Task HandleResult(IUAuthTryResult result) +{ + if (result is TryLoginResult loginResult) + { + if (loginResult.LockoutUntilUtc is not null) + { + errorMessage = "Your account is temporarily locked."; + } + else if (loginResult.RequiresMfa) + { + // Continue the supported additional authentication workflow. + } + else if (!loginResult.IsSuccess) + { + errorMessage = "Unable to sign in."; + } + } + else if (result is TryPkceLoginResult pkceResult) + { + if (pkceResult.RetryWithNewPkce) + { + errorMessage = "Please restart the authentication flow."; + } + else if (!pkceResult.IsSuccess) + { + errorMessage = "Unable to complete authentication."; + } + } + + return Task.CompletedTask; +} +``` + +The callback reports the structured try result. It does not replace authentication state management or server-side validation. + +### Using UAuthLoginForm with PKCE + +The same component also supports PKCE login completion when hosted within a supported UAuthHub environment. + +Set `LoginType` to `Pkce`: + +```razor + + + + + +``` + +In PKCE mode, the component: + +1. Resolves the relevant UAuthHub session context. +2. Retrieves the authorization code and code verifier through the configured Hub services. +3. Builds a `PkceCompleteRequest`. +4. Invokes the corresponding PKCE completion flow. +5. Reports the structured result when a try-enabled submission mode is used. + +The application does not need to manually construct the PKCE completion request for this form. + +**Important:** PKCE mode requires a supported server-side UAuthHub environment. `UAuthLoginForm` checks the available Hub capabilities and throws when PKCE login is requested in an unsupported client profile. + +A Blazor WebAssembly application should initiate PKCE using `BeginPkceAsync`; the Hub-hosted login form handles the completion stage. + +### Return URL Handling + +You can provide an explicit return URL: + +```razor + + ... + +``` + +The component resolves the effective return URL using the following precedence: + +1. Explicit `ReturnUrl` parameter. +2. UAuthHub flow return URL when using PKCE. +3. Current navigation URL. + +This allows a shared login component to participate in different authentication journeys without hardcoding navigation logic into the form. + +### Custom Login Endpoints + +For advanced integrations, the `Endpoint` parameter allows the form's HTML action endpoint to be overridden: + +```razor + + ... + +``` + +The endpoint is resolved relative to the configured UltimateAuth endpoint base path and multi-tenant settings. + +Note that `Endpoint` affects the form's HTML action. Try-based submissions invoke the configured `UAuthClient.Flows` endpoints, so changing `Endpoint` alone does not redirect those SDK requests to a custom endpoint. + +### Submit the Form Programmatically + +`UAuthLoginForm` exposes `SubmitAsync`, allowing submission through a component reference. + +```razor + + + + + + + +@code { + private UAuthLoginForm? loginForm; + + private string email = ""; + private string password = ""; + + private async Task SubmitLoginAsync() + { + if (loginForm is not null) + { + await loginForm.SubmitAsync(); + } + } +} +``` + +`SubmitAsync` must be called after the component has rendered. + +This is useful when submission is initiated from a custom button, dialog, or application-specific interaction. + +### Controlling Enter-Key Submission + +By default, the component supports Enter-key submission. + +You can disable its built-in hidden submit button: + +```razor + + ... + +``` + +This option controls the component's built-in Enter-key submit mechanism. Other submit buttons inside the form may still trigger normal browser form submission behavior. + +### Reloading PKCE Context + +The component exposes `ReloadAsync` to reload its Hub credentials and flow state. + +```razor + + ... + + +@code { + private UAuthLoginForm? loginForm; + + private async Task ReloadLoginContextAsync() + { + if (loginForm is not null) + { + await loginForm.ReloadAsync(); + } + } +} +``` + +This is an advanced capability intended for scenarios where the associated Hub state changes and the component must refresh its internal context. + +It does not initiate a new PKCE authorization flow by itself. + +### Component Parameters + +| Parameter | Type | Default | Description | +|---|---|---|---| +| `Identifier` | `string?` | `null` | Login identifier | +| `Secret` | `string?` | `null` | Authentication secret | +| `LoginType` | `UAuthLoginType` | `Password` | Password or PKCE login | +| `SubmitMode` | `UAuthSubmitMode` | `TryAndCommit` | Submission strategy | +| `ReturnUrl` | `string?` | `null` | Redirect destination | +| `Endpoint` | `string?` | `null` | Custom HTML form endpoint | +| `HubSessionId` | `HubSessionId?` | `null` | Explicit Hub session context | +| `AllowEnterKeyToSubmit` | `bool` | `true` | Built-in Enter-key submit support | +| `OnTryResult` | `EventCallback` | β€” | Structured try result callback | +| `ChildContent` | `RenderFragment?` | β€” | Custom form content | + +### When to Use UAuthLoginForm + +Use `UAuthLoginForm` when building a Blazor login interface that should integrate directly with UltimateAuth's supported authentication flows. + +It is particularly useful when: + +- You want to use your own component library without implementing the authentication submission logic. +- You need structured login validation feedback. +- You want to switch between try-only and commit behavior. +- You are building a UAuthHub login page that completes PKCE. +- You want to share login UI across supported Blazor hosting environments. + +For application-specific workflows that cannot be expressed through the component, use `UAuthClient.Flows` directly. + +**The component handles the login workflow, while your application retains control over presentation, user experience, and surrounding application logic.** + +## 18. Common Integration Mistakes + +### Calling Direct Login from a Public Client + +**Problem:** `LoginAsync` throws because direct credential posting is disabled. + +**Solution:** Use `BeginPkceAsync` and the supported PKCE completion flow. Do not enable `AllowCredentialPost` to bypass the public-client restriction. + +### Treating TryOnly as Authentication + +**Problem:** The application receives `IsSuccess = true` from `TryOnly` and assumes the user is logged in. + +**Solution:** A successful try result does not commit authentication. Use `TryAndCommit` or the appropriate commit flow. + +### Assuming DirectCommit Skips Server Validation + +**Problem:** DirectCommit is interpreted as a faster but less secure authentication mode. + +**Solution:** DirectCommit skips the separate client try stage. The server still validates the actual authentication request. + +### Assuming Every Refresh Rotates Tokens + +**Problem:** The application requires `RefreshOutcome.Rotated` for refresh success. + +**Solution:** Check `RefreshResult.IsSuccess`. Rotation, touch, and no-op behavior depend on the authentication mode and refresh strategy. + +### Using a Session ID for Device Logout + +**Problem:** A session identifier is passed to a device logout operation. + +**Solution:** `LogoutMyDeviceAsync` requires `LogoutDeviceRequest` with a `SessionChainId`. + +### Assuming Logout Immediately Invalidates All JWTs + +**Problem:** An application expects every issued JWT access token to become unusable immediately after server-side logout. + +**Solution:** Understand the enforcement behavior of SemiHybrid and PureJwt. Local JWT validation may continue accepting previously issued tokens until expiration. + +### Using ValidateAsync on Every Render + +**Problem:** Components repeatedly make validation requests just to display authenticated UI. + +**Solution:** Use the appropriate authentication state integration for UI rendering, and reserve explicit validation for operations that require it. + +## Next Steps + +- [Session Management](/docs/client/session-management) β€” Inspect and manage session and device information. +- [Token Security](/docs/security/token-security) β€” Understand token validity, refresh behavior, and revocation. +- [Refresh Rotation & Replay Protection](/docs/security/refresh-rotation) β€” Explore refresh concurrency and reuse security. +- [Authentication Modes](/docs/fundamentals/auth-modes) β€” Compare authentication behavior across modes. +- [Device Management](/docs/sessions-devices/device-management) β€” Understand device-associated session chains. \ No newline at end of file diff --git a/docs/content/client-sdk/authorization.md b/docs/content/client-sdk/authorization.md new file mode 100644 index 00000000..58844d42 --- /dev/null +++ b/docs/content/client-sdk/authorization.md @@ -0,0 +1,1361 @@ +--- +title: Authorization +order: 7 +group: client +--- + +# Authorization + +UltimateAuth provides a unified client API for managing roles, assigning permissions, controlling user-role relationships, and requesting authorization decisions from the server. + +Through `UAuthClient.Authorization`, applications can build administrative authorization interfaces and check whether the current user is allowed to perform a particular action. + +Authorization is enforced on the server. Client-side permission checks are useful for improving the user experience, but they do not replace authorization at the endpoint or application-service level. + +This guide covers the current `IAuthorizationClient` API, practical role management workflows, permission patterns, and secure client integration. + +## 1. Understanding Authorization + +UltimateAuth authorization combines several concepts: + +| Concept | Purpose | +|---|---| +| Roles | Named collections of permissions | +| Permissions | Fine-grained capabilities represented by normalized values | +| User-role assignments | Associations between users and roles | +| Policies | Server-side rules that participate in access decisions | +| Authorization context | Information about the actor, resource, action, and current security state | + +A typical authorization relationship looks like this: + +```text +User + | + +-- Manager + | | + | +-- products.read + | +-- products.create + | +-- products.update + | + +-- Auditor + | + +-- reports.read +``` + +A user can have multiple roles, and each role can contain multiple permissions. + +However, **having a permission does not automatically guarantee access**. The server's authorization pipeline may apply additional requirements, such as resource ownership, session validity, tenant isolation, or application policies. + +For the internal decision model, see [Authorization Pipeline Internals](/docs/security/authorization-pipeline). + +## 2. Accessing the Authorization Client + +Inject `IUAuthClient` into a Blazor component: + +```razor +@using CodeBeam.UltimateAuth.Client +@inject IUAuthClient UAuthClient +``` + +Authorization operations are available through: + +```csharp +UAuthClient.Authorization +``` + +This property exposes `IAuthorizationClient`. + +The interface provides three main groups of operations: + +- **Authorization checks:** Evaluate whether the current user may perform an action. +- **Role management:** Create, query, rename, configure, and delete roles. +- **User-role management:** Retrieve, assign, and remove user roles. + +The SDK sends requests to the corresponding UltimateAuth endpoints and returns structured `UAuthResult` values. + +## 3. Understanding Permissions + +A permission is represented by the `Permission` value type. + +```csharp +var permission = Permission.From("products.create"); +``` + +The current implementation normalizes permission values by trimming surrounding whitespace and converting the value to lowercase. + +For example: + +```csharp +var permission = Permission.From(" PRODUCTS.CREATE "); + +Console.WriteLine(permission.Value); +// products.create +``` + +The constructor rejects null, empty, and whitespace-only values. + +### Permission Patterns + +UltimateAuth recognizes three important forms: + +| Pattern | Example | Meaning | +|---|---|---| +| Exact | `products.create` | A specific permission | +| Prefix wildcard | `products.*` | Permissions within the corresponding prefix | +| Global wildcard | `*` | Unrestricted permission matching within the applicable authorization rules | + +Create them using: + +```csharp +var exact = Permission.From("products.create"); + +var prefix = Permission.From("products.*"); + +var global = Permission.Wildcard; +``` + +The type also exposes: + +```csharp +permission.IsWildcard; +permission.IsPrefix; +permission.IsValid; +``` + +### Permission Normalization + +The following values normalize to the same representation: + +```csharp +Permission.From("Products.Create"); +Permission.From("products.create"); +Permission.From(" PRODUCTS.CREATE "); +``` + +All produce: + +```text +products.create +``` + +Use consistent permission names across application services, policies, and role configuration. + +### Wildcards Are Powerful + +For example: + +```csharp +Permission.From("users.*") +``` + +may grant broad access to operations within the `users` permission prefix. + +The global wildcard: + +```csharp +Permission.Wildcard +``` + +is especially sensitive. + +Avoid assigning broad wildcard permissions merely to simplify development. Use least-privilege permissions for ordinary application roles. + +A wildcard is not a substitute for satisfying independent security policies, authentication requirements, or tenant restrictions. + +## 4. Check Authorization + +Use `CheckAsync` to ask the server whether the current user is authorized to perform an action. + +```csharp +var result = await UAuthClient.Authorization.CheckAsync( + new AuthorizationCheckRequest + { + Action = "read", + Resource = "products" + }); + +if (result.IsSuccess && result.Value is { IsAllowed: true }) +{ + // The server authorized this action. +} +``` + +The method returns: + +```csharp +UAuthResult +``` + +### AuthorizationCheckRequest + +The request contains: + +| Property | Type | Description | +|---|---|---| +| `Action` | `string` | Requested operation | +| `Resource` | `string` | Resource being accessed | +| `ResourceId` | `string?` | Optional identifier for a specific resource instance | + +Both `Action` and `Resource` are required. + +The action and resource values must follow the conventions understood by your server-side authorization configuration. + +### Check a Specific Resource + +For example, check access to a particular product: + +```csharp +var result = await UAuthClient.Authorization.CheckAsync( + new AuthorizationCheckRequest + { + Action = "update", + Resource = "products", + ResourceId = productId.ToString() + }); +``` + +Providing `ResourceId` allows the server to evaluate resource-specific rules where supported by the configured authorization pipeline. + +For example, the application may distinguish between updating any product and updating a product owned by the current user. + +Do not assume that sending `ResourceId` automatically enables ownership checking. The relevant server-side policy must implement the required rule. + +## 5. Understanding Authorization Results + +`CheckAsync` returns an `AuthorizationResult` with the following properties: + +| Property | Description | +|---|---| +| `IsAllowed` | Whether the request was authorized | +| `RequiresReauthentication` | Whether the decision indicates reauthentication is required | +| `DenyReason` | Optional denial reason code | + +### Handle All Decision Outcomes + +```csharp +var result = await UAuthClient.Authorization.CheckAsync( + new AuthorizationCheckRequest + { + Action = "delete", + Resource = "products", + ResourceId = productId.ToString() + }); + +if (!result.IsSuccess) +{ + // The authorization request itself failed. +} +else if (result.Value is { RequiresReauthentication: true }) +{ + // Ask the user to authenticate again. +} +else if (result.Value is { IsAllowed: true }) +{ + // The operation is allowed. +} +else +{ + // The operation was denied. + var reason = result.Value?.DenyReason; +} +``` + +An unsuccessful API request and an explicit authorization denial are different outcomes. + +For example: + +- `result.IsSuccess == false` means the authorization request did not complete successfully at the API level. +- `result.Value.IsAllowed == false` means the server returned a denial decision. +- `result.Value.RequiresReauthentication == true` indicates that the caller needs to reestablish authentication. + +Treat failed or unavailable authorization checks as **not authorized** in the UI. + +Never grant access because an authorization request failed. + +## 6. Use Authorization Checks in Blazor + +A common use case is controlling whether an action button should be displayed. + +```razor +@using CodeBeam.UltimateAuth.Client +@using CodeBeam.UltimateAuth.Authorization.Contracts +@inject IUAuthClient UAuthClient + +@if (_canCreate) +{ + +} + +@code { + private bool _canCreate; + + protected override async Task OnInitializedAsync() + { + var result = await UAuthClient.Authorization.CheckAsync( + new AuthorizationCheckRequest + { + Action = "create", + Resource = "products" + }); + + _canCreate = result.IsSuccess && result.Value is { IsAllowed: true }; + } + + private Task CreateProductAsync() + { + // Invoke the protected application operation. + return Task.CompletedTask; + } +} +``` + +This improves the interface by hiding unavailable actions. + +However, the endpoint handling product creation must still enforce authorization independently. + +### Why Client Checks Are Not Security Boundaries + +A user can bypass a hidden button and send a request directly to an endpoint. + +Therefore: + +```text +Client authorization check + | + v +Improve the user experience + | + v +User submits an operation + | + v +Server independently authorizes the operation + | + v +Execute or deny +``` + +Client authorization checks should never be the only protection for sensitive operations. + +## 7. Get My Roles + +Use `GetMyRolesAsync` to retrieve roles assigned to the current authenticated user. + +```csharp +var result = await UAuthClient.Authorization.GetMyRolesAsync(); + +if (result.IsSuccess && result.Value is { } data) +{ + foreach (var role in data.Roles.Items) + { + Console.WriteLine(role.Name); + } +} +``` + +The method returns: + +```csharp +UAuthResult +``` + +### UserRolesGetResult + +The response contains: + +| Property | Description | +|---|---| +| `UserKey` | User whose roles were requested | +| `Roles` | Paginated collection of role assignments | + +Each entry is a `UserRoleInfo`. + +### UserRoleInfo + +| Property | Description | +|---|---| +| `Tenant` | Tenant associated with the assignment | +| `UserKey` | Assigned user | +| `RoleId` | Assigned role | +| `Name` | Role name | +| `AssignedAt` | Assignment timestamp | + +### Pagination + +```csharp +var result = await UAuthClient.Authorization.GetMyRolesAsync( + new PageRequest + { + PageNumber = 1, + PageSize = 20 + }); +``` + +The returned role collection is paginated. + +Do not assume that the first page contains all assigned roles. + +## 8. Get Another User's Roles + +Administrative interfaces can retrieve the roles assigned to a specific user. + +```csharp +var result = await UAuthClient.Authorization.GetUserRolesAsync(userKey, + new PageRequest + { + PageNumber = 1, + PageSize = 20 + }); + +if (result.IsSuccess && result.Value is { } data) +{ + foreach (var role in data.Roles.Items) + { + Console.WriteLine($"{role.Name} β€” {role.AssignedAt}"); + } +} +``` + +This operation requires appropriate administrative authorization. + +The `UserKey` identifies the target user, not the current actor. + +The server determines whether the current actor may inspect that user's role assignments. + +## 9. Query Roles + +Use `QueryRolesAsync` to retrieve roles with pagination and optional filtering. + +```csharp +var result = await UAuthClient.Authorization.QueryRolesAsync( + new RoleQuery + { + PageNumber = 1, + PageSize = 20 + }); + +if (result.IsSuccess && result.Value is { } page) +{ + foreach (var role in page.Items) + { + Console.WriteLine(role.Name); + } +} +``` + +The method returns: + +```csharp +UAuthResult> +``` + +### RoleQuery + +`RoleQuery` inherits from `PageRequest` and adds: + +| Property | Description | +|---|---| +| `Search` | Optional role-name search | +| `IncludeDeleted` | Include deleted roles when supported and authorized | +| `PageNumber` | Requested page | +| `PageSize` | Requested page size | + +### Search Roles + +```csharp +var result = await UAuthClient.Authorization.QueryRolesAsync( + new RoleQuery + { + Search = "manager", + PageNumber = 1, + PageSize = 10 + }); +``` + +### Include Deleted Roles + +```csharp +var result = await UAuthClient.Authorization.QueryRolesAsync( + new RoleQuery + { + IncludeDeleted = true, + PageNumber = 1, + PageSize = 20 + }); +``` + +Use `IncludeDeleted` only where the application needs administrative access to deleted role records. + +The server remains responsible for enforcing the query's permissions and lifecycle rules. + +## 10. Understanding RoleInfo + +Each role returned by the SDK is represented by `RoleInfo`. + +| Property | Type | Description | +|---|---|---| +| `Id` | `RoleId` | Role identifier | +| `Name` | `string` | Role name | +| `Permissions` | `IReadOnlyCollection` | Assigned permissions | +| `CreatedAt` | `DateTimeOffset` | Creation timestamp | +| `UpdatedAt` | `DateTimeOffset?` | Last update timestamp | + +For example: + +```csharp +var result = await UAuthClient.Authorization.QueryRolesAsync( + new RoleQuery + { + PageNumber = 1, + PageSize = 20 + }); + +if (result.IsSuccess && result.Value is { } page) +{ + foreach (var role in page.Items) + { + Console.WriteLine($"Role: {role.Name}"); + + foreach (var permission in role.Permissions) + { + Console.WriteLine($" {permission.Value}"); + } + } +} +``` + +`RoleId` is a strongly typed identifier rather than a plain `Guid`. + +For example: + +```csharp +RoleId roleId = RoleId.From(existingGuid); +``` + +Use `RoleId` for role management requests. + +Do not confuse it with `UserKey` or other identifier types. + +## 11. Create a Role + +Use `CreateRoleAsync` to create a role. + +```csharp +var result = await UAuthClient.Authorization.CreateRoleAsync( + new CreateRoleRequest + { + Name = "Manager" + }); + +if (result.IsSuccess && result.Value is { } role) +{ + Console.WriteLine($"Created: {role.Name}"); + Console.WriteLine($"Role ID: {role.Id}"); +} +``` + +The method returns: + +```csharp +UAuthResult +``` + +Role names must be unique within the relevant tenant according to the server's role-name rules. + +### Create a Role with Permissions + +`CreateRoleRequest` supports an optional initial permission collection. + +```csharp +var result = await UAuthClient.Authorization.CreateRoleAsync( + new CreateRoleRequest + { + Name = "ProductManager", + Permissions = + [ + Permission.From("products.read"), + Permission.From("products.create"), + Permission.From("products.update") + ] + }); +``` + +This allows applications to create and configure a role in one operation. + +The server validates the requested permissions and whether the caller is authorized to create the role. + +### Create a Read-Only Role + +```csharp +var result = await UAuthClient.Authorization.CreateRoleAsync( + new CreateRoleRequest + { + Name = "Auditor", + Permissions = + [ + Permission.From("reports.read"), + Permission.From("products.read") + ] + }); +``` + +Use role names that describe the responsibility or capability represented by the role. + +## 12. Rename a Role + +Use `RenameRoleAsync` to change a role's name. + +```csharp +var result = await UAuthClient.Authorization.RenameRoleAsync( + new RenameRoleRequest + { + Id = roleId, + Name = "SeniorManager" + }); + +if (result.IsSuccess) +{ + // Role renamed. +} +``` + +`RenameRoleRequest` requires: + +- `Id`: The role's `RoleId`. +- `Name`: The new role name. + +The operation may fail if another role already uses the requested name. + +Role renaming does not mean that permissions are automatically changed. + +After a successful rename, the SDK publishes an `AuthorizationChanged` state event. + +## 13. Set Role Permissions + +Use `SetRolePermissionsAsync` to configure a role's permissions. + +```csharp +var result = await UAuthClient.Authorization.SetRolePermissionsAsync( + new SetRolePermissionsRequest + { + RoleId = roleId, + Permissions = + [ + Permission.From("products.read"), + Permission.From("products.create"), + Permission.From("products.update") + ] + }); +``` + +The request contains: + +| Property | Description | +|---|---| +| `RoleId` | Role to modify | +| `Permissions` | New permission collection | + +### Important: This Is a Replacement Operation + +`SetRolePermissionsAsync` replaces the role's existing permission collection. + +It does not append permissions to the current collection. + +For example, suppose a role currently contains: + +```text +products.read +products.create +products.update +``` + +Submitting: + +```csharp +Permissions = +[ + Permission.From("products.read") +] +``` + +means the intended new permission collection contains only: + +```text +products.read +``` + +The other permissions are removed from the role. + +### Safely Add a Permission to an Existing Role + +To preserve existing permissions, first retrieve the role and construct the complete desired collection. + +```csharp +var query = await UAuthClient.Authorization.QueryRolesAsync( + new RoleQuery + { + Search = "ProductManager", + PageNumber = 1, + PageSize = 50 + }); + +if (query.IsSuccess && query.Value is { } page) +{ + var role = page.Items.FirstOrDefault(x => x.Name == "ProductManager"); + + if (role is not null) + { + var permissions = role.Permissions.Append(Permission.From("products.delete")).Distinct().ToArray(); + + var result = + await UAuthClient.Authorization.SetRolePermissionsAsync( + new SetRolePermissionsRequest + { + RoleId = role.Id, + Permissions = permissions + }); + } +} +``` + +This is a read-modify-write pattern, not an atomic append operation. + +If multiple administrators edit the same role concurrently, the last successful replacement may overwrite another administrator's changes. + +Applications requiring concurrency-safe editing should implement suitable coordination or version checking at the application level. + +### Remove a Permission + +```csharp +var updatedPermissions = role.Permissions.Where(x => x.Value != "products.delete").ToArray(); + +var result = await UAuthClient.Authorization.SetRolePermissionsAsync( + new SetRolePermissionsRequest + { + RoleId = role.Id, + Permissions = updatedPermissions + }); +``` + +Again, submit the complete desired permission set. + +### Clear All Permissions + +```csharp +var result = await UAuthClient.Authorization.SetRolePermissionsAsync( + new SetRolePermissionsRequest + { + RoleId = roleId, + Permissions = [] + }); +``` + +An empty collection requests that the role have no permissions. + +The server determines whether the operation is permitted. + +## 14. Assign a Role to a User + +Use `AssignRoleToUserAsync` to assign an existing role to a user. + +```csharp +var result = await UAuthClient.Authorization.AssignRoleToUserAsync( + new AssignRoleRequest + { + UserKey = userKey, + RoleName = "Manager" + }); + +if (result.IsSuccess) +{ + // Role assigned. +} +``` + +The request contains: + +| Property | Description | +|---|---| +| `UserKey` | Target user | +| `RoleName` | Existing role name | + +The role must exist, and the current actor must be authorized to assign it. + +### Assign Multiple Roles + +```csharp +foreach (var roleName in new[] { "Manager", "Auditor" }) +{ + var result = await UAuthClient.Authorization.AssignRoleToUserAsync( + new AssignRoleRequest + { + UserKey = userKey, + RoleName = roleName + }); + + if (!result.IsSuccess) + { + // Handle the failed assignment. + break; + } +} +``` + +Each call is a separate operation. + +This example does not provide transactional, all-or-nothing behavior across the entire collection. + +For complex provisioning workflows, consider whether the application needs a server-side orchestration operation. + +## 15. Remove a Role from a User + +Use `RemoveRoleFromUserAsync`: + +```csharp +var result = await UAuthClient.Authorization.RemoveRoleFromUserAsync( + new RemoveRoleRequest + { + UserKey = userKey, + RoleName = "Manager" + }); + +if (result.IsSuccess) +{ + // Role removed. +} +``` + +Removing a role changes the user's role assignments. + +It does not delete the role itself. + +Other users assigned to that role are unaffected by this particular operation. + +### Important: Effective Permissions May Remain + +A user may receive the same permission through multiple roles. + +For example: + +```text +User + | + +-- Manager + | +-- products.read + | + +-- Auditor + +-- products.read +``` + +Removing `Manager` does not necessarily remove the user's ability to read products because `Auditor` still provides `products.read`. + +To determine whether the user may perform an action, rely on the server's authorization decision rather than inspecting one role assignment in isolation. + +## 16. Delete a Role + +Use `DeleteRoleAsync` to delete a role. + +```csharp +var result = await UAuthClient.Authorization.DeleteRoleAsync( + new DeleteRoleRequest + { + Id = roleId, + Mode = DeleteMode.Soft + }); +``` + +The request supports a deletion mode. + +### Hard Delete + +```csharp +var result = await UAuthClient.Authorization.DeleteRoleAsync( + new DeleteRoleRequest + { + Id = roleId, + Mode = DeleteMode.Hard + }); +``` + +The availability and effect of each deletion mode are determined by server-side lifecycle rules. + +### RoleDeleteResult + +The method returns: + +```csharp +UAuthResult +``` + +The response contains: + +| Property | Description | +|---|---| +| `RoleId` | Deleted role | +| `RemovedAssignments` | Number of user-role assignments removed | +| `Mode` | Applied deletion mode | +| `DeletedAt` | Deletion timestamp | + +### Inspect the Result + +```csharp +var result = await UAuthClient.Authorization.DeleteRoleAsync( + new DeleteRoleRequest + { + Id = roleId, + Mode = DeleteMode.Soft + }); + +if (result.IsSuccess && result.Value is { } deleted) +{ + Console.WriteLine($"Removed assignments: {deleted.RemovedAssignments}"); +} +``` + +Deleting a role removes its assignments from users. + +This can affect multiple users at once. + +For that reason, role deletion should be presented as a sensitive administrative operation with appropriate confirmation. + +## 17. Build a Role Management Page + +The following Blazor example demonstrates querying roles, creating a role, and deleting a selected role. + +```razor +@using CodeBeam.UltimateAuth.Client +@using CodeBeam.UltimateAuth.Authorization.Contracts +@using CodeBeam.UltimateAuth.Core.Contracts +@inject IUAuthClient UAuthClient + +

Role Management

+ + + + + +@if (_loading) +{ +

Loading roles...

+} +else +{ + @foreach (var role in _roles) + { +
+ @role.Name + @role.Permissions.Count permissions + +
+ } +} + +

Create Role

+ + + +@if (_message is not null) +{ +

@_message

+} + +@code { + private IReadOnlyList _roles = []; + + private string _search = ""; + private string _newRoleName = ""; + private string? _message; + + private bool _loading = true; + private bool _busy; + + protected override async Task OnInitializedAsync() + { + await LoadRolesAsync(); + } + + private async Task LoadRolesAsync() + { + _loading = true; + + try + { + var result = await UAuthClient.Authorization.QueryRolesAsync( + new RoleQuery + { + Search = _search, + PageNumber = 1, + PageSize = 50 + }); + + if (result.IsSuccess && result.Value is { } page) + { + _roles = page.Items; + } + else + { + _message = "Unable to load roles."; + } + } + catch (Exception) + { + _message = "The authorization service is unavailable."; + } + finally + { + _loading = false; + } + } + + private async Task CreateRoleAsync() + { + if (_busy || string.IsNullOrWhiteSpace(_newRoleName)) + return; + + _busy = true; + _message = null; + + try + { + var result = await UAuthClient.Authorization.CreateRoleAsync( + new CreateRoleRequest + { + Name = _newRoleName + }); + + if (result.IsSuccess) + { + _newRoleName = ""; + await LoadRolesAsync(); + } + else + { + _message = "Unable to create role."; + } + } + finally + { + _busy = false; + } + } + + private async Task DeleteRoleAsync(RoleId roleId) + { + // Add a confirmation dialog before deletion in production. + + var result = await UAuthClient.Authorization.DeleteRoleAsync( + new DeleteRoleRequest + { + Id = roleId, + Mode = DeleteMode.Soft + }); + + if (result.IsSuccess) + { + await LoadRolesAsync(); + } + else + { + _message = "Unable to delete role."; + } + } +} +``` + +This example demonstrates the SDK integration rather than a complete production administration interface. + +A production implementation should include pagination, role assignment controls, permission editing, deletion confirmation, and appropriate loading and error handling for every operation. + +## 18. Build a User Role Assignment Interface + +An administrative user details page can display assigned roles and allow administrators to add or remove them. + +### Load User Roles + +```csharp +var result = await UAuthClient.Authorization.GetUserRolesAsync(userKey, + new PageRequest + { + PageNumber = 1, + PageSize = 50 + }); + +if (result.IsSuccess && result.Value is { } data) +{ + var assignedRoles = data.Roles.Items.ToList(); +} +``` + +### Assign a Selected Role + +```csharp +var result = await UAuthClient.Authorization.AssignRoleToUserAsync( + new AssignRoleRequest + { + UserKey = userKey, + RoleName = selectedRoleName + }); + +if (result.IsSuccess) +{ + // Reload the user's assigned roles. +} +``` + +### Remove a Selected Role + +```csharp +var result = await UAuthClient.Authorization.RemoveRoleFromUserAsync( + new RemoveRoleRequest + { + UserKey = userKey, + RoleName = selectedRoleName + }); + +if (result.IsSuccess) +{ + // Reload the user's assigned roles. +} +``` + +This pattern works well alongside `UAuthClient.Users` for building a unified user administration interface. + +See [User Management](/docs/client/user-management). + +## 19. Authorization State Events + +The current `UAuthAuthorizationClient` publishes: + +```csharp +UAuthStateEvent.AuthorizationChanged +``` + +after successful authorization mutations. + +| Operation | Publishes `AuthorizationChanged` | +|---|---| +| `AssignRoleToUserAsync` | Yes | +| `RemoveRoleFromUserAsync` | Yes | +| `RenameRoleAsync` | Yes | +| `SetRolePermissionsAsync` | Yes | +| `DeleteRoleAsync` | Yes | +| `CreateRoleAsync` | No | +| `QueryRolesAsync` | No | +| `GetMyRolesAsync` | No | +| `GetUserRolesAsync` | No | +| `CheckAsync` | No | + +The event follows the configured client state event handling mode. + +It can be used to refresh navigation menus, role assignment views, or permission-sensitive UI. + +However, it is a **local client event**, not a guarantee that all active clients or distributed caches have immediately observed the change. + +For example, an administrator assigning a role to another user does not automatically mean that user's open browser receives the administrator's local event. + +Applications requiring cross-client updates should provide an appropriate synchronization mechanism. + +## 20. Authorization Changes and Active Sessions + +Role and permission changes may affect active users. + +However, the timing of those changes depends on how authorization data is resolved and cached by the server. + +For example: + +1. An administrator removes a role. +2. The server persists the new role assignment state. +3. A subsequent authorization evaluation uses the effective authorization state available to the server. + +If the application caches authorization information, changes may not become visible everywhere immediately. + +Do not assume that a client event invalidates every active session or refreshes every distributed authorization cache. + +For sensitive operations, authorization should be evaluated against sufficiently current server-side state. + +### Roles Are Not Sessions + +Removing a role and revoking a session are different operations. + +Removing a role changes authorization data. + +Revoking a session changes whether an authentication session remains valid. + +Applications may combine these operations when security policy requires immediate termination of access, but the concepts should remain separate. + +See [Session Management](/docs/client/session-management). + +## 21. Multi-Tenant Authorization + +UltimateAuth authorization operations are scoped to the current tenant context. + +Role names are unique within the relevant tenant, and role assignments belong to the applicable tenant. + +For example: + +```text +Tenant A + | + +-- Manager + | +-- products.read + | + +-- User A + +Tenant B + | + +-- Manager + | +-- reports.read + | + +-- User B +``` + +The same role name may represent different permission sets in different tenants. + +The client does not gain cross-tenant access merely by providing another user's `UserKey`. + +The server must enforce tenant isolation and administrative authorization. + +When implementing multi-tenant administration, do not assume role names or permission configurations are globally shared. + +## 22. Handling Authorization Errors + +Authorization methods return `UAuthResult` or `UAuthResult`. + +### Handle Role Creation Errors + +```csharp +var result = await UAuthClient.Authorization.CreateRoleAsync( + new CreateRoleRequest + { + Name = "Manager" + }); + +if (result.IsSuccess) +{ + // Role created. +} +else if (result.Http.IsConflict) +{ + // A conflicting role may already exist. +} +else if (result.Http.IsForbidden) +{ + // Caller lacks permission. +} +else if (result.Http.IsBadRequest) +{ + // Invalid request. +} +else +{ + // Handle another failure. +} +``` + +### Handle Authorization Check Failures + +```csharp +var result = await UAuthClient.Authorization.CheckAsync( + new AuthorizationCheckRequest + { + Action = "update", + Resource = "products" + }); + +var allowed = result.IsSuccess && result.Value is { IsAllowed: true }; +``` + +This fail-closed pattern is appropriate when determining whether the UI should expose a protected operation. + +Use the server's structured error information for diagnostics where appropriate. + +Do not expose sensitive policy internals unnecessarily. + +## 23. Security Considerations + +### Server Authorization Is Authoritative + +Client-side checks improve usability but cannot protect an endpoint by themselves. + +Every protected operation must enforce authorization on the server. + +### Follow Least Privilege + +Assign only the permissions a role actually needs. + +Avoid using `*` or broad prefix wildcards for ordinary users. + +### Protect Administrative Operations + +Role creation, permission replacement, role assignment, and role deletion are privileged operations. + +The server must authorize each mutation independently. + +### Treat Permission Replacement Carefully + +`SetRolePermissionsAsync` replaces the entire permission collection. + +Always inspect the existing role configuration before submitting changes. + +### Handle Reauthentication Requirements + +An authorization decision can indicate `RequiresReauthentication`. + +Applications should handle this state explicitly rather than treating it as an ordinary permission denial. + +### Respect Tenant Boundaries + +Roles and assignments must remain within their authorized tenant context. + +### Do Not Infer Effective Access from One Role + +A user can have multiple roles, overlapping permissions, and additional policy restrictions. + +Use the server's authorization decision when determining whether an action is permitted. + +### Account for Cached Authorization State + +Permission changes may not propagate instantly to every active client or cached authorization context. + +Design sensitive operations around server-side enforcement and appropriate cache invalidation. + +## 24. Putting Everything Together + +A typical administrative authorization workflow may look like this: + +```text +Create role + | + v +Configure permissions + | + v +Assign role to user + | + v +User requests protected action + | + v +Server evaluates authorization + | + +-- Allow --> Execute operation + | + +-- Deny --> Reject operation + | + +-- Reauthentication required + | + v + Authenticate again +``` + +UltimateAuth separates role management from authorization enforcement. + +This allows applications to provide flexible administrative interfaces while keeping access decisions under server control. + +## Next Steps + +- [Policies & Access Control](/docs/plugin-domains/policies-access-control) β€” Understand application policies and access rules. +- [Authorization Pipeline Internals](/docs/security/authorization-pipeline-internals) β€” Explore how authorization decisions are evaluated. +- [User Management](/docs/client/user-management) β€” Manage user accounts and lifecycle. +- [Session Management](/docs/client/session-management) β€” Manage authenticated sessions and devices. +- [Session Security Model](/docs/security/session-security) β€” Understand session validity and security boundaries. \ No newline at end of file diff --git a/docs/content/client-sdk/credentials.md b/docs/content/client-sdk/credentials.md new file mode 100644 index 00000000..0523b2a4 --- /dev/null +++ b/docs/content/client-sdk/credentials.md @@ -0,0 +1,1078 @@ +--- +title: Credentials +order: 6 +group: client +--- + +# Credentials + +UltimateAuth provides a dedicated client API for managing authentication credentials, including password creation, password changes, credential revocation, account recovery, and administrative credential operations. + +Through `UAuthClient.Credentials`, applications can implement credential management without manually constructing requests to the underlying endpoints. + +Credential management is security-sensitive. UltimateAuth separates authenticated credential changes from recovery workflows and provides configurable session invalidation policies. + +This guide covers the current `ICredentialClient` API, its request and response models, and practical implementation patterns. + +## 1. Understanding Credentials + +A credential is authentication material associated with a user. + +Passwords are the primary credential type supported by the current credential management request contracts. + +UltimateAuth also defines a broader `CredentialType` enum for additional authentication mechanisms. + +```csharp +public enum CredentialType +{ + Password = 0, + OneTimeCode = 10, + EmailOtp = 11, + SmsOtp = 12, + Totp = 30, + Passkey = 100, + Certificate = 200, + ApiKey = 210, + External = 1000 +} +``` + +These enum values do not mean every credential type is currently supported by the credential management API. + +For example, `AddCredentialRequest` currently accepts a secret but does not expose a credential type selector. + +Do not assume that passkeys, API keys, or external authentication credentials can be created through `AddMyAsync`. + +## 2. Accessing the Credential Client + +Inject `IUAuthClient` into your Blazor component: + +```razor +@using CodeBeam.UltimateAuth.Client +@inject IUAuthClient UAuthClient +``` + +Credential operations are available through: + +```csharp +UAuthClient.Credentials +``` + +The `Credentials` property exposes `ICredentialClient`. + +Its operations fall into three groups: + +| Group | Purpose | +|---|---| +| Self-service | Add, change, and revoke the current user's credentials | +| Recovery | Begin and complete a credential reset | +| Administration | Manage another user's credentials | + +Recovery operations are designed to support users who cannot authenticate normally. + +Administrative operations require appropriate server-side authorization. + +## 3. Change Your Password + +Use `ChangeMyAsync` when an authenticated user knows their current password and wants to replace it. + +```csharp +var result = await UAuthClient.Credentials.ChangeMyAsync( + new ChangeCredentialRequest + { + CurrentSecret = currentPassword, + NewSecret = newPassword + }); + +if (result.IsSuccess && result.Value is { IsSuccess: true }) +{ + // Password changed successfully. +} +``` + +The method returns: + +```csharp +UAuthResult +``` + +### ChangeCredentialRequest + +| Property | Type | Description | +|---|---|---| +| `CurrentSecret` | `string?` | Current password; required for self-service changes | +| `NewSecret` | `string` | New password | + +`CurrentSecret` is nullable in the shared request contract because administrative changes do not require the user's existing password. + +For self-service password changes, provide the current secret. + +### CredentialChangeResult + +The response contains: + +| Property | Description | +|---|---| +| `IsSuccess` | Whether the credential change succeeded | +| `Error` | Optional operation-level failure | +| `Type` | Credential type affected by the change | + +For example: + +```csharp +var result = await UAuthClient.Credentials.ChangeMyAsync( + new ChangeCredentialRequest + { + CurrentSecret = currentPassword, + NewSecret = newPassword + }); + +if (!result.IsSuccess) +{ + errorMessage = "Unable to change your password."; +} +else if (result.Value is { IsSuccess: false } change) +{ + errorMessage = change.Error ?? "Password change failed."; +} +else +{ + successMessage = "Your password has been changed."; +} +``` + +Check both the API-level result and the operation-level result when processing credential changes. + +### Client State Event + +After a successful `ChangeMyAsync` operation, the SDK publishes: + +```csharp +UAuthStateEvent.CredentialsChangedSelf +``` + +The event uses the configured client state event handling mode. + +It can help the application refresh authentication-related UI. + +It does not replace the server's credential or session security checks. + +## 4. Build a Password Change Form + +The following example demonstrates an authenticated password change form in Blazor. + +```razor +@using CodeBeam.UltimateAuth.Client +@using CodeBeam.UltimateAuth.Credentials.Contracts +@inject IUAuthClient UAuthClient + +

Change Password

+ + + + + + + + + + + + + +@if (!string.IsNullOrWhiteSpace(_message)) +{ +

@_message

+} + +@code { + private readonly PasswordChangeModel _model = new(); + private bool _busy; + private string? _message; + + private async Task ChangePasswordAsync() + { + if (_busy) + return; + + _busy = true; + _message = null; + + try + { + var result = await UAuthClient.Credentials.ChangeMyAsync( + new ChangeCredentialRequest + { + CurrentSecret = _model.CurrentPassword, + NewSecret = _model.NewPassword + }); + + if (result.IsSuccess && + result.Value is { IsSuccess: true }) + { + _model.CurrentPassword = ""; + _model.NewPassword = ""; + _message = "Password changed successfully."; + } + else + { + _message = "Unable to change password."; + } + } + catch (Exception) + { + _message = "The credential service is unavailable."; + } + finally + { + _busy = false; + } + } + + private sealed class PasswordChangeModel + { + [System.ComponentModel.DataAnnotations.Required] + public string CurrentPassword { get; set; } = ""; + + [System.ComponentModel.DataAnnotations.Required] + public string NewPassword { get; set; } = ""; + } +} +``` + +This example demonstrates the client integration. + +A production form should additionally provide password confirmation, appropriate password policy feedback, and safeguards against repeated submissions. + +Never log password values or include them in application telemetry. + +## 5. Add a Credential + +Use `AddMyAsync` to add a credential to the current user. + +```csharp +var result = await UAuthClient.Credentials.AddMyAsync( + new AddCredentialRequest + { + Secret = password + }); + +if (result.IsSuccess && result.Value is { IsSuccess: true } added) +{ + Console.WriteLine($"Credential ID: {added.Id}"); +} +``` + +The method returns: + +```csharp +UAuthResult +``` + +### AddCredentialRequest + +The current request contract is: + +```csharp +public sealed record AddCredentialRequest +{ + public required string Secret { get; init; } +} +``` + +Credential type and source selection are not currently exposed by this request. + +### CredentialAddResult + +The response contains: + +| Property | Description | +|---|---| +| `IsSuccess` | Whether the credential was added | +| `Error` | Optional failure information | +| `Id` | Created credential ID, when available | +| `Type` | Created credential type, when available | + +Adding a credential is subject to the server's credential policies. + +For example, the server may reject attempts to create a duplicate or otherwise unsupported credential. + +## 6. Password Change Versus Password Recovery + +UltimateAuth distinguishes between two different workflows. + +| Workflow | Typical situation | API | +|---|---|---| +| Password change | Authenticated user knows the current password | `ChangeMyAsync` | +| Password recovery | User cannot authenticate using the existing password | `BeginResetAsync` β†’ `CompleteResetAsync` | + +A password change verifies the existing secret. + +Password recovery uses a separate proof mechanism, such as a reset token or code delivered through a trusted channel. + +**Do not use administrative password changes as a replacement for public password recovery.** + +## 7. Begin Password Recovery + +Use `BeginResetAsync` to initiate a credential recovery workflow. + +```csharp +var result = await UAuthClient.Credentials.BeginResetAsync( + new BeginResetCredentialRequest + { + Identifier = "john@example.com", + CredentialType = CredentialType.Password, + ResetCodeType = ResetCodeType.Token + }); + +if (result.IsSuccess && result.Value is { } reset) +{ + Console.WriteLine($"Expires at: {reset.ExpiresAt}"); +} +``` + +The method returns: + +```csharp +UAuthResult +``` + +### BeginResetCredentialRequest + +| Property | Description | +|---|---| +| `Identifier` | User identifier associated with the recovery request | +| `CredentialType` | Credential type to recover | +| `ResetCodeType` | Reset proof format | + +### Reset Code Types + +```csharp +public enum ResetCodeType +{ + Token = 0, + Code = 10 +} +``` + +Use `Token` for a token-based recovery workflow. + +Use `Code` for a code-based recovery workflow. + +The delivery mechanism is determined by the application's configured server-side notification implementation. + +### CredentialBeginResetResult + +The result contains: + +```csharp +public sealed record CredentialBeginResetResult +{ + public DateTimeOffset ExpiresAt { get; init; } +} +``` + +**The reset token or code is not returned in the API response.** + +This prevents the recovery endpoint from directly exposing the secret needed to take over an account. + +The proof must be delivered through the configured trusted notification channel. + +## 8. Complete Password Recovery + +After the user receives a reset token or code, call `CompleteResetAsync`. + +```csharp +var result = await UAuthClient.Credentials.CompleteResetAsync( + new CompleteResetCredentialRequest + { + Identifier = "john@example.com", + CredentialType = CredentialType.Password, + ResetToken = resetToken, + NewSecret = newPassword + }); + +if (result.IsSuccess && result.Value is { IsSuccess: true }) +{ + // Password recovery completed. +} +``` + +The method returns: + +```csharp +UAuthResult +``` + +### CompleteResetCredentialRequest + +| Property | Description | +|---|---| +| `Identifier` | Identifier used for the recovery workflow | +| `CredentialType` | Credential type being recovered | +| `ResetToken` | Delivered recovery proof | +| `NewSecret` | Replacement secret | + +The property is named `ResetToken` even when the selected reset format is a code. + +Use the exact contract property name. + +### CredentialActionResult + +```csharp +public sealed record CredentialActionResult +{ + public bool IsSuccess { get; init; } + public string? Error { get; init; } +} +``` + +For example: + +```csharp +if (!result.IsSuccess) +{ + errorMessage = "Unable to complete password recovery."; +} +else if (result.Value is { IsSuccess: false } action) +{ + errorMessage = action.Error ?? "Password recovery failed."; +} +else +{ + successMessage = "Your password has been reset."; +} +``` + +After a successful `CompleteResetAsync` operation, the SDK publishes: + +```csharp +UAuthStateEvent.CredentialsChanged +``` + +The event is not the same as `CredentialsChangedSelf`, which is published by `ChangeMyAsync`. + +## 9. Complete Recovery Using a Numeric Code + +UltimateAuth supports code-based recovery through `ResetCodeType.Code`. + +Begin the recovery: + +```csharp +var begin = await UAuthClient.Credentials.BeginResetAsync( + new BeginResetCredentialRequest + { + Identifier = email, + CredentialType = CredentialType.Password, + ResetCodeType = ResetCodeType.Code + }); +``` + +Then submit the code received by the user: + +```csharp +var complete = await UAuthClient.Credentials.CompleteResetAsync( + new CompleteResetCredentialRequest + { + Identifier = email, + CredentialType = CredentialType.Password, + ResetToken = enteredCode, + NewSecret = newPassword + }); +``` + +The same completion API handles token-based and code-based recovery. + +The server is responsible for validating the submitted proof against the active recovery state. + +## 10. Build a Forgot Password Form + +A typical forgot-password interface has two stages: + +1. Request a recovery token or code. +2. Submit the received proof with a new password. + +### Step One: Request Recovery + +```razor +@using CodeBeam.UltimateAuth.Client +@using CodeBeam.UltimateAuth.Core.Domain +@using CodeBeam.UltimateAuth.Credentials.Contracts +@inject IUAuthClient UAuthClient + +

Forgot Password

+ + + + + +@if (_message is not null) +{ +

@_message

+} + +@code { + private string _email = ""; + private string? _message; + private bool _busy; + + private async Task BeginRecoveryAsync() + { + if (_busy || string.IsNullOrWhiteSpace(_email)) + return; + + _busy = true; + + try + { + var result = await UAuthClient.Credentials.BeginResetAsync( + new BeginResetCredentialRequest + { + Identifier = _email, + CredentialType = CredentialType.Password, + ResetCodeType = ResetCodeType.Token + }); + + _message = result.IsSuccess + ? "If the account is eligible, recovery instructions will be sent." + : "Unable to process the recovery request."; + } + catch (Exception) + { + _message = "Recovery is temporarily unavailable."; + } + finally + { + _busy = false; + } + } +} +``` + +The confirmation message intentionally avoids revealing whether an account exists. + +Applications should avoid introducing account enumeration through UI messages, timing differences, or diagnostic details. + +### Step Two: Complete Recovery + +```razor +@using CodeBeam.UltimateAuth.Client +@using CodeBeam.UltimateAuth.Core.Domain +@using CodeBeam.UltimateAuth.Credentials.Contracts +@inject IUAuthClient UAuthClient + +

Reset Password

+ + + + + + + +@if (_message is not null) +{ +

@_message

+} + +@code { + private string _email = ""; + private string _token = ""; + private string _newPassword = ""; + + private string? _message; + private bool _busy; + + private async Task CompleteRecoveryAsync() + { + if (_busy) + return; + + _busy = true; + _message = null; + + try + { + var result = await UAuthClient.Credentials.CompleteResetAsync( + new CompleteResetCredentialRequest + { + Identifier = _email, + CredentialType = CredentialType.Password, + ResetToken = _token, + NewSecret = _newPassword + }); + + if (result.IsSuccess && result.Value is { IsSuccess: true }) + { + _token = ""; + _newPassword = ""; + _message = "Password reset successfully."; + } + else + { + _message = "Unable to reset password."; + } + } + catch (Exception) + { + _message = "Recovery is temporarily unavailable."; + } + finally + { + _busy = false; + } + } +} +``` + +These examples show the SDK integration. + +A production implementation should also validate user input, protect recovery proofs from accidental disclosure, and avoid logging reset tokens or passwords. + +## 11. Configure Reset Notifications + +UltimateAuth uses `ICredentialResetNotifier` to deliver recovery tokens and codes. + +```csharp +public interface ICredentialResetNotifier +{ + Task NotifyAsync(CredentialResetNotification notification, CancellationToken ct = default); +} +``` + +The notification includes: + +```csharp +public sealed record CredentialResetNotification( + TenantKey Tenant, + UserKey UserKey, + CredentialType CredentialType, + ResetCodeType CodeType, + string Token, + DateTimeOffset ExpiresAt); +``` + +The application must provide a secure delivery implementation. + +### Register the Notifier + +```csharp +builder.Services.AddScoped(); +``` + +`ApplicationCredentialResetNotifier` is an application-defined implementation. + +It might deliver the proof through email, SMS, or another trusted channel associated with the user's account. + +The notifier should not expose the proof to an arbitrary address supplied by an unauthenticated caller. + +### Missing Notifier Configuration + +UltimateAuth provides a `NotConfiguredCredentialResetNotifier` fallback. + +When recovery requires notification but no implementation has been configured, the fallback throws an explanatory exception. + +This prevents a reset flow from silently succeeding without a configured proof delivery mechanism. + +**Never return recovery tokens to anonymous clients as a substitute for configuring the notifier.** + +## 12. Recovery Security Behavior + +Credential recovery is designed around several security properties. + +### Account Enumeration Resistance + +Recovery initiation should not reveal whether a submitted identifier belongs to an account. + +Applications should preserve this behavior in their own UI and logging policies. + +### Proof Expiration + +Recovery proofs have a server-controlled lifetime. + +The client does not choose an arbitrary expiration time. + +### Single-Use Proofs + +A successfully consumed recovery proof must not be reusable. + +After successful completion, a second attempt using the same proof should be rejected. + +### Identifier and Target Binding + +Recovery must apply to the correct user and credential. + +Administrative recovery must not permit an identifier belonging to one user to reset another user's credential. + +### Session Revocation + +Successful recovery may invalidate existing sessions according to the configured credential session policy. + +By default, UltimateAuth revokes all session chains after recovery. + +For further details, see [Session Security Model](/docs/security/session-security). + +## 13. Revoke Your Credential + +Use `RevokeMyAsync` to revoke a credential belonging to the authenticated user. + +```csharp +var result = await UAuthClient.Credentials.RevokeMyAsync( + new RevokeCredentialRequest + { + Id = credentialId + }); + +if (result.IsSuccess) +{ + // Credential revoked. +} +``` + +The request contract is: + +```csharp +public sealed record RevokeCredentialRequest +{ + public Guid Id { get; init; } +} +``` + +Use the credential record ID, not a user ID or session ID. + +Credential revocation is distinct from credential deletion. + +Revocation changes the credential's usability, while deletion uses a separate lifecycle operation available through the administrative API. + +### State Event + +After successful `RevokeMyAsync`, the SDK publishes: + +```csharp +UAuthStateEvent.CredentialsChanged +``` + +The application can use this event to refresh credential-related UI. + +Credential revocation may affect authentication eligibility and session continuation depending on server policy. + +## 14. Administrative Credential Management + +Administrative credential operations target a specific user. + +They require appropriate server-side authorization. + +### Add a Credential + +```csharp +var result = await UAuthClient.Credentials.AddUserAsync(userKey, + new AddCredentialRequest + { + Secret = temporaryPassword + }); + +if (result.IsSuccess && result.Value is { IsSuccess: true } added) +{ + Console.WriteLine(added.Id); +} +``` + +The server validates whether the administrative actor may add a credential for the target user. + +### Change a User's Password + +```csharp +var result = await UAuthClient.Credentials.ChangeUserAsync(userKey, + new ChangeCredentialRequest + { + NewSecret = newPassword + }); + +if (result.IsSuccess && result.Value is { IsSuccess: true }) +{ + // Administrative password change succeeded. +} +``` + +`CurrentSecret` is not required for an authorized administrative credential change. + +This makes administrative credential changes a privileged operation. + +Do not expose this capability through ordinary self-service UI. + +### Revoke a User's Credential + +```csharp +var result = await UAuthClient.Credentials.RevokeUserAsync(userKey, + new RevokeCredentialRequest + { + Id = credentialId + }); +``` + +### Begin Administrative Recovery + +```csharp +var result = await UAuthClient.Credentials.BeginResetUserAsync(userKey, + new BeginResetCredentialRequest + { + Identifier = userEmail, + CredentialType = CredentialType.Password, + ResetCodeType = ResetCodeType.Token + }); +``` + +### Complete Administrative Recovery + +```csharp +var result = await UAuthClient.Credentials.CompleteResetUserAsync(userKey, + new CompleteResetCredentialRequest + { + Identifier = userEmail, + CredentialType = CredentialType.Password, + ResetToken = recoveryToken, + NewSecret = newPassword + }); +``` + +Administrative recovery remains a proof-based workflow. + +The server validates the relationship between the target user and the supplied identifier. + +### Delete a User's Credential + +```csharp +var result = await UAuthClient.Credentials.DeleteUserAsync(userKey, + new DeleteCredentialRequest + { + Id = credentialId, + Mode = DeleteMode.Soft + }); +``` + +### Hard Delete + +```csharp +var result = await UAuthClient.Credentials.DeleteUserAsync(userKey, + new DeleteCredentialRequest + { + Id = credentialId, + Mode = DeleteMode.Hard + }); +``` + +Hard deletion is a destructive administrative operation. + +Its availability and effects depend on server authorization and credential lifecycle policies. + +Administrative operations do not automatically publish the same local credential state events as self-service operations. + +## 15. Credential Changes and Session Invalidation + +UltimateAuth allows applications to configure how existing sessions are handled after credential changes and recovery. + +The server exposes: + +```csharp +UAuthServerOptions.CredentialSessionPolicy +``` + +The policy contains: + +| Option | Default | Purpose | +|---|---|---| +| `OnSelfChange` | `PreserveCurrent` | Sessions after self-service credential changes | +| `OnAdminChange` | `RevokeAll` | Sessions after administrative credential changes | +| `OnRecovery` | `RevokeAll` | Sessions after credential recovery | + +### CredentialSessionBehavior + +```csharp +public enum CredentialSessionBehavior +{ + PreserveAll = 0, + PreserveCurrent = 10, + RevokeAll = 20 +} +``` + +| Behavior | Meaning | +|---|---| +| `PreserveAll` | Preserve existing session chains | +| `PreserveCurrent` | Preserve the current chain and revoke other chains | +| `RevokeAll` | Revoke all applicable session chains | + +### Configure the Policy + +```csharp +builder.Services.AddUltimateAuthServer(options => +{ + options.CredentialSessionPolicy.OnSelfChange = CredentialSessionBehavior.PreserveCurrent; + options.CredentialSessionPolicy.OnAdminChange = CredentialSessionBehavior.RevokeAll; + options.CredentialSessionPolicy.OnRecovery = CredentialSessionBehavior.RevokeAll; +}); +``` + +The defaults are intended to balance usability and security. + +For example, an authenticated user changing their password can retain their current device while other devices are invalidated. + +An administrator changing a user's password typically requires broader session revocation. + +### Recovery and PreserveCurrent + +`PreserveCurrent` is not meaningful for many recovery workflows because the user may not have an authenticated current session. + +UltimateAuth's recovery policy documentation specifies that `PreserveCurrent` is treated as `RevokeAll` in this context. + +### Authentication Mode Considerations + +The precise effect of session invalidation depends on the authentication mode. + +In particular, a locally validated JWT access token may remain usable until expiration if the resource server does not consult current revocation state. + +Credential session policies should be understood together with the application's token validation strategy. + +See [Token Security](/docs/security/token-security). + +## 16. Handling Credential Results + +Credential operations use `UAuthResult` or `UAuthResult`. + +Several operations also return a domain-level success flag. + +### Credential Creation + +```csharp +var result = await UAuthClient.Credentials.AddMyAsync(request); + +if (!result.IsSuccess) +{ + // API-level failure. +} +else if (result.Value is { IsSuccess: false } add) +{ + // Credential creation was rejected. + Console.WriteLine(add.Error); +} +else if (result.Value is { IsSuccess: true } added) +{ + Console.WriteLine(added.Id); +} +``` + +### Credential Recovery + +```csharp +var result = await UAuthClient.Credentials.CompleteResetAsync(request); + +if (!result.IsSuccess) +{ + // API-level failure. +} +else if (result.Value is { IsSuccess: false } action) +{ + // Recovery did not complete successfully. +} +else +{ + // Recovery succeeded. +} +``` + +### HTTP Status Helpers + +```csharp +if (result.Http.IsUnauthorized) +{ + // Authentication required. +} +else if (result.Http.IsForbidden) +{ + // Operation not permitted. +} +else if (result.Http.IsConflict) +{ + // Conflicting or expired operation state. +} +else if (result.Http.IsBadRequest) +{ + // Invalid request. +} +``` + +The actual status depends on the server's response. + +Avoid exposing sensitive internal credential errors directly to end users. + +## 17. Credential State Events + +The current `UAuthCredentialClient` publishes the following events after successful self-service operations. + +| Operation | Event | +|---|---| +| `ChangeMyAsync` | `CredentialsChangedSelf` | +| `RevokeMyAsync` | `CredentialsChanged` | +| `CompleteResetAsync` | `CredentialsChanged` | + +The following operations do not publish those events in the current client implementation: + +- `AddMyAsync` +- `BeginResetAsync` +- Administrative credential operations + +Applications should refresh relevant data explicitly where needed. + +Events help synchronize client state but do not themselves establish that a credential or session has been invalidated on the server. + +## 18. Security Considerations + +### Never Expose Stored Secrets + +Credential secrets must not be returned through credential management responses. + +Applications should never log passwords, reset proofs, or other authentication secrets. + +### Use Secure Transport + +Credential operations must be performed over appropriately secured transport. + +### Protect Recovery Proofs + +Recovery tokens and codes should be short-lived, single-use, and delivered through trusted channels. + +Do not store recovery proofs in persistent client storage unless the application has a carefully justified security requirement. + +### Keep Self-Service and Administrative Operations Separate + +Self-service changes require the current credential. + +Administrative changes are privileged and require server-side authorization. + +Do not substitute one for the other. + +### Preserve Account Recovery Safety + +Avoid revealing account existence through public recovery responses. + +Apply appropriate rate limiting, proof attempt limits, and abuse controls. + +### Consider Session Consequences + +Changing or resetting a password can affect existing sessions. + +Applications should explain the expected consequences to users, especially when other devices will be logged out. + +### Do Not Assume Every Credential Type Is Implemented + +The existence of a `CredentialType` enum member does not establish that its complete lifecycle is supported. + +Use the currently implemented credential contracts and documented authentication flows. + +## Next Steps + +- [Authentication](/docs/client/authentication) β€” Login, logout, refresh, and authentication flows. +- [Identifiers](/docs/client/identifiers) β€” Manage user identifiers and ownership verification. +- [User Management](/docs/client/user-management) β€” Manage profiles and user lifecycle. +- [Session Management](/docs/client/session-management) β€” Inspect and revoke sessions. +- [Token Security](/docs/security/token-security) β€” Understand token validation and revocation behavior. +- [Session Security Model](/docs/security/session-security) β€” Understand session invalidation and account-level security boundaries. \ No newline at end of file diff --git a/docs/content/client-sdk/identifiers.md b/docs/content/client-sdk/identifiers.md new file mode 100644 index 00000000..4e6fbec7 --- /dev/null +++ b/docs/content/client-sdk/identifiers.md @@ -0,0 +1,1045 @@ +--- +title: Identifiers +order: 5 +group: client +--- + +# Identifiers + +UltimateAuth treats user identifiers as independent, managed entities rather than simple fields stored directly on a user profile. + +Through `UAuthClient.Identifiers`, applications can manage usernames, email addresses, phone numbers, and custom identifiers, including primary designation, ownership verification, availability checks, and administrative operations. + +This guide covers the public `IUserIdentifierClient` API with practical examples for account settings, registration forms, and administration interfaces. + +## 1. Understanding User Identifiers + +An identifier represents a value associated with a user that may be used for identification, authentication, or communication, depending on the application's configuration. + +Supported identifier types are: + +```csharp +public enum UserIdentifierType +{ + Username = 0, + Email = 10, + Phone = 20, + Custom = 100 +} +``` + +A single user can have multiple identifiers. + +For example: + +```text +User + | + +-- Username + | johndoe [Primary] + | + +-- Email + | john@example.com [Primary, Verified] + | john@business.com [Verified] + | + +-- Phone + +1 555 0100 [Primary, Verified] +``` + +Each identifier has its own value, type, verification state, and primary designation. + +**Identifiers are not profiles.** A user profile contains information such as display name and biography, while identifiers represent managed identity-related values. + +For general profile management, see [User Management](/docs/client/user-management). + +## 2. Accessing the Identifier Client + +Inject `IUAuthClient` into a Blazor component: + +```razor +@using CodeBeam.UltimateAuth.Client +@inject IUAuthClient UAuthClient +``` + +Identifier operations are available through: + +```csharp +UAuthClient.Identifiers +``` + +This property exposes `IUserIdentifierClient`. + +The client supports three main categories of operations: + +| Category | Purpose | +|---|---| +| Self-service | Manage identifiers belonging to the current user | +| Administration | Manage identifiers belonging to a specified user | +| Availability | Check whether an identifier value is valid and available | + +All operations are subject to server-side authorization and identifier policies. + +## 3. Get My Identifiers + +Use `GetMyAsync` to retrieve identifiers belonging to the authenticated user. + +```csharp +var result = await UAuthClient.Identifiers.GetMyAsync(); + +if (result.IsSuccess && result.Value is { } page) +{ + foreach (var identifier in page.Items) + { + Console.WriteLine($"{identifier.Type}: {identifier.Value}"); + } +} +``` + +The method returns: + +```csharp +UAuthResult> +``` + +### UserIdentifierInfo + +Each identifier is represented by `UserIdentifierInfo`. + +| Property | Type | Description | +|---|---|---| +| `Id` | `Guid` | Identifier record ID | +| `Type` | `UserIdentifierType` | Identifier type | +| `Value` | `string` | Identifier value | +| `NormalizedValue` | `string` | Server-normalized value | +| `IsPrimary` | `bool` | Whether it is primary for its type | +| `IsVerified` | `bool` | Whether ownership has been verified | +| `CreatedAt` | `DateTimeOffset` | Creation timestamp | +| `VerifiedAt` | `DateTimeOffset?` | Verification timestamp | +| `Version` | `long` | Entity version | + +The `Id` identifies the managed identifier record and is used by update, verification, primary designation, and deletion operations. + +### Filter Identifiers in the UI + +For example, display verified email addresses from a returned page: + +```csharp +var result = await UAuthClient.Identifiers.GetMyAsync(); + +if (result.IsSuccess && result.Value is { } page) +{ + var verifiedEmails = page.Items + .Where(x => + x.Type == UserIdentifierType.Email && + x.IsVerified) + .ToList(); +} +``` + +This filters the current page only. Retrieve additional pages when the application needs the complete collection. + +## 4. Pagination + +`GetMyAsync` accepts an optional `PageRequest`. + +```csharp +var result = await UAuthClient.Identifiers.GetMyAsync( + new PageRequest + { + PageNumber = 1, + PageSize = 20 + }); +``` + +The server applies its configured pagination policy. + +The returned `PagedResult` includes: + +- `Items` +- `TotalCount` +- `PageNumber` +- `PageSize` +- `HasNext` + +For example: + +```csharp +if (result.IsSuccess && result.Value is { } page) +{ + Console.WriteLine($"Total: {page.TotalCount}"); + + if (page.HasNext) + { + // Enable the next-page control. + } +} +``` + +Do not assume the first page contains every identifier. + +## 5. Add an Identifier + +Use `AddMyAsync` to associate a new identifier with the current user. + +### Add an Email Address + +```csharp +var result = await UAuthClient.Identifiers.AddMyAsync( + new AddUserIdentifierRequest + { + Type = UserIdentifierType.Email, + Value = "john@business.com" + }); + +if (result.IsSuccess) +{ + // Identifier added. +} +``` + +### Add a Phone Number + +```csharp +var result = await UAuthClient.Identifiers.AddMyAsync( + new AddUserIdentifierRequest + { + Type = UserIdentifierType.Phone, + Value = "+15550100" + }); +``` + +### Add a Username + +```csharp +var result = await UAuthClient.Identifiers.AddMyAsync( + new AddUserIdentifierRequest + { + Type = UserIdentifierType.Username, + Value = "johndoe" + }); +``` + +### Add a Custom Identifier + +```csharp +var result = await UAuthClient.Identifiers.AddMyAsync( + new AddUserIdentifierRequest + { + Type = UserIdentifierType.Custom, + Value = "external-customer-123" + }); +``` + +Custom identifier support does not automatically mean that arbitrary custom values can be used for authentication. + +Login eligibility depends on server configuration and the relevant identifier policies. + +### Add as Primary + +Set `IsPrimary` when creating the identifier: + +```csharp +var result = await UAuthClient.Identifiers.AddMyAsync( + new AddUserIdentifierRequest + { + Type = UserIdentifierType.Email, + Value = "john@example.com", + IsPrimary = true + }); +``` + +The server determines whether the new value can be assigned as primary. + +Adding an identifier does not automatically establish verified ownership. + +## 6. Identifier Uniqueness and Normalization + +UltimateAuth normalizes identifier values and enforces uniqueness within the relevant tenant boundary. + +This allows applications to work with consistent identifier representations. + +For example, email or username normalization may affect how equivalent input values are compared. + +The exact normalization rules depend on the identifier type and server implementation. + +### Use Server Results as Authoritative + +Do not implement a separate client-side normalization algorithm and assume it exactly matches the server. + +Client-side formatting can improve the user experience, but the server must remain responsible for: + +- Validating identifier format. +- Normalizing identifier values. +- Enforcing uniqueness. +- Checking tenant ownership. +- Applying identifier-specific policies. + +An identifier value that appears available in the UI may become unavailable before the user submits it. + +Always handle the result of the actual add or update operation. + +## 7. Check Identifier Availability + +Use `CheckAvailabilityAsync` to check whether an identifier value is valid and available. + +This is useful for registration forms and account settings. + +### Check a Username + +```csharp +var result = await UAuthClient.Identifiers.CheckAvailabilityAsync( + new CheckUserIdentifierAvailabilityRequest + { + Type = UserIdentifierType.Username, + Value = "johndoe" + }); + +if (result.IsSuccess && result.Value is { } availability) +{ + if (!availability.IsValid) + { + // The identifier value is invalid. + } + else if (!availability.IsAvailable) + { + // The identifier is already unavailable. + } + else + { + // The identifier is currently available. + } +} +``` + +### Check an Email Address + +```csharp +var result = await UAuthClient.Identifiers.CheckAvailabilityAsync( + new CheckUserIdentifierAvailabilityRequest + { + Type = UserIdentifierType.Email, + Value = "john@example.com" + }); +``` + +### Availability Result + +The operation returns: + +```csharp +UAuthResult +``` + +The response value contains: + +| Property | Description | +|---|---| +| `IsValid` | Whether the value passes validation | +| `IsAvailable` | Whether the value is currently available | +| `NormalizedValue` | Normalized identifier value, when available | +| `Errors` | Validation errors | + +### Display Validation Feedback + +```csharp +var result = await UAuthClient.Identifiers.CheckAvailabilityAsync( + new CheckUserIdentifierAvailabilityRequest + { + Type = UserIdentifierType.Username, + Value = username + }); + +if (result.IsSuccess && result.Value is { } availability) +{ + if (!availability.IsValid) + { + foreach (var error in availability.Errors) + { + // Display the appropriate validation feedback. + } + } + else if (!availability.IsAvailable) + { + message = "This username is unavailable."; + } + else + { + message = "This username is available."; + } +} +``` + +**An availability check is advisory, not a reservation.** + +The value may become unavailable between the availability check and the subsequent creation operation. + +For public registration forms, also consider whether exposing detailed availability information could enable account enumeration. Apply appropriate server-side controls. + +## 8. Update an Identifier + +Use `UpdateMyAsync` to change the value of an existing identifier. + +```csharp +var result = await UAuthClient.Identifiers.UpdateMyAsync( + new UpdateUserIdentifierRequest + { + Id = identifierId, + NewValue = "newemail@example.com" + }); + +if (result.IsSuccess) +{ + // Identifier updated. +} +``` + +`UpdateUserIdentifierRequest` contains: + +| Property | Description | +|---|---| +| `Id` | Existing identifier record ID | +| `NewValue` | Replacement identifier value | + +Unlike profile updates, this operation targets one identifier record. + +It does not replace the user's entire identifier collection. + +### Verification After an Update + +Changing an identifier's value may require ownership verification again. + +For example, verification of an old email address does not prove ownership of a new email address. + +Applications should reload the identifier after updating it and inspect its current `IsVerified` state. + +Do not assume the previous verification state remains valid. + +## 9. Understanding Primary Identifiers + +UltimateAuth supports **one primary identifier per type**, rather than one global primary identifier for the entire user. + +For example, a user may have: + +- One primary username. +- One primary email. +- One primary phone number. + +These primary designations are independent. + +The primary flag identifies the preferred identifier within its type. + +How the application uses primary identifiers for display, communication, or authentication depends on its configuration and business rules. + +### Set a Primary Identifier + +Use `SetMyPrimaryAsync`: + +```csharp +var result = await UAuthClient.Identifiers.SetMyPrimaryAsync( + new SetPrimaryUserIdentifierRequest + { + Id = identifierId + }); + +if (result.IsSuccess) +{ + // Primary designation updated. +} +``` + +When a different identifier becomes primary within the same type, the server maintains the per-type primary constraint. + +Setting an email address as primary does not unset the primary phone number or username. + +### Unset a Primary Identifier + +Use `UnsetMyPrimaryAsync`: + +```csharp +var result = await UAuthClient.Identifiers.UnsetMyPrimaryAsync( + new UnsetPrimaryUserIdentifierRequest + { + Id = identifierId + }); +``` + +Whether a primary identifier can be unset without selecting a replacement depends on server policy. + +Applications should handle rejection when the server requires a primary identifier of that type. + +### Display Primary Identifiers + +```csharp +var result = await UAuthClient.Identifiers.GetMyAsync(); + +if (result.IsSuccess && result.Value is { } page) +{ + var primaryIdentifiers = page.Items.Where(x => x.IsPrimary).ToList(); + + foreach (var identifier in primaryIdentifiers) + { + Console.WriteLine($"{identifier.Type}: {identifier.Value}"); + } +} +``` + +Remember that this example searches only the returned page. + +## 10. Verify Identifier Ownership + +Verification is a security-sensitive operation. + +A user must not be able to mark an email address or phone number as verified merely by knowing or submitting its identifier ID. + +UltimateAuth requires ownership proof for self-service verification. + +### Verify an Identifier + +Use `VerifyMyAsync` with a proof obtained through your application's verification workflow. + +```csharp +var result = await UAuthClient.Identifiers.VerifyMyAsync( + new VerifyUserIdentifierRequest + { + Id = identifierId, + Proof = verificationProof + }); + +if (result.IsSuccess) +{ + // Identifier verification succeeded. +} +``` + +The `Proof` property is required for self-service verification. + +For example, a verification workflow may deliver a code to the email address or phone number being verified. + +The application then submits the received proof through `VerifyMyAsync`. + +The SDK does not itself guarantee delivery of verification messages. + +### Verification Flow + +A typical verification workflow looks like this: + +```text +User adds identifier + | + v +Application initiates verification + | + v +Verification proof is delivered + | + v +User submits proof + | + v +Identifiers.VerifyMyAsync(...) + | + v +Server validates ownership proof + | + +-- Valid --> Identifier verified + | + +-- Invalid --> Verification rejected +``` + +The mechanism used to issue and deliver the proof is application-specific. + +The server-side verification implementation must validate the proof before updating the identifier's verified state. + +## 11. Server-Side Verification with IUserIdentifierVerifier + +UltimateAuth defines `IUserIdentifierVerifier` for validating identifier ownership proof. + +```csharp +public interface IUserIdentifierVerifier +{ + Task VerifyAsync(UserIdentifierVerificationContext context, CancellationToken ct = default); +} +``` + +The verification context includes: + +```csharp +public sealed record UserIdentifierVerificationContext( + TenantKey Tenant, + UserKey UserKey, + Guid IdentifierId, + UserIdentifierType Type, + string Value, + string Proof); +``` + +A secure verifier must bind the proof to: + +- The tenant. +- The user. +- The identifier record. +- The current identifier value. + +It must also enforce appropriate expiration, attempt limits, and single-use consumption. + +### Register Your Verifier + +In the server application, register your implementation: + +```csharp +builder.Services.AddScoped(); +``` + +`ApplicationIdentifierVerifier` represents your own implementation of the interface. + +UltimateAuth includes a `NotConfiguredUserIdentifierVerifier` fallback that throws an explanatory exception when verification is attempted without a configured implementation. + +This prevents an unconfigured verifier from silently approving identifier ownership. + +**Do not implement a verifier that returns `true` unconditionally in production.** + +## 12. Delete an Identifier + +Use `DeleteMyAsync` to remove an identifier. + +```csharp +var result = await UAuthClient.Identifiers.DeleteMyAsync( + new DeleteUserIdentifierRequest + { + Id = identifierId, + Mode = DeleteMode.Soft + }); + +if (result.IsSuccess) +{ + // Identifier deleted. +} +``` + +The request includes a deletion mode: + +```csharp +public enum DeleteMode +{ + Soft = 0, + Hard = 10 +} +``` + +### Hard Delete + +```csharp +var result = await UAuthClient.Identifiers.DeleteMyAsync( + new DeleteUserIdentifierRequest + { + Id = identifierId, + Mode = DeleteMode.Hard + }); +``` + +Deletion behavior depends on the server's identifier lifecycle and authorization rules. + +An application should not assume that every caller can select either deletion mode. + +### Deleting Primary Identifiers + +Deleting a primary identifier may be restricted. + +For example, the server may require another identifier of the same type to become primary first. + +Applications should inspect the result and provide appropriate feedback. + +### Deleting Login Identifiers + +Removing an identifier that is used for authentication may affect future login attempts. + +Before exposing deletion, consider whether the user will retain a supported method of authentication. + +Identifier deletion should not be treated as equivalent to revoking all existing sessions. + +## 13. Build an Identifier Management Page + +The following example demonstrates a basic Blazor page that lists identifiers, adds an email address, and allows users to change primary designation. + +```razor +@using CodeBeam.UltimateAuth.Client +@using CodeBeam.UltimateAuth.Core.Contracts +@using CodeBeam.UltimateAuth.Users.Contracts +@inject IUAuthClient UAuthClient + +

My Identifiers

+ +@if (_loading) +{ +

Loading identifiers...

+} +else +{ + @foreach (var item in _identifiers) + { +
+ @item.Value + @item.Type + + @if (item.IsVerified) + { + Verified + } + else + { + Not verified + } + + @if (item.IsPrimary) + { + Primary + } + else + { + + } +
+ } +} + +

Add Email

+ + + +@if (!string.IsNullOrWhiteSpace(_message)) +{ +

@_message

+} + +@code { + private IReadOnlyList _identifiers = []; + + private string _newEmail = ""; + private string? _message; + + private bool _loading = true; + private bool _busy; + + protected override async Task OnInitializedAsync() + { + await LoadAsync(); + } + + private async Task LoadAsync() + { + _loading = true; + + try + { + var result = await UAuthClient.Identifiers.GetMyAsync( + new PageRequest + { + PageNumber = 1, + PageSize = 50 + }); + + if (result.IsSuccess && result.Value is { } page) + { + _identifiers = page.Items; + } + else + { + _message = "Unable to load identifiers."; + } + } + catch (Exception) + { + _message = "The identifier service is unavailable."; + } + finally + { + _loading = false; + } + } + + private async Task AddEmailAsync() + { + if (_busy || string.IsNullOrWhiteSpace(_newEmail)) + return; + + _busy = true; + _message = null; + + try + { + var result = await UAuthClient.Identifiers.AddMyAsync( + new AddUserIdentifierRequest + { + Type = UserIdentifierType.Email, + Value = _newEmail + }); + + if (result.IsSuccess) + { + _newEmail = ""; + await LoadAsync(); + } + else + { + _message = "Unable to add email."; + } + } + finally + { + _busy = false; + } + } + + private async Task SetPrimaryAsync(Guid id) + { + var result = await UAuthClient.Identifiers.SetMyPrimaryAsync( + new SetPrimaryUserIdentifierRequest + { + Id = id + }); + + if (result.IsSuccess) + { + await LoadAsync(); + } + else + { + _message = "Unable to update primary identifier."; + } + } +} +``` + +This example demonstrates the core SDK integration. + +It displays the first page of identifiers. A production implementation should add pagination, verification actions, deletion confirmation, appropriate exception handling, and loading states for all mutations. + +For sensitive changes, consider requiring recent authentication or additional verification according to application policy. + +## 14. Administrative Identifier Management + +UltimateAuth provides administrative methods for managing identifiers belonging to another user. + +These operations require appropriate server-side authorization. + +### Get a User's Identifiers + +```csharp +var result = await UAuthClient.Identifiers.GetUserAsync(userKey, + new PageRequest + { + PageNumber = 1, + PageSize = 20 + }); + +if (result.IsSuccess && result.Value is { } page) +{ + foreach (var identifier in page.Items) + { + Console.WriteLine(identifier.Value); + } +} +``` + +### Add an Identifier + +```csharp +var result = await UAuthClient.Identifiers.AddUserAsync(userKey, + new AddUserIdentifierRequest + { + Type = UserIdentifierType.Email, + Value = "employee@example.com" + }); +``` + +### Update an Identifier + +```csharp +var result = await UAuthClient.Identifiers.UpdateUserAsync(userKey, + new UpdateUserIdentifierRequest + { + Id = identifierId, + NewValue = "updated@example.com" + }); +``` + +### Set Primary + +```csharp +var result = await UAuthClient.Identifiers.SetUserPrimaryAsync(userKey, + new SetPrimaryUserIdentifierRequest + { + Id = identifierId + }); +``` + +### Unset Primary + +```csharp +var result = await UAuthClient.Identifiers.UnsetUserPrimaryAsync(userKey, + new UnsetPrimaryUserIdentifierRequest + { + Id = identifierId + }); +``` + +### Verify an Identifier as Administrator + +```csharp +var result = await UAuthClient.Identifiers.VerifyUserAsync(userKey, + new VerifyUserIdentifierRequest + { + Id = identifierId + }); +``` + +The request contract does not require `Proof` for authorized administrative verification. + +However, this does not mean any authenticated caller may bypass ownership verification. + +The server must authorize the administrative verification operation and enforce its security requirements. + +Administrative verification should be reserved for trusted workflows with appropriate auditability. + +### Delete an Identifier + +```csharp +var result = await UAuthClient.Identifiers.DeleteUserAsync(userKey, + new DeleteUserIdentifierRequest + { + Id = identifierId, + Mode = DeleteMode.Soft + }); +``` + +Administrative operations act on the specified target user. + +They do not automatically publish the same local identifier state events as self-service operations. + +## 15. Handling Identifier Operation Results + +Most identifier mutations return `UAuthResult`. + +Retrieval and availability methods return `UAuthResult`. + +### Handle Success and Failure + +```csharp +var result = await UAuthClient.Identifiers.AddMyAsync(request); + +if (result.IsSuccess) +{ + // Operation succeeded. +} +else if (result.Http.IsConflict) +{ + // Identifier conflict. +} +else if (result.Http.IsForbidden) +{ + // Operation not permitted. +} +else if (result.Http.IsBadRequest) +{ + // Invalid request. +} +else +{ + // Handle another failure. +} +``` + +The actual status code depends on the server's response. + +`UAuthResult` also exposes `Problem`, `ErrorText`, `CorrelationId`, and `TraceId`. + +Use structured errors when appropriate, but avoid displaying sensitive internal information directly to users. + +## 16. Identifier State Events + +The client publishes `IdentifiersChanged` after successful self-service identifier mutations. + +| Operation | Event | +|---|---| +| `AddMyAsync` | `IdentifiersChanged` | +| `UpdateMyAsync` | `IdentifiersChanged` | +| `SetMyPrimaryAsync` | `IdentifiersChanged` | +| `UnsetMyPrimaryAsync` | `IdentifiersChanged` | +| `VerifyMyAsync` | `IdentifiersChanged` | +| `DeleteMyAsync` | `IdentifiersChanged` | + +For add and update operations, the event carries the corresponding request payload. + +For primary designation, verification, and deletion, the SDK publishes the event without a request payload. + +The event handling mode follows the configured client state event options. + +Read operations and availability checks do not publish `IdentifiersChanged`. + +Administrative mutations also do not automatically publish this local self-service event. + +### Refresh Identifier Data After Changes + +A component may respond to an identifier state event by reloading its identifier list. + +Alternatively, after a successful mutation, the calling component can explicitly refresh the relevant data. + +Client events are synchronization mechanisms, not authoritative security decisions. + +## 17. Security Considerations + +### Verification Requires Proof + +Self-service verification must establish ownership through a valid proof. + +Knowing an identifier ID is not sufficient. + +The server must validate proof binding, expiration, attempt limits, and single-use consumption. + +### Primary Does Not Mean Verified + +An identifier's primary designation and verification state are separate properties. + +Applications should not assume that a primary email or phone number has been verified. + +Check `IsVerified` where verified ownership is required. + +### Login Eligibility Is Separate + +An identifier may exist on an account without being permitted for login. + +Login eligibility depends on the configured authentication and identifier policies. + +Do not assume that every custom identifier is automatically an authentication identifier. + +### Tenant Boundaries Matter + +Identifier uniqueness and ownership are enforced within the relevant tenant context. + +Client code must not attempt to bypass tenant isolation by modifying request paths or target user identifiers. + +### Availability Checks Are Not Reservations + +Availability can change between requests. + +Always enforce uniqueness during the actual identifier mutation. + +### Administrative Verification Is Privileged + +The administrative verification API has different proof requirements from self-service verification. + +Only appropriately authorized actors should be able to use it. + +### Identifier Changes May Affect Authentication + +Changing or deleting a login identifier may affect future authentication. + +Applications should distinguish identifier management from session revocation and credential management. + +## Next Steps + +- [User Management](/docs/client/user-management) β€” Manage user profiles, account status, and lifecycle. +- [Credentials](/docs/client/credentials) β€” Manage passwords, credentials, and recovery workflows. +- [Authentication](/docs/client/authentication) β€” Understand login and authentication flows. +- [Users](/docs/plugin-domains/users) β€” Explore the user domain architecture. +- [Session Security Model](/docs/security/session-security) β€” Understand authentication security boundaries. \ No newline at end of file diff --git a/docs/content/client-sdk/overview.md b/docs/content/client-sdk/overview.md new file mode 100644 index 00000000..28441579 --- /dev/null +++ b/docs/content/client-sdk/overview.md @@ -0,0 +1,284 @@ +--- +title: Client SDK Overview +order: 1 +group: client +--- + +# Client SDK Overview + +The UltimateAuth Client SDK provides a unified way to integrate authentication, session management, user operations, credentials, and authorization into .NET applications. + +Instead of manually constructing requests for every authentication endpoint, applications can use dedicated client services that understand UltimateAuth's API contracts and authentication workflows. + +The SDK is designed for applications that need to interact with an UltimateAuth server, including Blazor WebAssembly, Blazor Server, and other supported .NET client environments. + +**This section is the practical developer guide to using UltimateAuth from application code.** + +## What the Client SDK Provides + +The Client SDK is more than a collection of HTTP methods. + +It brings together several responsibilities: + +- **Authentication workflows** β€” Start login, refresh, logout, and other supported authentication operations. +- **Session management** β€” Interact with available session and device management capabilities. +- **User management** β€” Retrieve and manage user information through the appropriate API contracts. +- **Credential operations** β€” Manage credentials and initiate supported recovery workflows. +- **Authorization management** β€” Work with roles, permissions, and other exposed authorization operations. +- **Structured results** β€” Receive operation results through the SDK's response contracts. +- **Client state integration** β€” Coordinate supported authentication state changes and notifications. + +The SDK helps applications interact with UltimateAuth consistently while keeping the server responsible for authentication and authorization enforcement. + +It does not replace server-side security checks. + +## The Client Architecture + +UltimateAuth separates client responsibilities into specialized services. + +The main client entry point is `UAuthClient`, which provides access to the SDK's functional areas. + +Conceptually: + +```text +Application + | + v +UAuthClient + | + +-- Flows + | Login, logout, refresh, PKCE + | + +-- Sessions + | Sessions and device-associated chains + | + +-- Users + | User information and lifecycle + | + +-- Identifiers + | User identifiers and verification + | + +-- Credentials + | Credential management and recovery + | + +-- Authorization + Roles, permissions, and access management +``` + +These represent functional responsibilities. The precise available members and operations depend on the installed packages and supported SDK contracts. + +### Why Specialized Clients? + +Authentication-related operations have different requirements. + +A login flow may establish authentication state and involve redirects. A user management operation may retrieve or update account information. A credential recovery operation may require additional verification and security handling. + +Keeping these responsibilities separate provides a clearer programming model and makes individual APIs easier to discover. + +## Getting Started with UAuthClient + +In a supported dependency injection environment, application components can consume the configured UltimateAuth client. + +For example, a Blazor component can receive the client through dependency injection: + +```razor +@inject UAuthClient UAuthClient +``` + +Once configured, the application uses the appropriate client service for the operation it needs. + +The following illustrates the intended programming model: + +```csharp +var user = await UAuthClient.Users.GetMeAsync(); +``` + +The SDK handles the corresponding client operation and returns its structured result. + +Before using the client, the application must register the appropriate UltimateAuth services and configure its connection to the authentication server. + +For a complete setup, follow the Getting Started guide for your application environment. + +## Choose Your Application Environment + +UltimateAuth supports different application architectures, but their authentication and transport requirements are not identical. + +### Blazor WebAssembly + +Blazor WebAssembly executes application code in the browser. + +The client communicates with the authentication server and protected APIs through browser-compatible HTTP requests. + +Browser security rules, credential handling, CORS configuration, and authentication state synchronization are relevant to this environment. + +### Blazor Server + +Blazor Server executes application code on the server while maintaining an interactive connection to the browser. + +Authentication state, cookie behavior, and server-side execution require different considerations from a browser-hosted WebAssembly application. + +Server-side code may also invoke application services directly rather than making an HTTP request. + +Such operations still require appropriate server-side authorization enforcement. + +### Interactive Web Applications + +Applications combining server rendering and WebAssembly interactivity may have multiple execution environments. + +Developers should distinguish between operations performed on the server and operations performed in the browser. + +Shared UI components do not automatically imply identical transport or credential handling. + +### Other .NET Clients + +Other supported .NET application environments can use the relevant client SDK capabilities according to their platform requirements. + +Credential storage, HTTP transport, redirect handling, and authentication state integration may differ from browser applications. + +The SDK's shared contracts help maintain consistency, while platform-specific integration handles the environment's requirements. + +## Working with Authentication State + +Authentication is not simply a method call that returns a token. + +The client may need to coordinate several related concerns: + +- Completing the selected authentication flow. +- Handling the resulting authentication state. +- Renewing authentication when continuation is supported. +- Reacting to logout or session invalidation. +- Updating application components when relevant state changes. + +UltimateAuth provides client-side abstractions and notifications for supported operations. + +For example, user or credential changes may publish SDK events after a successful operation. + +Applications can use the available state integration mechanisms to update their UI without treating every API response as a complete authentication state transition. + +The exact behavior depends on the client environment and configured integration. + +## Understanding SDK Results + +UltimateAuth client operations use structured response contracts. + +Applications should inspect the result of an operation rather than assuming that completing an asynchronous call means the requested action succeeded. + +A typical application workflow is: + +```text +Call SDK operation + | + v +Receive structured result + | + +-- Success + | | + | v + | Continue application workflow + | + +-- Failure + | + v + Handle error +``` + +Different operations may return different result types. + +The relevant guide for each client service explains its request and response contracts, including how to handle failures. + +## Client Events and UI Updates + +Some SDK operations can publish notifications when a successful operation changes application-relevant state. + +For example, credential or user management operations may notify subscribers that related information has changed. + +These notifications help decouple an operation from the UI components that react to it. + +A component can respond by refreshing its data rather than requiring the caller to manually update every affected view. + +However, client events are not a security authority. + +An event does not replace server-side validation, and the absence of an event does not prove that the underlying security state has remained unchanged. + +Applications should continue to treat the server as authoritative wherever the selected authentication mode requires it. + +## Calling Protected Resource APIs + +Authentication is often only the first step. + +Applications also need to call protected business APIs after the user has authenticated. + +UltimateAuth supports integration with protected resource requests, allowing the client application to use the authentication mechanisms appropriate to its configured environment. + +A protected resource request involves two distinct responsibilities: + +1. The client sends the request with the appropriate authentication material. +2. The receiving server validates authentication and enforces authorization. + +The Client SDK can help with the client-side responsibility. + +It cannot grant access to an operation that the receiving server does not authorize. + +When building applications with separate resource APIs, developers must configure the relevant API integration and server-side authentication enforcement. + +## Client SDK Guides + +The following guides cover the major client responsibilities with practical C# examples. + +| Guide | What you will learn | +|---|---| +| **Authentication** | Login, logout, refresh, PKCE, and authentication state handling | +| **Session Management** | Retrieve, inspect, and manage sessions and device-associated chains | +| **User Management** | Retrieve user information and perform supported user lifecycle operations | +| **Identifiers** | Manage email addresses, usernames, phone numbers, and verification workflows | +| **Credentials** | Add, change, revoke, and recover credentials | +| **Authorization** | Work with roles, permissions, and exposed authorization management operations | +| **Resource API Requests** | Call protected application APIs using the appropriate authentication integration | + +Each guide focuses on a specific developer task and includes the relevant SDK methods, request models, response handling, and implementation considerations. + +### Recommended Learning Path + +If you are integrating UltimateAuth for the first time, start with **Authentication**. + +Once login and authentication state handling work correctly, continue with **Resource API Requests** to connect authentication to your application's protected functionality. + +After that, use the domain-specific guides for the features your application needs. + +For example, an account settings page may require the User Management, Identifiers, and Credentials guides, while an administration panel may additionally require Authorization and Session Management. + +## Client SDK and Server Responsibilities + +The Client SDK simplifies integration, but it does not independently enforce the security of server-side operations. + +| Client responsibility | Server responsibility | +|---|---| +| Submit authentication requests | Validate authentication requirements | +| Manage supported client state | Establish and validate authoritative session state | +| Send protected resource requests | Authenticate and authorize protected operations | +| Handle structured responses | Enforce domain rules and security policies | +| React to state notifications | Apply revocation and account security controls | + +This separation is particularly important for applications that share UI components across different hosting models. + +A shared component can use the same SDK abstractions, while the hosting environment and server determine how authentication material is transported and validated. + +## When to Use the Client SDK + +Use the Client SDK when building an application that interacts with UltimateAuth's exposed authentication and management APIs. + +It is generally preferable to manually reproducing the corresponding HTTP contracts because it provides a consistent integration surface and reduces duplicated request-handling code. + +Direct endpoint integration may still be appropriate for clients that cannot use the .NET SDK or for specialized interoperability requirements. + +Regardless of the client implementation, the same server-side authentication and authorization requirements apply. + +## Next Steps + +Continue with the Client SDK guides to implement real application workflows. + +Start with **Authentication** to configure and use login, refresh, and logout operations. + +Then explore session management, user operations, credentials, authorization, and protected resource API integration according to your application's requirements. + +For the concepts behind these operations, refer to [Authentication Modes](/docs/fundamentals/auth-modes) and [Plugin Domains](/docs/plugin-domains/overview). \ No newline at end of file diff --git a/docs/content/client-sdk/session-management.md b/docs/content/client-sdk/session-management.md new file mode 100644 index 00000000..03ba446c --- /dev/null +++ b/docs/content/client-sdk/session-management.md @@ -0,0 +1,797 @@ +--- +title: Session Management +order: 3 +group: client +--- + +# Session Management + +UltimateAuth provides a dedicated client API for inspecting and managing authentication sessions across devices and application instances. + +Through `UAuthClient.Sessions`, applications can list session chains, inspect authentication history, revoke individual chains, terminate sessions across devices, and perform administrative session management. + +This guide demonstrates the available operations using the current `ISessionClient` contract. + +## 1. Understanding the Session Hierarchy + +In session-managed authentication modes, UltimateAuth organizes authentication state into three levels: + +```text +Session Root + | + +-- Session Chain A (Browser) + | | + | +-- Session A1 + | +-- Session A2 + | + +-- Session Chain B (Mobile) + | + +-- Session B1 +``` + +| Level | Responsibility | +|---|---| +| **Root** | Account-level session authority | +| **Chain** | Device-associated authentication continuity | +| **Session** | Individual authentication instance | + +These levels provide different boundaries for session management and revocation. + +A chain is associated with a device or client context, but it is not cryptographic proof of physical device identity. + +The available hierarchy and enforcement behavior depend on the authentication mode. + +For architectural details, see [Session Model & Lifecycle](/docs/sessions-devices/session-lifecycle). + +## 2. Accessing the Session Client + +Inject `IUAuthClient` into your Blazor component: + +```razor +@using CodeBeam.UltimateAuth.Client +@inject IUAuthClient UAuthClient +``` + +Session operations are available through: + +```csharp +UAuthClient.Sessions +``` + +The `Sessions` property exposes `ISessionClient`. + +The interface separates operations for the current user from administrative operations targeting another user. + +## 3. List My Devices and Session Chains + +Use `GetMyChainsAsync` to retrieve the current user's session chains. + +```csharp +var result = await UAuthClient.Sessions.GetMyChainsAsync(); + +if (result.IsSuccess && result.Value is { } page) +{ + foreach (var chain in page.Items) + { + Console.WriteLine($"{chain.Browser} - {chain.OperatingSystem}"); + } +} +``` + +The method returns: + +```csharp +UAuthResult> +``` + +The response contains a paginated collection of session chain summaries. + +### SessionChainSummary + +Each summary provides information suitable for building a device management interface. + +| Property | Type | Description | +|---|---|---| +| `ChainId` | `SessionChainId` | Unique chain identifier | +| `DeviceType` | `string?` | Device category | +| `OperatingSystem` | `string?` | Reported operating system | +| `Platform` | `string?` | Client platform | +| `Browser` | `string?` | Reported browser | +| `CreatedAt` | `DateTimeOffset` | Chain creation time | +| `LastSeenAt` | `DateTimeOffset?` | Last recorded activity | +| `RotationCount` | `int` | Recorded rotation count | +| `TouchCount` | `int` | Recorded touch count | +| `IsRevoked` | `bool` | Whether the chain is revoked | +| `RevokedAt` | `DateTimeOffset?` | Revocation timestamp | +| `ActiveSessionId` | `AuthSessionId?` | Associated active session identifier | +| `IsCurrentDevice` | `bool` | Whether this is the current device context | +| `State` | `SessionChainState` | Current chain state | + +Device metadata is descriptive information, not a trusted physical device identifier. + +### Identify the Current Device + +Use `IsCurrentDevice` to distinguish the current chain in your UI. + +```csharp +var result = await UAuthClient.Sessions.GetMyChainsAsync(); + +if (result.IsSuccess && result.Value is { } page) +{ + var current = page.Items.FirstOrDefault(x => x.IsCurrentDevice); + + if (current is not null) + { + Console.WriteLine($"Current chain: {current.ChainId}"); + } +} +``` + +This is useful when displaying a device list with a **Current device** indicator. + +The property is determined by the server's current authentication context. + +## 4. Pagination + +`GetMyChainsAsync` accepts an optional `PageRequest`. + +```csharp +var result = await UAuthClient.Sessions.GetMyChainsAsync( + new PageRequest + { + PageNumber = 1, + PageSize = 10, + SortBy = "CreatedAt", + Descending = true + }); +``` + +`PageRequest` supports: + +| Property | Default | Description | +|---|---|---| +| `PageNumber` | `1` | Requested page | +| `PageSize` | `0` | Requested page size; zero uses the server pagination default | +| `SortBy` | `null` | Optional sorting field | +| `Descending` | `false` | Descending sort order | + +The server applies its pagination policy, including default and maximum page sizes. + +Supported sorting fields depend on the server endpoint. + +### Navigating Pages + +```csharp +var pageNumber = 1; + +var result = await UAuthClient.Sessions.GetMyChainsAsync( + new PageRequest + { + PageNumber = pageNumber, + PageSize = 10 + }); + +if (result.IsSuccess && result.Value is { } page) +{ + foreach (var chain in page.Items) + { + // Render the chain. + } + + if (page.HasNext) + { + // Enable the next-page action. + } +} +``` + +`PagedResult` includes `TotalCount`, `PageNumber`, `PageSize`, and `HasNext`. + +You can use these properties directly when building pagination controls. + +## 5. Inspect a Session Chain + +Use `GetMyChainDetailAsync` to retrieve detailed information about a specific chain. + +```csharp +SessionChainId chainId = selectedChain.ChainId; + +var result = await UAuthClient.Sessions.GetMyChainDetailAsync(chainId); + +if (result.IsSuccess && result.Value is { } detail) +{ + Console.WriteLine($"State: {detail.State}"); + Console.WriteLine($"Created: {detail.CreatedAt}"); + Console.WriteLine($"Last seen: {detail.LastSeenAt}"); +} +``` + +The method returns: + +```csharp +UAuthResult +``` + +### SessionChainDetail + +The detail model includes: + +- Chain identity and device metadata. +- Creation and last-seen timestamps. +- Chain state and revocation information. +- Rotation and touch counters. +- Active session identifier. +- Session history. + +### Inspect Session History + +```csharp +var result = await UAuthClient.Sessions.GetMyChainDetailAsync(chainId); + +if (result.IsSuccess && result.Value is { } detail) +{ + foreach (var session in detail.Sessions) + { + Console.WriteLine($"Session: {session.SessionId}"); + Console.WriteLine($"Created: {session.CreatedAt}"); + Console.WriteLine($"Expires: {session.ExpiresAt}"); + Console.WriteLine($"Revoked: {session.IsRevoked}"); + } +} +``` + +Each session is represented by `SessionInfo`. + +```csharp +public sealed record SessionInfo( + AuthSessionId SessionId, + DateTimeOffset CreatedAt, + DateTimeOffset ExpiresAt, + bool IsRevoked); +``` + +This allows applications to display individual authentication instances within a device-associated chain. + +## 6. Understanding Chain States + +`SessionChainState` defines three values: + +| State | Meaning | +|---|---| +| `Active` | Active chain state | +| `Passive` | Passive chain state | +| `Revoked` | Revoked chain state | + +For example: + +```csharp +switch (chain.State) +{ + case SessionChainState.Active: + // Display as active. + break; + + case SessionChainState.Passive: + // Display as passive. + break; + + case SessionChainState.Revoked: + // Display as revoked. + break; +} +``` + +A passive chain should not automatically be presented as a revoked chain. + +Use the returned state rather than inferring revocation from the absence of an active session. + +## 7. Revoke One of My Devices + +Use `RevokeMyChainAsync` to revoke a selected session chain. + +```csharp +var result = await UAuthClient.Sessions.RevokeMyChainAsync(chainId); + +if (result.IsSuccess) +{ + // Refresh the device list. +} +``` + +The method returns: + +```csharp +UAuthResult +``` + +Revoking a chain invalidates its associated session continuity according to the server's revocation model. + +It does not physically disable the device or prevent the user from authenticating again. + +### Detect Revocation of the Current Chain + +`RevokeResult` contains: + +```csharp +public sealed record RevokeResult +{ + public bool CurrentChain { get; init; } + public bool RootRevoked { get; init; } +} +``` + +You can inspect these properties: + +```csharp +var result = await UAuthClient.Sessions.RevokeMyChainAsync(chainId); + +if (result.IsSuccess && result.Value is { } revoke) +{ + if (revoke.CurrentChain) + { + // The current authentication chain was affected. + } + + if (revoke.RootRevoked) + { + // The result reports root-level revocation. + } +} +``` + +When `RevokeMyChainAsync` succeeds and `CurrentChain` is true, the SDK publishes a `SessionRevoked` state event. + +This helps the application react when its own authentication context has been invalidated. + +## 8. Revoke All Other Devices + +Use `RevokeMyOtherChainsAsync` to revoke other session chains while retaining the current chain. + +```csharp +var result = await UAuthClient.Sessions.RevokeMyOtherChainsAsync(); + +if (result.IsSuccess) +{ + // Other device chains were revoked. +} +``` + +This is suitable for a **Log out other devices** action in account settings. + +The server determines the current chain and applies the corresponding revocation scope. + +Applications should not attempt to identify the current device by comparing browser names, operating systems, or other descriptive metadata. + +## 9. Revoke All My Devices + +Use `RevokeAllMyChainsAsync` to revoke every session chain associated with the current user. + +```csharp +var result = await UAuthClient.Sessions.RevokeAllMyChainsAsync(); + +if (result.IsSuccess) +{ + // All applicable chains were revoked. +} +``` + +This includes the current device-associated chain. + +When the operation succeeds, the SDK publishes a `SessionRevoked` event. + +A typical use case is an account security page offering **Log out everywhere**. + +### Security Considerations + +Revoking server-managed sessions prevents their continued use where the authentication mode enforces that state. + +However, previously issued JWT access tokens may remain valid until expiration if the receiving resource server validates them locally without consulting current revocation state. + +Do not assume that chain revocation automatically invalidates every access token in every authentication mode. + +## 10. Logout Versus Session Revocation + +UltimateAuth exposes authentication logout operations through `Flows` and session management operations through `Sessions`. + +Both may affect authentication state, but they serve different application responsibilities. + +| Operation | Recommended API | +|---|---| +| Normal logout | `UAuthClient.Flows.LogoutAsync()` | +| Logout a selected device | `UAuthClient.Flows.LogoutMyDeviceAsync(request)` | +| Logout other devices | `UAuthClient.Flows.LogoutMyOtherDevicesAsync()` | +| Logout all devices | `UAuthClient.Flows.LogoutAllMyDevicesAsync()` | +| Inspect session chains | `UAuthClient.Sessions.GetMyChainsAsync()` | +| Revoke a selected chain | `UAuthClient.Sessions.RevokeMyChainAsync(chainId)` | +| Revoke other chains | `UAuthClient.Sessions.RevokeMyOtherChainsAsync()` | +| Revoke all chains | `UAuthClient.Sessions.RevokeAllMyChainsAsync()` | + +### Which API Should You Use? + +For ordinary authentication actions, prefer `Flows`. + +```csharp +await UAuthClient.Flows.LogoutAsync(); +``` + +For a session management interface that lists and controls chains, use `Sessions`. + +```csharp +var result = await UAuthClient.Sessions.RevokeMyChainAsync(chainId); +``` + +The APIs expose related capabilities through different client surfaces. + +The `Flows` API emphasizes authentication workflows, while `Sessions` exposes the session management model more directly. + +### Logout a Device Through Flows + +The device logout method requires `LogoutDeviceRequest`. + +```csharp +var result = await UAuthClient.Flows.LogoutMyDeviceAsync( + new LogoutDeviceRequest + { + ChainId = chainId + }); +``` + +Pass a `SessionChainId`, not an individual `AuthSessionId`. + +For complete logout examples, see [Authentication](/docs/client/authentication). + +## 11. Build a Device Management Page + +A common application requirement is to show all devices associated with the current account and allow the user to revoke them. + +The following example uses the session client to build a simple Blazor device list. + +```razor +@using CodeBeam.UltimateAuth.Client +@using CodeBeam.UltimateAuth.Core.Contracts +@using CodeBeam.UltimateAuth.Core.Domain +@inject IUAuthClient UAuthClient + +

My Devices

+ +@if (_loading) +{ +

Loading devices...

+} +else if (_error is not null) +{ +

@_error

+} +else +{ + @foreach (var chain in _chains) + { +
+ @(chain.Browser ?? "Unknown browser") +

@(chain.OperatingSystem ?? "Unknown OS")

+

State: @chain.State

+ + @if (chain.IsCurrentDevice) + { + Current device + } + + +
+ } +} + +@code { + private IReadOnlyList _chains = []; + private bool _loading = true; + private string? _error; + + protected override async Task OnInitializedAsync() + { + await LoadAsync(); + } + + private async Task LoadAsync() + { + _loading = true; + _error = null; + + try + { + var result = await UAuthClient.Sessions.GetMyChainsAsync(new PageRequest + { + PageNumber = 1, + PageSize = 20 + }); + + if (result.IsSuccess && result.Value is { } page) + { + _chains = page.Items; + } + else + { + _error = "Unable to load devices."; + } + } + catch (Exception) + { + _error = "The session service is unavailable."; + } + finally + { + _loading = false; + } + } + + private async Task RevokeAsync(SessionChainId chainId) + { + var result = await UAuthClient.Sessions.RevokeMyChainAsync(chainId); + + if (result.IsSuccess) + { + await LoadAsync(); + } + else + { + _error = "Unable to revoke the selected device."; + } + } +} +``` + +This example demonstrates the core integration. + +A production UI should additionally provide confirmation before revocation, disable repeated submissions while an operation is in progress, and handle the possibility that the current device is being revoked. + +The example displays the first page. Use the pagination API to expose additional chains. + +## 12. Administrative Session Management + +Administrative operations allow authorized actors to inspect and manage another user's session state. + +These methods require appropriate server-side permissions. + +The presence of an administrative SDK method does not grant administrative access. + +### List a User's Devices + +```csharp +var result = await UAuthClient.Sessions.GetUserChainsAsync(userKey); + +if (result.IsSuccess && result.Value is { } page) +{ + foreach (var chain in page.Items) + { + Console.WriteLine(chain.ChainId); + } +} +``` + +The method accepts an optional `PageRequest`. + +```csharp +var result = await UAuthClient.Sessions.GetUserChainsAsync(userKey, + new PageRequest + { + PageNumber = 1, + PageSize = 20 + }); +``` + +### Inspect a User's Chain + +Use `GetUserChainDetailAsync`: + +```csharp +var result = await UAuthClient.Sessions.GetUserChainDetailAsync(userKey, chainId); + +if (result.IsSuccess && result.Value is { } detail) +{ + foreach (var session in detail.Sessions) + { + Console.WriteLine(session.SessionId); + } +} +``` + +This provides the same `SessionChainDetail` model used for self-service chain inspection. + +### Revoke an Individual Session + +Use `RevokeUserSessionAsync` when you need to revoke one authentication instance rather than its entire chain. + +```csharp +var result = await UAuthClient.Sessions.RevokeUserSessionAsync(userKey, sessionId); + +if (result.IsSuccess) +{ + // The session revocation operation succeeded. +} +``` + +The method requires an `AuthSessionId`. + +This is different from chain revocation. + +A session-level operation targets a single authentication instance; a chain-level operation targets the associated session continuity boundary. + +### Revoke a User's Device Chain + +```csharp +var result = await UAuthClient.Sessions.RevokeUserChainAsync(userKey, chainId); + +if (result.IsSuccess && result.Value is { } revoke) +{ + // Inspect revoke.CurrentChain or revoke.RootRevoked if needed. +} +``` + +The method returns `UAuthResult`. + +### Revoke All User Chains + +```csharp +var result = await UAuthClient.Sessions.RevokeAllUserChainsAsync(userKey); + +if (result.IsSuccess) +{ + // All applicable user chains were revoked. +} +``` + +This is useful for administrative account security operations. + +### Revoke the User's Session Root + +UltimateAuth also exposes root-level revocation: + +```csharp +var result = await UAuthClient.Sessions.RevokeUserRootAsync(userKey); + +if (result.IsSuccess) +{ + // Root-level revocation succeeded. +} +``` + +Root revocation is a stronger account-level security operation than revoking one session or one chain. + +It is intended for situations where the user's broader session authority must be invalidated. + +Examples include account compromise response or other security-critical administrative actions. + +Do not use root revocation as a routine substitute for ordinary logout. + +## 13. Choosing the Correct Revocation Scope + +UltimateAuth provides multiple revocation levels because different security events require different responses. + +| Scope | API | Intended effect | +|---|---|---| +| Current logout | `Flows.LogoutAsync()` | End current authentication | +| Individual session | `Sessions.RevokeUserSessionAsync()` | Revoke one authentication instance | +| One chain | `Sessions.RevokeMyChainAsync()` | Revoke a device-associated chain | +| Other chains | `Sessions.RevokeMyOtherChainsAsync()` | Retain current chain, revoke others | +| All chains | `Sessions.RevokeAllMyChainsAsync()` | Revoke all current user's chains | +| User root | `Sessions.RevokeUserRootAsync()` | Invalidate account-level session authority | + +Use the narrowest scope that satisfies the security requirement. + +For example, removing one unrecognized device from an account should not automatically require revoking the entire user's session root. + +Conversely, a confirmed account-wide compromise may justify broader revocation. + +## 14. Handling Session Operation Results + +Session client methods return `UAuthResult` or `UAuthResult`. + +A typical result-handling pattern is: + +```csharp +var result = await UAuthClient.Sessions.GetMyChainsAsync(); + +if (result.IsSuccess && result.Value is { } page) +{ + // Use the returned data. +} +else if (result.Http.IsUnauthorized) +{ + // Authentication is required. +} +else if (result.Http.IsForbidden) +{ + // The operation is not authorized. +} +else +{ + // Handle another failure. +} +``` + +Available response information includes: + +- `IsSuccess` +- `Status` +- `Problem` +- `ErrorText` +- `CorrelationId` +- `TraceId` + +The `Http` helper exposes status checks such as `IsBadRequest`, `IsUnauthorized`, `IsForbidden`, and `IsConflict`. + +### Administrative Authorization Failures + +An administrative session operation may return `403 Forbidden` when the actor lacks the required permission. + +```csharp +var result = await UAuthClient.Sessions.GetUserChainsAsync(userKey); + +if (result.Http.IsForbidden) +{ + // The current actor cannot inspect this user's sessions. +} +``` + +Do not attempt to work around authorization failures by switching to a different endpoint. + +The server is responsible for enforcing the operation's access policy. + +## 15. Session State Events + +The session client publishes state events for certain successful revocation operations. + +The current implementation publishes `SessionRevoked` when: + +- `RevokeMyChainAsync` succeeds and the result identifies the current chain. +- `RevokeAllMyChainsAsync` succeeds. + +Other session management methods do not necessarily publish the same event. + +Applications should not assume that every successful administrative revocation produces a local state event. + +State events help the client update its authentication-related UI. + +They do not replace authoritative server-side session validation. + +## 16. Security and Operational Considerations + +### Device Metadata Is Descriptive + +Browser, operating system, platform, and device type are useful for displaying session information. + +They are not reliable proof of physical device ownership. + +A malicious or modified client may be able to influence some reported metadata. + +### Session Chains Are Not Permanent Device Identities + +Revoking a chain invalidates its authentication continuity. + +It does not permanently ban the physical device. + +A later successful login may establish new authentication state. + +### Revocation Is Server-Controlled + +Client-side removal of a device card does not revoke the underlying session. + +Always use the appropriate session or logout operation and inspect its result. + +### Authentication Modes Matter + +The Root β†’ Chain β†’ Session hierarchy is relevant to server-managed session modes. + +Do not assume identical session management semantics in PureJwt or other modes that do not enforce the same hierarchy during ordinary request validation. + +### Protect Administrative Operations + +Administrative session revocation can interrupt another user's authentication. + +These operations should be protected by appropriate permissions and, where required, stronger authentication or administrative confirmation. + +## Next Steps + +- [Authentication](/docs/client/authentication) β€” Login, logout, refresh, and authentication state validation. +- [Session Model & Lifecycle](/docs/sessions-devices/session-lifecycle) β€” Session roots, chains, lifecycle, and expiration. +- [Device Management](/docs/sessions-devices/device-management) β€” Device-associated authentication contexts. +- [Session Security Model](/docs/security/session-security) β€” Revocation and session security boundaries. \ No newline at end of file diff --git a/docs/content/client-sdk/user-management.md b/docs/content/client-sdk/user-management.md new file mode 100644 index 00000000..68c785c4 --- /dev/null +++ b/docs/content/client-sdk/user-management.md @@ -0,0 +1,1212 @@ +--- +title: User Management +order: 4 +group: client +--- + +# User Management + +The UltimateAuth Client SDK provides a comprehensive API for managing users, profiles, account status, and user lifecycle operations. + +Through `UAuthClient.Users`, applications can implement account settings, profile management, user registration, and administrative user management without manually constructing API requests. + +UltimateAuth distinguishes between **user identity**, **user profiles**, and **account lifecycle state**. + +A user has a stable identity and may have multiple profiles. Account status controls lifecycle behavior independently of profile data. + +This guide covers the public `IUserClient` API, including self-service and administrative operations, structured results, pagination, multi-profile support, and important update semantics. + +For email addresses, usernames, phone numbers, and identifier verification, see [Identifiers](/docs/client/identifiers). + +## 1. Accessing the User Client + +In a Blazor component, inject `IUAuthClient`: + +```razor +@using CodeBeam.UltimateAuth.Client +@inject IUAuthClient UAuthClient +``` + +User operations are available through: + +```csharp +UAuthClient.Users +``` + +The `Users` property exposes `IUserClient`. + +Its operations are divided into three functional areas: + +| Area | Capabilities | +|---|---| +| User lifecycle | Create users, change status, delete accounts | +| Self-service profiles | Retrieve, update, create, and delete your own profiles | +| Administration | Query users and manage another user's account and profiles | + +Self-service methods operate on the authenticated user. + +Administrative methods operate on an explicitly identified target and require the appropriate server-side authorization. + +## 2. Get the Current User + +Use `GetMeAsync` to retrieve the current user's profile. + +```csharp +var result = await UAuthClient.Users.GetMeAsync(); + +if (result.IsSuccess && result.Value is { } user) +{ + Console.WriteLine(user.DisplayName); + Console.WriteLine(user.Status); + Console.WriteLine(user.UserKey); +} +``` + +The method returns: + +```csharp +UAuthResult +``` + +When no profile is specified, UltimateAuth returns the default profile. + +### UserView + +`UserView` contains account information and the selected profile's data. + +| Property | Description | +|---|---| +| `UserKey` | Stable user identifier | +| `Status` | Current account status | +| `UserName` | Username | +| `PrimaryEmail` | Primary email address | +| `PrimaryPhone` | Primary phone number | +| `ProfileKey` | Selected profile | +| `FirstName`, `LastName` | Profile name fields | +| `DisplayName` | Display name | +| `Bio` | Biography | +| `BirthDate` | Birth date | +| `Gender` | Profile gender field | +| `Language` | Preferred language | +| `TimeZone` | Preferred time zone | +| `Culture` | Culture preference | +| `EmailVerified`, `PhoneVerified` | Identifier verification information | +| `CreatedAt` | Account creation timestamp | +| `Metadata` | Custom profile metadata | + +The returned profile is a view of server-managed user information. + +It should not be treated as a substitute for authoritative authentication or authorization state. + +## 3. Update the Current User's Profile + +Use `UpdateMyProfileAsync` to update the authenticated user's profile. + +```csharp +var result = await UAuthClient.Users.UpdateMyProfileAsync( + new UpdateProfileRequest + { + DisplayName = "John Doe", + FirstName = "John", + LastName = "Doe", + Language = "en", + TimeZone = "Europe/London" + }); + +if (result.IsSuccess) +{ + // Profile update succeeded. +} +``` + +The method returns `UAuthResult`. + +### Important: Profile Updates Replace Editable Fields + +`UpdateProfileRequest` uses replacement semantics rather than partial-update semantics. + +This means: + +- Editable fields supplied in the request replace their previous values. +- Explicit `null` values clear existing values. +- Omitted editable properties are also treated as `null`. + +For example: + +```csharp +await UAuthClient.Users.UpdateMyProfileAsync( + new UpdateProfileRequest + { + DisplayName = "John Doe" + }); +``` + +This does **not** mean β€œchange only the display name.” + +Other editable profile fields may be cleared. + +### Recommended: Read, Modify, and Submit + +When building a profile editor, retrieve the current profile and preserve values that should remain unchanged. + +```csharp +var currentResult = await UAuthClient.Users.GetMeAsync(); + +if (!currentResult.IsSuccess || currentResult.Value is not { } current) +{ + return; +} + +var update = new UpdateProfileRequest +{ + FirstName = current.FirstName, + LastName = current.LastName, + DisplayName = "John Doe", + BirthDate = current.BirthDate, + Gender = current.Gender, + Bio = current.Bio, + Language = current.Language, + TimeZone = current.TimeZone, + Culture = current.Culture, + Metadata = current.Metadata +}; + +var result = await UAuthClient.Users.UpdateMyProfileAsync(update); +``` + +This preserves the values returned by the initial read, but it is not an atomic patch operation. + +If another request modifies the profile between the read and update, the later replacement may overwrite those changes. + +Applications with concurrent profile editing requirements should account for this behavior. + +### Profile State Events + +After a successful `UpdateMyProfileAsync` operation, the client publishes a `ProfileChanged` event. + +The event carries the `UpdateProfileRequest` used for the operation. + +The configured client state event handling mode determines how the application processes the notification. + +The event is published only when the SDK operation reports success. + +## 4. Understanding Multiple Profiles + +UltimateAuth supports multiple profiles for a single user when enabled through server configuration. + +For example, one account may have: + +```text +User + | + +-- default + | + +-- business + | + +-- public +``` + +All profiles belong to the same user identity. + +They do not represent separate authentication accounts or independently authenticated principals. + +Profiles allow applications to organize different sets of user-facing information under one account. + +### Default Profile + +Every newly created user receives a default profile. + +Its key is: + +```csharp +ProfileKey.Default +``` + +The default profile cannot be deleted. + +When a profile request omits `ProfileKey`, the default profile is selected. + +### Profile Keys + +`ProfileKey` is a strongly typed value. + +Use `TryCreate` to construct a custom key: + +```csharp +if (!ProfileKey.TryCreate("business", out var profileKey)) +{ + throw new InvalidOperationException("Invalid profile key."); +} +``` + +Profile keys are normalized by trimming whitespace and converting the value to lowercase. + +A valid key must not be empty or exceed 64 characters. + +## 5. Retrieve a Specific Profile + +Use `GetProfileRequest` to select a profile. + +```csharp +if (!ProfileKey.TryCreate("business", out var profileKey)) +{ + return; +} + +var result = await UAuthClient.Users.GetMeAsync( + new GetProfileRequest + { + ProfileKey = profileKey + }); + +if (result.IsSuccess && result.Value is { } profile) +{ + Console.WriteLine(profile.DisplayName); + Console.WriteLine(profile.ProfileKey); +} +``` + +The response is still `UserView`. + +The account identity and status remain associated with the same user; the selected profile determines which profile information is returned. + +## 6. Create an Additional Profile + +Use `CreateMyProfileAsync` to create a new profile for the authenticated user. + +```csharp +if (!ProfileKey.TryCreate("business", out var profileKey)) +{ + return; +} + +var result = await UAuthClient.Users.CreateMyProfileAsync( + new CreateProfileRequest + { + ProfileKey = profileKey, + DisplayName = "John Doe Consulting", + Bio = "Business profile", + Language = "en" + }); + +if (result.IsSuccess) +{ + // Profile created. +} +``` + +`CreateProfileRequest` requires `ProfileKey`. + +It also supports the same general profile information used for user-facing profiles. + +### Clone an Existing Profile + +A new profile can be created using an existing profile as its source. + +```csharp +ProfileKey.TryCreate("business", out var businessKey); + +var result = await UAuthClient.Users.CreateMyProfileAsync( + new CreateProfileRequest + { + ProfileKey = businessKey, + CloneFrom = ProfileKey.Default + }); +``` + +`CloneFrom` identifies the source profile. + +This is useful when creating a new profile that should initially reuse information from an existing one. + +The exact merge and override behavior is determined by the server's profile creation implementation. + +### Profile Creation Events + +After successful self-service profile creation, the SDK publishes `ProfileChanged`. + +This allows application components to refresh profile-related views. + +## 7. Update a Specific Profile + +Set `ProfileKey` in `UpdateProfileRequest` to select the profile being updated. + +```csharp +ProfileKey.TryCreate("business", out var businessKey); + +var result = await UAuthClient.Users.UpdateMyProfileAsync( + new UpdateProfileRequest + { + ProfileKey = businessKey, + DisplayName = "John Doe Consulting", + Bio = "Updated business profile" + }); +``` + +The same replacement semantics apply to custom profiles. + +If the application intends to preserve existing fields, retrieve the selected profile first and include the values that should remain unchanged. + +## 8. Delete a Profile + +Use `DeleteMyProfileAsync` to remove a custom profile. + +```csharp +ProfileKey.TryCreate("business", out var businessKey); + +var result = await UAuthClient.Users.DeleteMyProfileAsync( + new DeleteProfileRequest + { + ProfileKey = businessKey + }); + +if (result.IsSuccess) +{ + // Profile deleted. +} +``` + +The default profile cannot be deleted. + +Deleting a profile is different from deleting the user account. + +The user identity and other profiles remain subject to their own lifecycle and server-side state. + +On successful self-service profile deletion, the SDK publishes `ProfileChanged` with the deleted profile key. + +## 9. Create a User + +Use `CreateAsync` to request user creation through the standard user creation endpoint. + +```csharp +var result = await UAuthClient.Users.CreateAsync( + new CreateUserRequest + { + UserName = "johndoe", + Email = "john@example.com", + Password = password, + FirstName = "John", + LastName = "Doe", + DisplayName = "John Doe" + }); + +if (result.IsSuccess && result.Value is { IsSuccess: true } created) +{ + Console.WriteLine($"Created user: {created.UserKey}"); +} +``` + +The method returns: + +```csharp +UAuthResult +``` + +User creation availability depends on the server's configuration and access policies. + +### CreateUserRequest + +The request supports several groups of fields: + +| Category | Properties | +|---|---| +| Identifiers | `UserName`, `Email`, `Phone` | +| Verification flags | `UserNameVerified`, `EmailVerified`, `PhoneVerified` | +| Credentials | `Password` | +| Profile | `FirstName`, `LastName`, `DisplayName`, `BirthDate`, `Gender`, `Bio` | +| Preferences | `Language`, `TimeZone`, `Culture` | +| Extensions | `Metadata` | + +Verification flags must not be treated as client-controlled proof of identifier ownership. + +The server is responsible for enforcing verification requirements and determining whether a caller is authorized to establish verified identifier state. + +For identifier-specific operations and verification workflows, see [Identifiers](/docs/client/identifiers). + +### UserCreateResult + +The returned value includes: + +```csharp +public sealed record UserCreateResult +{ + public required bool IsSuccess { get; init; } + public string? UserKey { get; init; } + public string? FailureReason { get; init; } +} +``` + +The `UserKey` is returned as a string when creation succeeds. + +Check both the transport-level `UAuthResult.IsSuccess` and the operation-level `UserCreateResult.IsSuccess` where applicable. + +## 10. Administrative User Creation + +Use `CreateAsAdminAsync` when creating a user through an administrative context. + +```csharp +var result = await UAuthClient.Users.CreateAsAdminAsync( + new CreateUserRequest + { + UserName = "newemployee", + Email = "employee@example.com", + DisplayName = "New Employee" + }); + +if (result.IsSuccess && result.Value is { IsSuccess: true } created) +{ + Console.WriteLine(created.UserKey); +} +``` + +Administrative creation is distinct from ordinary user creation. + +The server enforces the required permissions and applies the relevant user creation policies. + +Applications should not assume that calling the administrative endpoint bypasses validation or identifier ownership rules. + +## 11. Query Users + +Use `QueryAsync` to search and paginate users. + +```csharp +var result = await UAuthClient.Users.QueryAsync( + new UserQuery + { + Search = "john", + PageNumber = 1, + PageSize = 20 + }); + +if (result.IsSuccess && result.Value is { } page) +{ + foreach (var user in page.Items) + { + Console.WriteLine(user.DisplayName); + Console.WriteLine(user.Status); + } +} +``` + +The method returns: + +```csharp +UAuthResult> +``` + +### UserQuery Filters + +`UserQuery` extends `PageRequest` and supports: + +| Property | Description | +|---|---| +| `Search` | Search term | +| `Status` | Filter by user status | +| `IncludeDeleted` | Include deleted users when authorized | +| `ProfileKey` | Select the profile context for the query | +| `PageNumber` | Page number | +| `PageSize` | Page size | +| `SortBy` | Optional sort field | +| `Descending` | Descending sort order | + +### Search Behavior + +The query model documents case-insensitive partial matching for: + +- Display name. +- Username. +- Primary email. +- Primary phone. + +A complete valid `UserKey` can also be used as a search term. + +Partial `UserKey` matching is not supported. + +### Filter by Status + +```csharp +var result = await UAuthClient.Users.QueryAsync( + new UserQuery + { + Status = UserStatus.Active, + PageNumber = 1, + PageSize = 20 + }); +``` + +### Include Deleted Users + +```csharp +var result = await UAuthClient.Users.QueryAsync( + new UserQuery + { + IncludeDeleted = true, + PageNumber = 1, + PageSize = 20 + }); +``` + +The ability to query deleted accounts depends on server-side authorization and data retention behavior. + +## 12. UserSummary and Pagination + +`QueryAsync` returns `UserSummary` items. + +Each summary contains: + +| Property | Description | +|---|---| +| `UserKey` | User identifier | +| `UserName` | Username | +| `DisplayName` | Display name | +| `PrimaryEmail` | Primary email | +| `PrimaryPhone` | Primary phone | +| `Status` | Account status | +| `CreatedAt` | Creation timestamp | + +This model is suitable for administrative user lists. + +### Navigating Pages + +```csharp +var result = await UAuthClient.Users.QueryAsync( + new UserQuery + { + PageNumber = 2, + PageSize = 25 + }); + +if (result.IsSuccess && result.Value is { } page) +{ + Console.WriteLine($"Total users: {page.TotalCount}"); + Console.WriteLine($"Current page: {page.PageNumber}"); + Console.WriteLine($"Has next page: {page.HasNext}"); +} +``` + +Use `PagedResult` rather than assuming every query returns the complete user collection. + +## 13. Get a Specific User + +Use `GetUserAsync` to retrieve a user's profile through the administrative API. + +```csharp +var result = await UAuthClient.Users.GetUserAsync(userKey); + +if (result.IsSuccess && result.Value is { } user) +{ + Console.WriteLine(user.DisplayName); + Console.WriteLine(user.Status); +} +``` + +The method returns `UAuthResult`. + +When no profile is specified, the default profile is selected. + +### Retrieve a Specific User Profile + +```csharp +ProfileKey.TryCreate("business", out var businessKey); + +var result = await UAuthClient.Users.GetUserAsync( + userKey, + new GetProfileRequest + { + ProfileKey = businessKey + }); +``` + +The actor must have permission to access the target user's requested information. + +## 14. Update Another User's Profile + +Use `UpdateUserProfileAsync` to update a selected user's profile. + +```csharp +var result = await UAuthClient.Users.UpdateUserProfileAsync( + userKey, + new UpdateProfileRequest + { + DisplayName = "Updated Display Name", + FirstName = "John", + LastName = "Doe" + }); + +if (result.IsSuccess) +{ + // Profile updated. +} +``` + +This operation uses the same replacement semantics as `UpdateMyProfileAsync`. + +Omitted editable fields may be cleared. + +For partial UI editing, retrieve the target profile and preserve values that should not change. + +### Update a Named Profile + +```csharp +ProfileKey.TryCreate("business", out var businessKey); + +var result = await UAuthClient.Users.UpdateUserProfileAsync(userKey, + new UpdateProfileRequest + { + ProfileKey = businessKey, + DisplayName = "Updated Business Profile" + }); +``` + +Administrative profile updates require the appropriate authorization for the target user. + +## 15. Manage Another User's Profiles + +Administrators can create and delete profiles for a selected user. + +### Create a Profile + +```csharp +ProfileKey.TryCreate("business", out var businessKey); + +var result = await UAuthClient.Users.CreateUserProfileAsync(userKey, + new CreateProfileRequest + { + ProfileKey = businessKey, + DisplayName = "Business Profile" + }); +``` + +### Delete a Profile + +```csharp +ProfileKey.TryCreate("business", out var businessKey); + +var result = await UAuthClient.Users.DeleteUserProfileAsync(userKey, + new DeleteProfileRequest + { + ProfileKey = businessKey + }); +``` + +The default profile cannot be deleted. + +Administrative profile operations do not automatically publish the same local `ProfileChanged` event as self-service profile operations. + +Refresh affected administrative UI data explicitly when needed. + +## 16. Understanding User Status + +UltimateAuth separates account lifecycle status from profile information. + +A user may be active, suspended, disabled, locked, awaiting activation, or in another supported lifecycle state. + +Status can affect authentication eligibility, session continuation, and authorization. + +However, the exact enforcement behavior depends on the configured server policies and authentication mode. + +### Self-Service and Administrative Statuses + +UltimateAuth exposes separate status enums for self-service and administrative operations. + +This prevents the client contract from treating every administrative lifecycle transition as a self-service capability. + +**SelfAssignableUserStatus** + +```csharp +public enum SelfAssignableUserStatus +{ + Active = 0, + SelfSuspended = 10 +} +``` + +**AdminAssignableUserStatus** + +```csharp +public enum AdminAssignableUserStatus +{ + Active = 0, + Disabled = 20, + Suspended = 30, + Locked = 40, + RiskHold = 50, + PendingActivation = 60, + PendingVerification = 70, + Unknown = 100 +} +``` + +These enums describe requestable statuses. + +They do not mean every transition is permitted in every application context. + +The server validates the requested transition. + +## 17. Change Your Own Status + +Use `ChangeMyStatusAsync` for supported self-service status transitions. + +```csharp +var result = await UAuthClient.Users.ChangeMyStatusAsync( + new ChangeUserStatusSelfRequest + { + NewStatus = SelfAssignableUserStatus.SelfSuspended + }); + +if (result.IsSuccess && result.Value is { IsSuccess: true } change) +{ + Console.WriteLine($"New status: {change.CurrentStatus}"); +} +``` + +This operation can be used to implement account self-suspension. + +The server determines whether the transition is permitted. + +### UserStatusChangeResult + +The result model includes: + +| Property | Description | +|---|---| +| `IsSuccess` | Whether the status operation succeeded | +| `PreviousStatus` | Status before the operation | +| `CurrentStatus` | Status after the operation | +| `FailureReason` | Optional failure description | + +For example: + +```csharp +if (result.IsSuccess && result.Value is { } change) +{ + if (change.IsSuccess) + { + Console.WriteLine($"{change.PreviousStatus} -> {change.CurrentStatus}"); + } + else + { + Console.WriteLine(change.FailureReason ?? "Status change failed."); + } +} +``` + +The result may also report a successful no-change outcome when the requested state already matches the current state. + +### State Event + +After a successful SDK operation, `ChangeMyStatusAsync` publishes `ProfileChanged`. + +This is the current client event behavior, even though the operation changes account status rather than editable profile fields. + +Applications should not assume that a status change always produces a dedicated `AuthorizationChanged` event. + +## 18. Change Another User's Status + +Use `ChangeUserStatusAsync` for administrative lifecycle management. + +```csharp +var result = await UAuthClient.Users.ChangeUserStatusAsync(userKey, + new ChangeUserStatusAdminRequest + { + NewStatus = AdminAssignableUserStatus.Suspended + }); + +if (result.IsSuccess && result.Value is { IsSuccess: true } change) +{ + Console.WriteLine($"Current status: {change.CurrentStatus}"); +} +``` + +This operation requires administrative authorization. + +### Example: Disable an Account + +```csharp +var result = await UAuthClient.Users.ChangeUserStatusAsync(userKey, + new ChangeUserStatusAdminRequest + { + NewStatus = AdminAssignableUserStatus.Disabled + }); +``` + +### Example: Restore an Active Status + +```csharp +var result = await UAuthClient.Users.ChangeUserStatusAsync(userKey, + new ChangeUserStatusAdminRequest + { + NewStatus = AdminAssignableUserStatus.Active + }); +``` + +Whether a particular transition is permitted depends on the server's lifecycle rules. + +Do not assume that setting `Active` automatically restores every form of previously revoked authentication state. + +## 19. Delete the Current User + +Use `DeleteMeAsync` to request deletion of the current account. + +```csharp +var result = await UAuthClient.Users.DeleteMeAsync(); + +if (result.IsSuccess) +{ + // Account deletion succeeded. +} +``` + +Self-service deletion is a soft-delete operation. + +According to the `IUserClient` contract, this operation removes associated profiles and sessions. + +After successful deletion, the client publishes a `UserDeleted` state event using patch handling mode. + +### Important Considerations + +Account deletion is not equivalent to ordinary logout. + +It changes the user's lifecycle state and may affect associated data across multiple domains. + +Before exposing a delete-account action, applications should provide an appropriate confirmation flow and explain the consequences. + +The server remains responsible for authorization, data cleanup, and deletion policy enforcement. + +## 20. Administrative User Deletion + +Administrators can delete users through `DeleteUserAsync`. + +The method accepts a `DeleteUserRequest` specifying the deletion mode. + +### Soft Delete + +```csharp +var result = await UAuthClient.Users.DeleteUserAsync(userKey, + new DeleteUserRequest + { + Mode = DeleteMode.Soft + }); + +if (result.IsSuccess && result.Value is { IsSuccess: true } deleted) +{ + Console.WriteLine($"Deletion mode: {deleted.Mode}"); +} +``` + +### Hard Delete + +```csharp +var result = await UAuthClient.Users.DeleteUserAsync(userKey, + new DeleteUserRequest + { + Mode = DeleteMode.Hard + }); +``` + +Hard deletion is a more destructive operation and should be restricted to appropriately authorized administrative workflows. + +### DeleteMode + +```csharp +public enum DeleteMode +{ + Soft = 0, + Hard = 10 +} +``` + +### UserDeleteResult + +The operation returns `UAuthResult`. + +`UserDeleteResult` includes: + +- `IsSuccess` +- `Mode` +- `FailureReason` + +For example: + +```csharp +if (!result.IsSuccess) +{ + // Handle transport or API-level failure. +} +else if (result.Value is { IsSuccess: false } deletion) +{ + // Handle the operation-level failure. + Console.WriteLine(deletion.FailureReason); +} +``` + +A previously deleted account may produce an operation-level success result indicating that no additional deletion was necessary. + +Do not infer the complete persistence or retention behavior from the `DeleteMode` value alone. + +## 21. Build a User Administration Table + +The following example demonstrates a basic Blazor user list using `QueryAsync`. + +```razor +@using CodeBeam.UltimateAuth.Client +@using CodeBeam.UltimateAuth.Core.Contracts +@using CodeBeam.UltimateAuth.Users.Contracts +@inject IUAuthClient UAuthClient + +

Users

+ + + + +@if (_loading) +{ +

Loading users...

+} +else if (_error is not null) +{ +

@_error

+} +else +{ + + + + + + + + + + @foreach (var user in _users) + { + + + + + + } + +
NameUsernameStatus
@user.DisplayName@user.UserName@user.Status
+ + + +} + +@code { + private string _search = ""; + private int _pageNumber = 1; + private bool _hasNext; + private bool _loading; + private string? _error; + private IReadOnlyList _users = []; + + protected override async Task OnInitializedAsync() + { + await LoadAsync(); + } + + private async Task SearchAsync() + { + _pageNumber = 1; + await LoadAsync(); + } + + private async Task PreviousPageAsync() + { + if (_pageNumber <= 1) + return; + + _pageNumber--; + await LoadAsync(); + } + + private async Task NextPageAsync() + { + if (!_hasNext) + return; + + _pageNumber++; + await LoadAsync(); + } + + private async Task LoadAsync() + { + _loading = true; + _error = null; + + try + { + var result = await UAuthClient.Users.QueryAsync( + new UserQuery + { + Search = _search, + PageNumber = _pageNumber, + PageSize = 20 + }); + + if (result.IsSuccess && result.Value is { } page) + { + _users = page.Items; + _hasNext = page.HasNext; + } + else + { + _error = "Unable to load users."; + _users = []; + _hasNext = false; + } + } + catch (Exception) + { + _error = "The user service is unavailable."; + _users = []; + _hasNext = false; + } + finally + { + _loading = false; + } + } +} +``` + +This example demonstrates searching and pagination. + +A production administration interface should additionally include loading indicators, action confirmation, permission-aware UI controls, and appropriate handling for concurrent changes. + +All administrative operations must remain protected by server-side authorization. + +## 22. Handling User Operation Results + +UltimateAuth uses structured result contracts for user operations. + +Some methods return: + +```csharp +UAuthResult +``` + +Others return: + +```csharp +UAuthResult +``` + +For operations such as user creation, status changes, and deletion, the returned value also contains an operation-level `IsSuccess` property. + +### Transport-Level Versus Operation-Level Success + +```csharp +var result = await UAuthClient.Users.CreateAsync(request); + +if (!result.IsSuccess) +{ + // API-level failure. +} +else if (result.Value is { IsSuccess: false } operation) +{ + // User creation was processed but did not succeed. + Console.WriteLine(operation.FailureReason); +} +else if (result.Value is { IsSuccess: true } created) +{ + Console.WriteLine(created.UserKey); +} +``` + +Check the appropriate success level for the operation. + +### HTTP Status Helpers + +`UAuthResult` exposes convenient status helpers. + +```csharp +if (result.Http.IsUnauthorized) +{ + // Authentication required. +} +else if (result.Http.IsForbidden) +{ + // Insufficient permission. +} +else if (result.Http.IsConflict) +{ + // Conflicting operation or state. +} +else if (result.Http.IsBadRequest) +{ + // Invalid request. +} +``` + +Additional diagnostic fields include `CorrelationId`, `TraceId`, `Problem`, and `ErrorText`. + +Avoid exposing sensitive internal failure details directly to end users. + +## 23. Client State Events + +`UAuthUserClient` publishes state events for successful self-service operations. + +| Operation | Event | +|---|---| +| `UpdateMyProfileAsync` | `ProfileChanged` | +| `CreateMyProfileAsync` | `ProfileChanged` | +| `DeleteMyProfileAsync` | `ProfileChanged` | +| `ChangeMyStatusAsync` | `ProfileChanged` | +| `DeleteMeAsync` | `UserDeleted` | + +The event payload depends on the operation. + +Administrative user operations do not automatically publish these same local self-service events. + +For example, after updating another user's profile, an administration panel should refresh the affected user data as needed. + +Client events help synchronize application state, but they do not replace authoritative server-side account status or permission checks. + +## 24. Security and Lifecycle Considerations + +### Self-Service and Administrative Boundaries + +Self-service methods operate on the current authenticated user. + +Administrative methods identify an explicit target user. + +Do not assume that access to the SDK automatically authorizes an operation. + +The server must enforce actor, target, tenant, and action requirements. + +### Status Changes and Existing Sessions + +Changing account status may affect authentication eligibility and session continuation. + +However, immediate invalidation of existing credentials depends on the server's security policy and authentication mode. + +A client-side status update is not itself proof that every previously issued token has been invalidated. + +### Profile Data Is Not Authorization Data + +A user's display name, biography, or profile metadata should not be used as proof of permissions. + +Authorization decisions must use the relevant trusted security context. + +### Profile Replacement Can Cause Data Loss + +`UpdateProfileRequest` is not a patch contract. + +Submitting an incomplete request may clear existing profile fields. + +Applications should preserve intended values and consider concurrency when multiple editors can modify the same profile. + +### Deletion Is a Lifecycle Operation + +Soft deletion and hard deletion have different implications. + +Applications should consider retention, recovery, audit requirements, and dependent data before exposing destructive actions. + +## Next Steps + +- [Identifiers](/docs/client/identifiers) β€” Manage usernames, email addresses, phone numbers, and verification. +- [Authentication](/docs/client/authentication) β€” Login, refresh, logout, and authentication state. +- [Session Management](/docs/client/session-management) β€” Inspect and revoke sessions across devices. +- [Users](/docs/plugin-domains/users) β€” Understand the user domain and lifecycle architecture. +- [Policies & Access Control](/docs/plugin-domains/policies) β€” Understand authorization enforcement for user operations. \ No newline at end of file diff --git a/docs/content/client/authentication.md b/docs/content/client/authentication.md deleted file mode 100644 index 53630cdf..00000000 --- a/docs/content/client/authentication.md +++ /dev/null @@ -1,187 +0,0 @@ ---- -title: Authentication -order: 2 -group: client ---- - - -# πŸ” Authentication Guide - -This section explains how to use the UltimateAuth client for authentication flows. - -## 🧠 Overview - -Authentication in UltimateAuth is **flow-based**, not endpoint-based. - -You interact with: - -πŸ‘‰ `FlowClient` - ---- - -## πŸ”‘ Login - -### Basic Login - -```csharp -await UAuthClient.Flows.LoginAsync(new LoginRequest -{ - Identifier = "user@ultimateauth.com", - Secret = "password" -}); -``` - -πŸ‘‰ This triggers a full login flow: - -- Sends credentials -- Handles redirect -- Establishes session - ---- - -## ⚑ Try Login (Pre-check) - -```csharp -var result = await UAuthClient.Flows.TryLoginAsync( - new LoginRequest - { - Identifier = "user@mail.com", - Secret = "password" - }, - UAuthSubmitMode.TryOnly -); -``` - -### Modes - -| Mode | Behavior | -|--------------|--------------------------------| -| TryOnly | Validate only | -| DirectCommit | Skip validation | -| TryAndCommit | Validate then login if success | - -πŸ‘‰ Use `DirectCommit` when: -- You need maximum performance while sacrificing interactive SPA capabilities. - -πŸ‘‰ Use `TryOnly` when: - -- You need validation feedback -- You want custom UI flows - -πŸ‘‰ Use `TryAndCommit` when: - -- You need completely interactive SPA experience. - -πŸ‘‰ `TryAndCommit` is the recommended mode for most applications. - -It provides: - -- Validation feedback -- Automatic redirect on success -- Smooth SPA experience - -
- -## πŸ”„ Refresh - -```csharp -var result = await UAuthClient.Flows.RefreshAsync(); -``` - -### Possible Outcomes - -- Success β†’ new tokens/session updated -- Touched β†’ session extended -- Rotated β†’ refresh token rotated -- NoOp β†’ nothing changed -- ReauthRequired β†’ login required - -πŸ‘‰ Refresh behavior depends on auth mode: - -- PureOpaque β†’ session touch -- Hybrid/JWT β†’ token rotation - -In default, successful refresh returns success outcome. If you want to learn success detail such as no-op, touched or rotated, open it via server options: - -```csharp -builder.Services.AddUltimateAuthServer(o => -{ - o.Diagnostics.EnableRefreshDetails = true; -}); -``` - -
- -## πŸšͺ Logout - -```csharp -await UAuthClient.Flows.LogoutAsync(); -``` - -πŸ‘‰ This: - -- Ends current session -- Clears authentication state - -
- -## πŸ“± Device Logout Variants - -```csharp -await UAuthClient.Flows.LogoutMyDeviceAsync(sessionId); -await UAuthClient.Flows.LogoutMyOtherDevicesAsync(); -await UAuthClient.Flows.LogoutAllMyDevicesAsync(); -``` - -πŸ‘‰ These operate on **session chains (devices)** - -
- -## πŸ” PKCE Flow (Public Clients) - -### Start PKCE - -```csharp -await UAuthClient.Flows.BeginPkceAsync(); -``` - -### Complete PKCE - -```csharp -await UAuthClient.Flows.CompletePkceLoginAsync(request); -``` - -> Complete PKCE also has try semantics the same as login flow. UltimateAuth suggests to use `TryCompletePkceLoginAsync` for complete interactive experience. - -πŸ‘‰ Required for: - -- SPA -- Blazor WASM -- Mobile apps - ---- - -## 🚨 Security Note - -- Public clients MUST use PKCE -- Server clients MAY allow direct login - -Direct credential posting disabled by default and throws exception when you directly call login. You can enable it via options. You should only use it for debugging and development purposes. -```csharp -builder.Services.AddUltimateAuthClientBlazor(o => -{ - o.Login.AllowCredentialPost = true; -}); ---- - -## 🎯 Summary - -Authentication in UltimateAuth: - -- is flow-driven -- adapts to client type -- enforces security by design - ---- - -πŸ‘‰ Always use `FlowClient` for authentication operations diff --git a/docs/content/client/authorization.md b/docs/content/client/authorization.md deleted file mode 100644 index b3c4a880..00000000 --- a/docs/content/client/authorization.md +++ /dev/null @@ -1,136 +0,0 @@ ---- -title: Authorization -order: 7 -group: client ---- - - -# πŸ›‘ Authorization Guide - -This section explains how to manage roles, permissions, and access control using the UltimateAuth client. - -## 🧠 Overview - -Authorization in UltimateAuth is policy-driven and role-based. - -On the client, you interact with: -``` -UAuthClient.Authorization -``` - -
- -## πŸ”‘ Core Concepts -Roles -- Named groups of permissions -- Assigned to users - -Permissions -- Fine-grained access definitions -- Example: users.create.anonymous, users.delete.self, authorization.roles.admin - -Policies -- Runtime decision rules -- Enforced automatically on server - -
- -### πŸ“‹ Query Roles -var result = await UAuthClient.Authorization.QueryRolesAsync(new RoleQuery -{ - PageNumber = 1, - PageSize = 10 -}); - -### βž• Create Role -await UAuthClient.Authorization.CreateRoleAsync(new CreateRoleRequest -{ - Name = "Manager" -}); - -### ✏️ Rename Role -await UAuthClient.Authorization.RenameRoleAsync(new RenameRoleRequest -{ - Id = roleId, - Name = "NewName" -}); - -### 🧩 Set Permissions -await UAuthClient.Authorization.SetRolePermissionsAsync(new SetRolePermissionsRequest -{ - RoleId = roleId, - Permissions = new[] - { - Permission.From("users.read"), - Permission.From("users.update") - } -}); - -πŸ‘‰ Permissions support: - -- Exact match β†’ users.create -- Prefix β†’ users.* -- Wildcard β†’ * - -### ❌ Delete Role -await UAuthClient.Authorization.DeleteRoleAsync(new DeleteRoleRequest -{ - Id = roleId -}); - -πŸ‘‰ Automatically removes role assignments from users - -### πŸ‘€ Assign Role to User -await UAuthClient.Authorization.AssignRoleToUserAsync(new AssignRoleRequest -{ - UserKey = userKey, - RoleName = "Manager" -}); - -### βž– Remove Role -await UAuthClient.Authorization.RemoveRoleFromUserAsync(new RemoveRoleRequest -{ - UserKey = userKey, - RoleName = "Manager" -}); - -### πŸ“‹ Get User Roles -var roles = await UAuthClient.Authorization.GetUserRolesAsync(userKey); -πŸ” Check Authorization -var result = await UAuthClient.Authorization.CheckAsync(new AuthorizationCheckRequest -{ - Action = "users.delete" -}); - -πŸ‘‰ Returns: - -- Allow / Deny -- Reason (if denied) - -
- -## 🧠 Permission Model - -Permissions are normalized and optimized: - -- Full access β†’ * -- Group access β†’ users.* -- Specific β†’ users.create - -πŸ‘‰ Internally compiled for fast evaluation - -
- -## πŸ” Security Notes -- Authorization is enforced server-side -- Client only requests actions -- Policies may override permissions -- Cross-tenant access is denied by default - -## 🎯 Summary - -Authorization in UltimateAuth: - -- combines roles + permissions + policies -- is evaluated through a decision pipeline -- supports fine-grained and scalable access control diff --git a/docs/content/client/credentials.md b/docs/content/client/credentials.md deleted file mode 100644 index 6ce81fb6..00000000 --- a/docs/content/client/credentials.md +++ /dev/null @@ -1,174 +0,0 @@ ---- -title: Credentials -order: 6 -group: client ---- - - -# πŸ”‘ Credential Management Guide - -This section explains how to manage user credentials (such as passwords) using the UltimateAuth client. - -## 🧠 Overview - -Credential operations are handled via: - -```csharp -UAuthClient.Credentials... -``` - -This includes: - -- password management -- credential reset flows -- admin credential operations - -
- -## πŸ” Change Password (Self) - -```csharp -await UAuthClient.Credentials.ChangeMyAsync(new ChangeCredentialRequest -{ - CurrentSecret = "old-password", - NewSecret = "new-password" -}); -``` - -πŸ‘‰ Requires current password -πŸ‘‰ Triggers `CredentialsChangedSelf` event - -## πŸ” Reset Password (Self) - -### Begin Reset - -```csharp -var begin = await UAuthClient.Credentials.BeginResetMyAsync( - new BeginResetCredentialRequest - { - Identifier = "user@mail.com" - }); -``` - -### Complete Reset - -```csharp -await UAuthClient.Credentials.CompleteResetMyAsync( - new CompleteResetCredentialRequest - { - Token = "reset-token", - NewSecret = "new-password" - }); -``` - -πŸ‘‰ Typically used in: - -- forgot password flows -- email-based reset flows - -## βž• Add Credential (Self) - -```csharp -await UAuthClient.Credentials.AddMyAsync(new AddCredentialRequest -{ - Secret = "password" -}); -``` - -## ❌ Revoke Credential (Self) - -```csharp -await UAuthClient.Credentials.RevokeMyAsync(new RevokeCredentialRequest -{ - CredentialId = credentialId -}); -``` - -πŸ‘‰ Useful for: - -- removing login methods -- invalidating compromised credentials - -
- -## πŸ‘‘ Admin: Change User Credential - -```csharp -await UAuthClient.Credentials.ChangeUserAsync(userKey, new ChangeCredentialRequest -{ - NewSecret = "new-password" -}); -``` - -πŸ‘‰ Does NOT require current password - -## πŸ‘‘ Admin: Add Credential - -```csharp -await UAuthClient.Credentials.AddUserAsync(userKey, new AddCredentialRequest -{ - Secret = "password" -}); -``` - -## πŸ‘‘ Admin: Revoke Credential - -```csharp -await UAuthClient.Credentials.RevokeUserAsync(userKey, new RevokeCredentialRequest -{ - CredentialId = credentialId -}); -``` - -## πŸ‘‘ Admin: Reset Credential - -### Begin - -```csharp -await UAuthClient.Credentials.BeginResetUserAsync(userKey, request); -``` - -### Complete - -```csharp -await UAuthClient.Credentials.CompleteResetUserAsync(userKey, request); -``` - -## ❌ Delete Credential (Admin) - -```csharp -await UAuthClient.Credentials.DeleteUserAsync(userKey, new DeleteCredentialRequest -{ - CredentialId = credentialId -}); -``` - -## 🧠 Credential Model - -Credentials are: - -- user-bound -- security-version aware -- lifecycle-managed - -πŸ‘‰ Changing credentials may: - -- invalidate sessions -- trigger security updates - -
- -## πŸ” Security Notes - -- Passwords are never sent back to client -- All secrets are hashed server-side -- Reset flows should be protected (email, OTP, etc.) -- Admin operations are policy-protected - -## 🎯 Summary - -Credential management in UltimateAuth: - -- supports self-service and admin flows -- integrates with security lifecycle -- enables safe credential rotation diff --git a/docs/content/client/identifiers.md b/docs/content/client/identifiers.md deleted file mode 100644 index ead1b971..00000000 --- a/docs/content/client/identifiers.md +++ /dev/null @@ -1,185 +0,0 @@ ---- -title: Identifiers -order: 5 -group: client ---- - - -# πŸ†” User Identifiers Guide - -This section explains how UltimateAuth manages user identifiers such as email, username, and phone. - -## 🧠 Overview - -In UltimateAuth, identifiers are **first-class entities**. - -They are NOT just fields on the user. - -πŸ‘‰ On the client, you interact with: - -```csharp -UAuthClient.Identifiers... -``` - -
- -## πŸ”‘ What is an Identifier? - -An identifier represents a way to identify a user: - -- Email -- Username -- Phone number -- Custom identifiers - -πŸ‘‰ Each identifier has: - -- Value (e.g. user@ultimateauth.com) -- Type (email, username, etc.) -- Verification state -- Primary flag - -
- -## ⭐ Primary Identifier - -A user can have multiple identifiers, but only one can be **primary**. Setting an identifier to primary automatically unset the current primary identifier if exists. - -```csharp -await UAuthClient.Identifiers.SetMyPrimaryAsync(new SetPrimaryUserIdentifierRequest -{ - Id = identifierId -}); -``` - -πŸ‘‰ Primary identifier is typically: - -- Used for display -- Preferred for communication - - -## πŸ” Login Identifiers - -Not all identifiers are used for login. - -πŸ‘‰ **Login identifiers are a subset of identifiers** - -πŸ‘‰ This is configurable: - -- Enable/disable per type -- Custom logic can be applied - -
- -## πŸ“‹ Get My Identifiers - -```csharp -var result = await UAuthClient.Identifiers.GetMyAsync(); -``` - - -## βž• Add Identifier - -```csharp -await UAuthClient.Identifiers.AddMyAsync(new AddUserIdentifierRequest -{ - Identifier = "new@ultimateauth.com", - Type = UserIdentifierType.Email, - IsPrimary = true -}); -``` - -
- -## ✏️ Update Identifier - -```csharp -UpdateUserIdentifierRequest updateRequest = new() -{ - Id = item.Id, - NewValue = item.Value -}; - -await UAuthClient.Identifiers.UpdateMyAsync(updateRequest); -``` - - -## βœ… Verify Identifier - -```csharp -await UAuthClient.Identifiers.VerifyMyAsync(new VerifyUserIdentifierRequest -{ - Id = identifierId -}); -``` - - -## ❌ Delete Identifier - -```csharp -await UAuthClient.Identifiers.DeleteMyAsync(new DeleteUserIdentifierRequest -{ - Id = identifierId -}); -``` - -
- -## πŸ‘€ Admin Operations - -### Get User Identifiers - -```csharp -await UAuthClient.Identifiers.GetUserAsync(userKey); -``` - - -### Add Identifier to User - -```csharp -await UAuthClient.Identifiers.AddUserAsync(userKey, request); -``` - - -### Update Identifier - -```csharp -await UAuthClient.Identifiers.UpdateUserAsync(userKey, request); -``` - - -### Delete Identifier - -```csharp -await UAuthClient.Identifiers.DeleteUserAsync(userKey, request); -``` - - -## πŸ”„ State Events - -Identifier changes trigger events: - -- IdentifiersChanged - -πŸ‘‰ Useful for: - -- UI updates -- Cache invalidation - -
- -## πŸ” Security Considerations - -- Identifiers may require verification -- Login identifiers can be restricted -- Primary identifier can be controlled - - -## 🎯 Summary - -UltimateAuth identifiers: - -- are independent entities -- support multiple types -- separate login vs non-login identifiers -- are fully manageable via client diff --git a/docs/content/client/index.md b/docs/content/client/index.md deleted file mode 100644 index 7f75a687..00000000 --- a/docs/content/client/index.md +++ /dev/null @@ -1,118 +0,0 @@ ---- -title: Client & API -order: 1 -group: client ---- - - -# πŸš€ Client Usage Guide - -UltimateAuth Client is a **high-level SDK** designed to simplify authentication flows. - -It is NOT just an HTTP wrapper. - -## 🧠 What Makes the Client Different? - -The client: - -- Handles full authentication flows (login, PKCE, refresh) -- Manages redirects automatically -- Publishes state events -- Provides structured results -- Works across multiple client types (SPA, server, hybrid) - -πŸ‘‰ You SHOULD use the client instead of calling endpoints manually. - -
- -## 🧱 Client Architecture - -The client is split into multiple specialized services: - -| Service | Responsibility | -|----------------------|-------------------------------------| -| FlowClient | Login, logout, refresh, PKCE | -| SessionClient | Session & device management | -| UserClient | User profile & lifecycle | -| IdentifierClient | Email / username / phone management | -| CredentialClient | Password management | -| AuthorizationClient | Roles & permissions | - -
- -## 🧩 Client Entry Point - -UltimateAuth exposes a single entry point: - -πŸ‘‰ `UAuthClient` - -```csharp -[Inject] IUAuthClient UAuthClient { get; set; } = null!; - -await UAuthClient.Flows.LoginAsync(...); -await UAuthClient.Users.GetMeAsync(); -await UAuthClient.Sessions.GetMyChainsAsync(); -``` - - -## πŸ”‘ Core Concept: Flow-Based Design - -UltimateAuth is **flow-oriented**, not endpoint-oriented. - -Instead of calling endpoints: - -❌ POST /auth/login -βœ” flowClient.LoginAsync() - -
- -## ⚑ Example - -```csharp -await UAuthClient.Flows.LoginAsync(new LoginRequest -{ - Identifier = "user@ultimateauth.com", - Secret = "password" -}); -``` - -πŸ‘‰ This automatically: - -- Builds request payload -- Handles redirect -- Integrates with configured endpoints - -
- -## πŸ”„ State Events - -Client automatically publishes events: - -- SessionRevoked -- ProfileChanged -- AuthorizationChanged - -
- -## 🧭 How to Use This Section - -Follow these guides: - -- authentication.md β†’ login, refresh, logout -- session-management.md β†’ sessions & devices -- user-management.md β†’ user operations -- identifiers.md β†’ login identifiers -- authorization.md β†’ roles & permissions - -## 🎯 Summary - -UltimateAuth Client: - -- abstracts complexity -- enforces correct flows -- reduces security mistakes - - -πŸ‘‰ Think of it as: - -**β€œAuthentication runtime for your frontend / app”** diff --git a/docs/content/client/session-management.md b/docs/content/client/session-management.md deleted file mode 100644 index 67e86767..00000000 --- a/docs/content/client/session-management.md +++ /dev/null @@ -1,171 +0,0 @@ ---- -title: Session Management -order: 3 -group: client ---- - - -# πŸ“± Session Management Guide - -This section explains how to manage sessions and devices using the UltimateAuth client. - -## 🧠 Overview - -In UltimateAuth, sessions are **not just tokens**. - -They are structured as: - -- Root β†’ user-level security -- Chain β†’ device (browser / mobile / app) -- Session β†’ individual authentication instance - -πŸ‘‰ On the client, you interact with: - -```csharp -[Inject] IUAuthClient UAuthClient { get; set; } = null!; - -await UAuthClient.Sessions... -``` - -## πŸ“‹ Get Active Sessions (Devices) - -```csharp -var result = await UAuthClient.Sessions.GetMyChainsAsync(); -``` - -πŸ‘‰ Returns: - -- Active devices -- Session chains -- Metadata (device, timestamps) - -
- -## πŸ” Get Session Detail - -```csharp -var detail = await UAuthClient.Sessions.GetMyChainDetailAsync(chainId); -``` - -πŸ‘‰ Use this to: - -- Inspect a specific device -- View session history - -
- -## πŸšͺ Logout vs Revoke (Important) - -UltimateAuth distinguishes between: - -### Logout - -```csharp -await UAuthClient.Flows.LogoutAsync(); -``` - -- Ends **current session** -- User can login again normally -- Does NOT affect other devices - -### Revoke (Session Control) - -```csharp -await UAuthClient.Sessions.RevokeMyChainAsync(chainId); -``` - -- Invalidates entire **device chain** -- All sessions under that device are revoked -- Cannot be restored -- New login creates a new chain - -πŸ‘‰ Key difference: - -- Logout = end current session -- Revoke = destroy device identity - -For standard auth process, use `UAuthClient.Flows.LogoutMyDeviceAsync(chainId)` instead of `UAuthClient.Sessions.RevokeMyChainAsync(chainId)` - -
- -## πŸ“± Revoke Other Devices - -```csharp -await UAuthClient.Sessions.RevokeMyOtherChainsAsync(); -``` - -πŸ‘‰ This: - -- Keeps current device active -- Logs out all other devices - -
- -## πŸ’₯ Revoke All Sessions - -```csharp -await UAuthClient.Sessions.RevokeAllMyChainsAsync(); -``` - -πŸ‘‰ This: - -- Logs out ALL devices (including current) -- Forces full reauthentication everywhere - -
- -## πŸ‘€ Admin Session Management - -### Get User Devices - -```csharp -await UAuthClient.Sessions.GetUserChainsAsync(userKey); -``` - -### Revoke Specific Session - -```csharp -await UAuthClient.Sessions.RevokeUserSessionAsync(userKey, sessionId); -``` - -### Revoke Device (Chain) - -```csharp -await UAuthClient.Sessions.RevokeUserChainAsync(userKey, chainId); -``` - -### Revoke All User Sessions - -```csharp -await UAuthClient.Sessions.RevokeAllUserChainsAsync(userKey); -``` - -
- -## 🧠 Device Model - -Each chain represents a **device identity**: - -- Browser instance -- Mobile device -- Application instance - -πŸ‘‰ Sessions are grouped under chains. - -
- -## πŸ” Security Implications - -Session operations are security-critical: - -- Revoke is irreversible -- Device isolation is enforced -- Cross-device attacks are contained - -## 🎯 Summary - -Session management in UltimateAuth: - -- is device-aware -- separates logout vs revoke -- gives full control over user sessions diff --git a/docs/content/client/user-management.md b/docs/content/client/user-management.md deleted file mode 100644 index f6c7c72e..00000000 --- a/docs/content/client/user-management.md +++ /dev/null @@ -1,182 +0,0 @@ ---- -title: User Management -order: 4 -group: client ---- - - -# πŸ‘€ User Management Guide - -This section explains how to manage users using the UltimateAuth client. - -## 🧠 Overview - -User operations are handled via: - -```csharp -UAuthClient.Users... -``` - -This includes: - -- Profile management -- User lifecycle -- Admin operations - -
- -## πŸ™‹ Get Current User - -```csharp -var me = await UAuthClient.Users.GetMeAsync(); -``` - -πŸ‘‰ Returns: - -- User profile -- Status -- Basic identity data - -
- -## ✏️ Update Profile - -```csharp -await UAuthClient.Users.UpdateMeAsync(new UpdateProfileRequest -{ - DisplayName = "John Doe" -}); -``` - -πŸ‘‰ Triggers: - -- Profile update -- State event (ProfileChanged) - -
- -## ❌ Delete Current User - -```csharp -await UAuthClient.Users.DeleteMeAsync(); -``` - -πŸ‘‰ This: - -- Deletes user (based on configured mode) -- Ends session -- Triggers state update - -
- -## πŸ‘‘ Admin: Query Users - -```csharp -var result = await UAuthClient.Users.QueryAsync(new UserQuery -{ - Search = "john", - PageNumber = 1, - PageSize = 10 -}); -``` - -πŸ‘‰ Supports: - -- search -- pagination -- filtering (status, etc.) - -
- -## βž• Create User - -```csharp -await UAuthClient.Users.CreateAsync(new CreateUserRequest -{ - UserName = "john", - Email = "john@mail.com", - Password = "123456" -}); -``` - -## πŸ›  Admin Create - -```csharp -await UAuthClient.Users.CreateAsAdminAsync(request); -``` - -
- -## πŸ”„ Change Status - -### Self - -```csharp -await UAuthClient.Users.ChangeMyStatusAsync(new ChangeUserStatusSelfRequest -{ - Status = UserStatus.SelfSuspended -}); -``` - -### Admin - -```csharp -await UAuthClient.Users.ChangeUserStatusAsync(userKey, new ChangeUserStatusAdminRequest -{ - Status = UserStatus.Suspended -}); -``` - -
- -## ❌ Delete User (Admin) - -```csharp -await UAuthClient.Users.DeleteUserAsync(userKey, new DeleteUserRequest -{ - Mode = DeleteMode.Soft -}); -``` - -## πŸ” Get User - -```csharp -var user = await UAuthClient.Users.GetUserAsync(userKey); -``` - -## ✏️ Update User (Admin) - -```csharp -await UAuthClient.Users.UpdateUserAsync(userKey, request); -``` - -## 🧠 Lifecycle Model - -Users have a lifecycle: - -- Active -- Suspended -- Disabled -- Deleted (soft/hard) - -πŸ‘‰ Status impacts: - -- login ability -- session validity -- authorization - -
- -## πŸ” Security Notes - -- Status changes may invalidate sessions -- Delete may trigger cleanup across domains -- Admin actions are policy-protected - -## 🎯 Summary - -User management in UltimateAuth: - -- is lifecycle-aware -- supports self + admin flows -- integrates with session & security model diff --git a/docs/content/configuration/advanced-configuration.md b/docs/content/configuration/advanced-configuration.md index 7ff33835..e37a688a 100644 --- a/docs/content/configuration/advanced-configuration.md +++ b/docs/content/configuration/advanced-configuration.md @@ -1,177 +1,1167 @@ --- title: Advanced Configuration -order: 6 +order: 4 group: configuration --- +# Advanced Configuration -# 🧠 Advanced Configuration +UltimateAuth supports advanced customization of authentication behavior through effective server options, authentication mode resolution, dependency injection, custom infrastructure, and lifecycle events. -UltimateAuth is designed to be flexible β€” but not fragile. +These mechanisms serve different purposes: -πŸ‘‰ You can customize almost every part of the system -πŸ‘‰ Without breaking its security guarantees +- **Options** define configurable behavior. +- **Effective options** adapt that behavior to the selected authentication mode. +- **Resolvers** determine information required by authentication flows. +- **Authorities and validators** participate in security decisions. +- **Stores** persist authentication and application state. +- **Events** allow applications to react to completed operations. -## ⚠️ Philosophy +Most applications should begin with the standard Server and Client options. Advanced customization becomes useful when an application needs specialized authentication behavior, custom infrastructure, or integration with existing systems. -Customization in UltimateAuth follows one rule: +This guide explains the supported extension mechanisms, their execution order, and the security boundaries developers must preserve. -πŸ‘‰ You can extend behavior -πŸ‘‰ You should not bypass security +## 1. Configuration Versus Extensibility -
+UltimateAuth distinguishes between configuring existing behavior and replacing an implementation. -## 🧩 Extension Points +For example, changing a session lifetime is configuration: -UltimateAuth exposes multiple extension points: +```csharp +builder.Services.AddUltimateAuthServer(options => +{ + options.Session.Lifetime = TimeSpan.FromHours(8); +}); +``` + +Providing a custom clock is an infrastructure extension: + +```csharp +builder.Services.AddSingleton(); +``` -- Resolvers -- Validators -- Authorities -- Orchestrators -- Stores -- Events +`ApplicationClock` is an implementation supplied by your application. -πŸ‘‰ You don’t replace the system -πŸ‘‰ You plug into it +These approaches are not interchangeable. -
+| Requirement | Recommended mechanism | +|---|---| +| Change session lifetime | Server options | +| Change token lifetime for a specific mode | `ConfigureMode()` | +| Select authentication mode differently | `IEffectiveAuthModeResolver` | +| Resolve tenants from a custom source | `ITenantIdResolver` | +| Customize login decisions | `ILoginAuthority` | +| Supply signing keys | `IJwtSigningKeyProvider` | +| Persist sessions in another backend | Store implementation | +| React to successful login | `UAuthEvents` | +| Add application-specific access rules | Policies | -## πŸ”Œ Replacing Services +Prefer the most specific supported mechanism rather than replacing an entire authentication component. -All core services can be overridden using DI: +## 2. Understanding Effective Server Options + +The server does not necessarily use the original `UAuthServerOptions` instance unchanged for every authentication flow. + +Instead, UltimateAuth can resolve an effective configuration based on the client profile and authentication mode. + +The relevant types are: ```csharp -services.AddScoped(); +IEffectiveServerOptionsProvider +EffectiveUAuthServerOptions +IEffectiveAuthModeResolver ``` -πŸ‘‰ This allows deep customization -πŸ‘‰ While preserving the pipeline +### Configured Options -
+`UAuthServerOptions` represents the application's configured server settings. -## 🧠 Authorities & Decisions +For example: -Authorities are responsible for decisions: +```csharp +builder.Services.AddUltimateAuthServer(options => +{ + options.Session.Lifetime = TimeSpan.FromHours(8); + options.Token.Issuer = "MyApplication"; +}); +``` -- LoginAuthority -- AccessAuthority +These settings form the base configuration. ---- +### Effective Options -You can override them: +The provider produces an `EffectiveUAuthServerOptions` instance containing: ```csharp -services.AddScoped(); +public sealed class EffectiveUAuthServerOptions +{ + public UAuthMode Mode { get; init; } + public UAuthServerOptions Options { get; init; } = default!; +} +``` + +`Mode` identifies the resolved authentication mode. + +`Options` contains the effective server configuration for that mode. + +The effective configuration is derived from the original settings rather than replacing the application's registered options globally. + +## 3. Effective Configuration Resolution + +The current `EffectiveServerOptionsProvider` follows this sequence: + +```text +Configured UAuthServerOptions + | + v + Resolve mode + | + v + Clone base options + | + v + Apply mode defaults + | + v + Apply ConfigureMode override + | + v + EffectiveUAuthServerOptions ``` -πŸ‘‰ This changes decision logic -πŸ‘‰ Without touching flows +In simplified form: + +```csharp +var original = baseOptions.Value; + +var mode = modeResolver.Resolve(clientProfile); + +var options = original.Clone(); -
+ConfigureDefaults.ApplyModeDefaults(mode, options); -## πŸ”„ Orchestrators +if (original.ModeConfigurations.TryGetValue(mode, out var configure)) +{ + configure(options); +} -Orchestrators coordinate execution: +return new EffectiveUAuthServerOptions +{ + Mode = mode, + Options = options +}; +``` -- Validate -- Authorize -- Execute command +This execution order is important. -πŸ‘‰ They enforce invariants -πŸ‘‰ Do not bypass them, unless you exact know what you are doing +**Mode defaults are applied after the original configuration is cloned.** -
+Consequently, certain values configured globally may be replaced by mode-specific defaults. -## πŸ—„ Store Customization +Explicit mode overrides are applied last. -You can provide custom stores: +### What Changes Between Requests? -- Session store -- Refresh token store -- User store +Different client profiles can resolve to different authentication modes. -πŸ‘‰ Supports EF Core, in-memory, or custom implementations +The provider therefore can produce different effective configurations for different client profiles. -## πŸ“‘ Events +However, this does not mean every setting is independently recalculated from arbitrary request data. -UltimateAuth provides event hooks: +In the current implementation, mode selection is based on the supplied `UAuthClientProfile`. -- Login -- Logout -- Refresh -- Revoke +The provider also accepts tenant context, but the default effective-options construction does not apply an additional tenant-specific configuration override. ---- +Applications should not assume that per-tenant options customization exists automatically. + +## 4. Default Authentication Mode Resolution + +The default `EffectiveAuthModeResolver` maps client profiles to authentication modes. + +| Client profile | Resolved mode | +|---|---| +| `BlazorServer` | `PureOpaque` | +| `WebServer` | `Hybrid` | +| `BlazorWasm` | `Hybrid` | +| `Maui` | `Hybrid` | +| `Api` | `PureJwt` | +| Other / unknown | `Hybrid` | + +These mappings describe the current default resolver. + +They do not imply that every resolved mode is fully supported in the current release. + +In particular, the current server options validator rejects explicit activation of `SemiHybrid` and `PureJwt` through `AllowedModes`. + +Applications should verify that their client profiles resolve to a supported authentication mode before deploying. + +### AllowedModes Is Not a Mode Selector + +For example: ```csharp -o.Events.OnUserLoggedIn = ctx => +builder.Services.AddUltimateAuthServer(options => { - // custom logic - return Task.CompletedTask; -}; + options.AllowedModes = + [ + UAuthMode.PureOpaque, + UAuthMode.Hybrid + ]; +}); ``` -πŸ‘‰ Use events for side-effects -πŸ‘‰ Not for core logic +This restricts which modes are allowed. + +It does not instruct the resolver to choose `Hybrid` for every client. -
+Mode selection and mode restrictions are separate responsibilities. -## βš™οΈ Mode Configuration Overrides +## 5. Configure Authentication Modes -You can customize behavior per mode: +Use the public `ConfigureMode()` extension to customize settings for a particular authentication mode. ```csharp -o.ModeConfigurations[UAuthMode.Hybrid] = options => +builder.Services.AddUltimateAuthServer(options => { - options.Token.AccessTokenLifetime = TimeSpan.FromMinutes(5); -}; + options.ConfigureMode(UAuthMode.Hybrid, mode => + { + mode.Token.AccessTokenLifetime = TimeSpan.FromMinutes(5); + }); +}); +``` + +This configuration changes the effective access token lifetime for `Hybrid`. + +It does not change the effective `PureOpaque` configuration. + +### Why ConfigureMode Matters + +Consider this configuration: + +```csharp +builder.Services.AddUltimateAuthServer(options => +{ + options.Token.AccessTokenLifetime = TimeSpan.FromMinutes(30); +}); ``` -πŸ‘‰ Runs after defaults -πŸ‘‰ Allows fine-grained control +The current `Hybrid` mode defaults explicitly set: -
+```csharp +Token.AccessTokenLifetime = TimeSpan.FromMinutes(10); +``` -## πŸ” Custom Resolvers +Because mode defaults are applied after the base options are cloned, the effective `Hybrid` access token lifetime becomes ten minutes rather than thirty. -You can override how data is resolved: +To override that behavior, use: -- Tenant resolver -- Session resolver -- Device resolver +```csharp +builder.Services.AddUltimateAuthServer(options => +{ + options.ConfigureMode(UAuthMode.Hybrid, mode => + { + mode.Token.AccessTokenLifetime = TimeSpan.FromMinutes(30); + }); +}); +``` -πŸ‘‰ Enables full control over request interpretation +The explicit mode override is applied after the mode defaults. -## πŸ›‘ Safety Boundaries +### Configure Multiple Modes -UltimateAuth enforces: +```csharp +builder.Services.AddUltimateAuthServer(options => +{ + options.ConfigureMode(UAuthMode.Hybrid, mode => + { + mode.Token.AccessTokenLifetime = TimeSpan.FromMinutes(10); + }); + + options.ConfigureMode(UAuthMode.PureOpaque, mode => + { + mode.Session.IdleTimeout = TimeSpan.FromDays(3); + }); +}); +``` -- Invariants -- Validation -- Fail-fast behavior +Each override is associated with its specified mode. -πŸ‘‰ Unsafe overrides will fail early +### Important Limitations -## 🧠 Mental Model +`ConfigureMode()` configures the effective options for a mode. -If you remember one thing: +It does not: -πŸ‘‰ Extend the system -πŸ‘‰ Don’t fight the system +- Enable an unsupported authentication mode. +- Bypass the server's allowed-mode restrictions. +- Replace the authentication mode resolver. +- Automatically create a tenant-specific configuration. +- Guarantee that every combination of effective settings is safe. ---- +The current implementation applies mode overrides during effective-options resolution. -## πŸ“Œ Key Takeaways +Do not assume that every mode-specific combination receives the same startup validation as the original `UAuthServerOptions` instance. -- Everything is replaceable via DI -- Authorities control decisions -- Orchestrators enforce flow -- Events are for side-effects -- Security boundaries are protected +Test mode overrides carefully, especially when changing token issuance, session lifetimes, or credential delivery. ---- +## 6. Mode Defaults + +UltimateAuth provides mode-specific defaults for authentication behavior. + +The current implementation includes templates for: + +- `PureOpaque` +- `Hybrid` +- `SemiHybrid` +- `PureJwt` + +These templates configure relevant token, session, cookie, and response behavior. + +### PureOpaque + +The `PureOpaque` defaults include: + +```csharp +Token.IssueJwt = false; +Token.IssueOpaque = false; +Token.IssueRefresh = false; +``` + +In this mode, the session identifier is the primary opaque authentication credential. + +The defaults also configure a longer idle timeout and session touch interval where no explicit values were supplied. + +### Hybrid + +The `Hybrid` defaults include: + +```csharp +Token.IssueJwt = true; +Token.IssueOpaque = true; +Token.AccessTokenLifetime = TimeSpan.FromMinutes(10); +Token.RefreshTokenLifetime = TimeSpan.FromDays(7); +``` + +Hybrid also configures refresh-token delivery through a cookie. + +### Unsupported Modes + +Although default templates exist for `SemiHybrid` and `PureJwt`, their presence does not mean those modes are ready for production use. + +The server's current validator explicitly rejects these modes when they are enabled through `AllowedModes`. + +Only use modes supported by the current release. + +## 7. Inspect Effective Options + +Advanced integrations can use `IEffectiveServerOptionsProvider` to inspect configured and effective server options. + +The interface exposes: + +```csharp +public interface IEffectiveServerOptionsProvider +{ + UAuthServerOptions GetOriginal(HttpContext context); + EffectiveUAuthServerOptions GetEffective(HttpContext context, UAuthClientProfile clientProfile); + EffectiveUAuthServerOptions GetEffective(TenantKey tenant, UAuthClientProfile clientProfile); +} +``` + +### Inspect the Original Configuration + +```csharp +var original = provider.GetOriginal(httpContext); +var configuredLifetime = original.Token.AccessTokenLifetime; +``` + +### Inspect the Effective Configuration + +```csharp +var effective = provider.GetEffective(httpContext, UAuthClientProfile.BlazorWasm); +var mode = effective.Mode; +var lifetime = effective.Options.Token.AccessTokenLifetime; +``` + +This is useful when diagnosing unexpected differences between configured and runtime behavior. + +For example, a globally configured access token lifetime may differ from the effective value because a mode template overrides it. + +### Do Not Mutate the Original Options + +`GetOriginal()` returns the original options instance. + +Treat it as read-only application configuration. + +Runtime customization should use the supported configuration mechanisms rather than mutating registered options during requests. + +## 8. Custom Authentication Mode Resolution + +UltimateAuth exposes: + +```csharp +IEffectiveAuthModeResolver +``` + +The interface contains: + +```csharp +public interface IEffectiveAuthModeResolver +{ + UAuthMode Resolve(UAuthClientProfile clientProfile); +} +``` + +The default implementation selects the mode using the client profile. + +An application can provide its own resolver when the default mapping does not meet its requirements. + +### Custom Resolver Example + +```csharp +public sealed class ApplicationAuthModeResolver : IEffectiveAuthModeResolver +{ + public UAuthMode Resolve(UAuthClientProfile clientProfile) + { + return clientProfile switch + { + UAuthClientProfile.BlazorServer => UAuthMode.PureOpaque, + UAuthClientProfile.BlazorWasm => UAuthMode.Hybrid, + UAuthClientProfile.Maui => UAuthMode.Hybrid, + + _ => UAuthMode.Hybrid + }; + } +} +``` + +Register the implementation: + +```csharp +builder.Services.AddSingleton(); +builder.Services.AddUltimateAuthServer(); +``` + +The server registers its default resolver using `TryAddSingleton`, so an existing registration can take precedence. + +### Security Considerations + +The custom resolver must return a mode compatible with the server's supported modes and security requirements. + +Do not choose authentication modes based solely on untrusted client-provided values without appropriate validation. + +Also note that the current resolver interface receives a client profile, not an arbitrary HTTP request context. + +For more complex context-dependent selection, additional application integration may be necessary. + +## 9. Dependency Injection Extension Points + +UltimateAuth registers many internal services through dependency injection. + +Several default registrations use `TryAddScoped` or `TryAddSingleton`, allowing applications to supply an implementation before the default registration is added. + +For example: + +```csharp +builder.Services.AddScoped(); +builder.Services.AddUltimateAuthServer(); +``` + +The application-defined validator is registered before the UltimateAuth server's default registration. + +Because the server uses `TryAddScoped` for this service, it does not add its default implementation when an existing registration is present. + +### Registration Order Matters + +For services registered using `TryAdd...`, prefer: + +```csharp +builder.Services.AddScoped(); +builder.Services.AddUltimateAuthServer(); +``` + +Do not assume that registering a replacement after the server will always produce the same behavior. + +The result depends on the service's registration pattern and how it is resolved. + +### Not Every Service Is Replaceable in the Same Way + +Some UltimateAuth services use ordinary `AddSingleton` or `AddScoped` registrations. + +Others use `TryAdd...`. + +Some are registered as collections. + +These patterns have different override semantics. + +Therefore, do not assume that every internal service can be replaced safely or that every replacement preserves the intended security behavior. + +## 10. Custom Login Authority + +The login authority participates in deciding whether a login attempt is permitted. + +UltimateAuth exposes: + +```csharp +ILoginAuthority +``` + +The interface is: + +```csharp +public interface ILoginAuthority +{ + LoginDecision Decide(LoginDecisionContext context); +} +``` + +An application can implement its own login decision logic. + +For example: + +```csharp +public sealed class ApplicationLoginAuthority : ILoginAuthority +{ + public LoginDecision Decide(LoginDecisionContext context) + { + // Evaluate application-specific login rules. + // Return a valid LoginDecision. + throw new NotImplementedException(); + } +} +``` + +The example illustrates the extension contract; it is not a usable implementation. + +Register the implementation: + +```csharp +builder.Services.AddScoped(); +builder.Services.AddUltimateAuthServer(); +``` + +### Important Security Boundary + +Replacing the login authority changes a security decision component. + +The replacement must preserve required authentication checks and correctly handle all decision outcomes. + +Do not replace the authority merely to add logging, notifications, or metrics. + +Use lifecycle events for those purposes. + +If the application only needs additional access rules, consider the policy system before replacing a core authority. + +## 11. Authorities, Policies, and Invariants + +UltimateAuth separates different responsibilities within its security architecture. + +### Authorities + +Authorities evaluate security decisions. + +Examples include: + +```csharp +IAuthAuthority +IAccessAuthority +ILoginAuthority +``` + +### Policies + +Policies define application-specific rules used by the authorization system. + +They should be the preferred extension mechanism when the requirement is to add or refine access rules. + +### Invariants + +Invariants enforce foundational security requirements. + +The server registers invariants including: + +```csharp +DeviceRequiredInvariant +ExpiredSessionInvariant +InvalidOrRevokedSessionInvariant +TenantResolvedInvariant +``` + +These are registered as a collection of `IAuthorityInvariant` implementations. + +Unlike ordinary application policies, security invariants protect fundamental authentication requirements. + +**Do not remove or bypass invariants to make a custom authentication flow succeed.** + +For example, an expired or revoked session should not become valid merely because a custom access policy grants permission. + +## 12. Custom Tenant Resolution + +UltimateAuth supports route, header, and host-based tenant resolution. + +These mechanisms are controlled through server options. + +For example: + +```csharp +builder.Services.AddUltimateAuthServer(options => +{ + options.MultiTenant.EnableHeader = true; +}); +``` + +For applications with a different tenant identification strategy, UltimateAuth exposes: + +```csharp +ITenantIdResolver +``` + +The server's default registration uses `TryAddSingleton`. + +A custom resolver can therefore be registered before the server: + +```csharp +builder.Services.AddSingleton(); +builder.Services.AddUltimateAuthServer(); +``` + +`ApplicationTenantIdResolver` represents an application-defined implementation. + +### When to Use a Custom Resolver + +A custom tenant resolver may be appropriate when tenants are identified through: + +- An application-specific host naming convention. +- A trusted gateway-provided tenant identifier. +- A custom routing structure. +- An existing tenant management system. + +### Security Considerations + +Tenant identification is not the same as tenant authorization. + +The server must still verify that the authenticated user or application is allowed to access the resolved tenant. + +Never trust arbitrary tenant identifiers supplied by clients without applying the required validation and isolation rules. + +## 13. Custom Session and Device Resolution + +UltimateAuth separates session identifier resolution from session validation. + +For example: + +```csharp +ISessionIdResolver +ISessionValidator +IDeviceResolver +``` + +These interfaces have different responsibilities. + +| Component | Responsibility | +|---|---| +| `ISessionIdResolver` | Determine which session identifier is supplied | +| `ISessionValidator` | Validate the session | +| `IDeviceResolver` | Resolve device information | + +The server supports multiple session identifier sources through internal resolvers, including cookies, bearer credentials, headers, and query parameters. + +The active resolution behavior is controlled by the configured session resolution options. + +### Custom Session Validator + +```csharp +builder.Services.AddScoped(); +builder.Services.AddUltimateAuthServer(); +``` + +A custom validator must preserve the relevant expiration, revocation, and security-state checks. + +### Custom Device Resolver + +```csharp +builder.Services.AddScoped(); +builder.Services.AddUltimateAuthServer(); +``` + +Device resolution may affect session identification and security decisions. + +Custom implementations should produce consistent, trustworthy device information. + +Avoid replacing these services unless the application's requirements cannot be met through standard options. + +## 14. Custom Stores + +UltimateAuth separates domain behavior from persistence through store interfaces. + +Examples include: + +```csharp +ISessionStore +IRefreshTokenStore +IRoleStore +IUserRoleStore +``` + +Some stores are resolved through corresponding factory interfaces. + +UltimateAuth provides persistence implementations in separate packages, including in-memory and Entity Framework Core implementations. + +### Choosing a Store Implementation + +Use in-memory implementations for suitable development, testing, or demonstration scenarios. + +Use durable persistence for production authentication state that must survive process restarts or be shared across application instances. + +### Custom Store Requirements + +A custom store must preserve the semantics expected by the authentication services. + +Depending on the store, these may include: + +- Correct session expiration handling. +- Reliable revocation state. +- Refresh token consumption and replay protection. +- Consistent user and tenant scoping. +- Appropriate concurrency behavior. +- Atomic state transitions where required. +- Correct deletion and lifecycle behavior. + +For example, refresh token rotation may depend on preventing two concurrent requests from successfully consuming the same token. + +A store implementation that does not enforce the required concurrency semantics can undermine the security of the refresh flow. + +### Store Registration + +Custom store registration depends on the specific store interface and its factory. + +Do not assume that replacing one store interface is sufficient when the runtime resolves stores through a factory. + +Use the relevant domain package's documented registration pattern. + +## 15. Authentication Lifecycle Events + +UltimateAuth provides application-wide authentication event hooks through: + +```csharp +UAuthEvents +``` + +These hooks allow applications to react to authentication lifecycle events without replacing the authentication flow. + +The current event options expose: + +```csharp +OnAnyEvent +OnUserLoggedIn +OnUserLoggedOut +``` + +### Login Event + +```csharp +builder.Services.AddUltimateAuthServer(options => +{ + options.Events.OnUserLoggedIn = context => + { + // Record a successful login. + return Task.CompletedTask; + }; +}); +``` + +### Logout Event + +```csharp +builder.Services.AddUltimateAuthServer(options => +{ + options.Events.OnUserLoggedOut = context => + { + // Record a logout event. + return Task.CompletedTask; + }; +}); +``` + +### Global Event Hook + +```csharp +builder.Services.AddUltimateAuthServer(options => +{ + options.Events.OnAnyEvent = context => + { + // Process an authentication event. + return Task.CompletedTask; + }; +}); +``` + +The global hook receives an `IAuthEventContext`. + +### Appropriate Event Uses + +Lifecycle events are suitable for: + +- Audit logging. +- Application metrics. +- Monitoring. +- Notifications. +- Lightweight post-operation integration. + +They should not be used to implement critical authentication checks. + +### Events Are Not Decision Hooks + +The documented event delegates run after their corresponding successful operations. + +For example, `OnUserLoggedIn` is not the place to decide whether the login should have been permitted. + +Use an appropriate authority or policy for security decisions. + +### Avoid Complex Work Inside Events + +Long-running or failure-prone external operations should generally be delegated to a reliable application service or message-processing mechanism. + +Do not make critical authentication behavior depend on an unreliable external notification system. + +## 16. Custom JWT Signing Keys + +UltimateAuth exposes: + +```csharp +IJwtSigningKeyProvider +``` + +The server registers a development signing key provider as its default implementation. + +Production applications should supply an appropriate signing key strategy. + +For example: + +```csharp +builder.Services.AddSingleton(); +builder.Services.AddUltimateAuthServer(); +``` + +`ApplicationJwtSigningKeyProvider` is an implementation supplied by the application. + +A production implementation should use appropriately protected signing keys and support the application's operational requirements. + +### Signing Key Responsibilities + +Consider: + +- Key confidentiality. +- Key rotation. +- Key identification. +- Consistent validation across resource servers. +- Secure key storage. +- Recovery and deployment procedures. + +Do not use development signing keys as a production security strategy. + +## 17. Endpoint Customization + +UltimateAuth provides an advanced endpoint configuration hook: + +```csharp +options.OnConfigureEndpoints +``` + +The hook receives: + +```csharp +IEndpointRouteBuilder +``` + +For example: + +```csharp +builder.Services.AddUltimateAuthServer(options => +{ + options.OnConfigureEndpoints = endpoints => + { + // Apply supported application-specific + // endpoint configuration here. + }; +}); +``` + +The hook is invoked after UltimateAuth registers its default endpoints. + +It can support advanced endpoint integration, but it should not be used to remove, remap, or weaken the security requirements of UltimateAuth endpoints. + +### Prefer Endpoint Options First + +For ordinary endpoint configuration, use: + +```csharp +options.Endpoints +``` + +For example: + +```csharp +builder.Services.AddUltimateAuthServer(options => +{ + options.Endpoints.Authentication = true; + options.Endpoints.Credentials = true; + options.Endpoints.Authorization = true; +}); +``` + +The advanced endpoint hook is intended for cases that cannot be addressed through the supported endpoint options. + +## 18. Configuration Sources and Precedence + +UltimateAuth integrates with the standard ASP.NET Core configuration system. + +For the server, the configuration section is: + +```text +UltimateAuth:Server +``` + +For the client: + +```text +UltimateAuth:Client +``` + +For Resource APIs: + +```text +UltimateAuth:ResourceApi +``` + +### Registration Order + +The standard server registration applies: + +1. Option type defaults. +2. The `AddUltimateAuthServer()` configuration delegate. +3. Values bound from `UltimateAuth:Server`. +4. Post-configuration processing. +5. Registered options validation. + +For example: + +```csharp +builder.Services.AddUltimateAuthServer(options => +{ + options.Login.MaxFailedAttempts = 5; +}); +``` + +With: + +```json +{ + "UltimateAuth": { + "Server": { + "Login": { + "MaxFailedAttempts": 3 + } + } + } +} +``` + +The effective configured value is `3`. + +The configuration binding occurs after the registration delegate. + +### Environment Variables + +ASP.NET Core supports hierarchical configuration keys through double underscores. + +For example: + +```text +UltimateAuth__Server__Login__MaxFailedAttempts=3 +``` + +This corresponds to: + +```text +UltimateAuth:Server:Login:MaxFailedAttempts +``` + +Environment-specific configuration can therefore override values without requiring changes to application code. + +### Environment-Specific Files + +Standard ASP.NET Core applications commonly use: + +```text +appsettings.json +appsettings.Development.json +appsettings.Staging.json +appsettings.Production.json +``` + +The active environment determines which environment-specific file is loaded. + +For example: + +```text +ASPNETCORE_ENVIRONMENT=Production +``` + +The final value depends on the configured ASP.NET Core configuration providers and their precedence. + +### Configuration Binding Does Not Replace Mode Resolution + +A value supplied through `appsettings.json` becomes part of the configured options. + +It may still be replaced later when UltimateAuth applies mode defaults. + +For settings explicitly controlled by mode templates, use `ConfigureMode()` when the desired behavior must apply to a specific mode. + +## 19. Startup Validation and Runtime Safety + +UltimateAuth validates server options through registered `IValidateOptions` implementations. + +The server uses: + +```csharp +ValidateOnStart() +``` + +This allows many invalid configurations to fail during application startup. + +Examples include: + +- Invalid endpoint base paths. +- Invalid or unsupported allowed modes. +- Invalid session lifetimes. +- Incompatible token and session lifetime constraints. +- Invalid login settings. +- Invalid tenant configuration. +- Invalid pagination settings. + +### Validation Has Boundaries + +Startup validation applies to the configured options according to the registered validators. + +It does not automatically prove that every custom dependency injection replacement is safe. + +It also does not guarantee that every mode-specific override has been validated against all possible security invariants. + +Custom implementations must preserve the relevant contracts and security requirements. + +### Recommended Validation Strategy + +For advanced configurations: + +1. Verify that the application starts successfully. +2. Test each enabled authentication mode. +3. Test login, refresh, validation, and logout. +4. Test session expiration and revocation. +5. Test tenant isolation. +6. Test authorization denial paths. +7. Test concurrent refresh requests where applicable. +8. Verify that effective options contain the intended values. + +These checks are especially important when customizing authorities, validators, stores, or authentication mode behavior. + +## 20. Common Configuration Mistakes + +### Configuring Core Options Separately from Server Options + +When using `AddUltimateAuthServer()`, configure Core-defined behavior through `UAuthServerOptions`. + +Do not expect a separate `UAuthOptions` configuration to override the server's composed options. + +### Assuming Global Values Always Survive Mode Defaults + +Mode templates can overwrite values such as access token lifetime. + +Use `ConfigureMode()` for explicit mode-specific customization. + +### Treating AllowedModes as Mode Selection + +`AllowedModes` restricts permitted modes. + +It does not replace `IEffectiveAuthModeResolver`. + +### Enabling an Unsupported Mode + +An enum value or default template does not establish that a mode is ready for production. + +Use only modes supported by the current implementation. + +### Replacing Security Components Unnecessarily + +Replacing a validator or authority can change fundamental authentication behavior. + +Prefer options and policies when they satisfy the requirement. + +### Assuming Every DI Service Is Replaceable + +Different services use different registration patterns. + +Review the actual registration behavior before replacing a service. + +### Using Events for Authorization Decisions + +Lifecycle events are for post-operation reactions. + +Use authorities and policies for decisions. + +### Ignoring Store Concurrency + +Authentication stores must preserve the required consistency and atomicity semantics. + +This is particularly important for refresh token rotation and revocation. + +### Assuming Runtime Overrides Receive Full Startup Validation + +Mode-specific overrides are applied during effective-options construction. + +Validate their results through tests and appropriate application safeguards. + +## 21. Advanced Configuration Reference + +| Mechanism | API / Type | Purpose | +|---|---|---| +| Server configuration | `UAuthServerOptions` | Base server settings | +| Mode customization | `ConfigureMode()` | Apply mode-specific overrides | +| Effective configuration | `IEffectiveServerOptionsProvider` | Resolve effective server settings | +| Effective result | `EffectiveUAuthServerOptions` | Resolved mode and options | +| Mode selection | `IEffectiveAuthModeResolver` | Resolve mode from client profile | +| Login decisions | `ILoginAuthority` | Evaluate login decisions | +| Authentication authority | `IAuthAuthority` | Authentication security decisions | +| Access authority | `IAccessAuthority` | Access decisions | +| Tenant identification | `ITenantIdResolver` | Resolve tenant identifiers | +| Session validation | `ISessionValidator` | Validate sessions | +| Session identification | `ISessionIdResolver` | Resolve session identifiers | +| Device identification | `IDeviceResolver` | Resolve device information | +| Signing keys | `IJwtSigningKeyProvider` | Supply JWT signing keys | +| Lifecycle events | `UAuthEvents` | Observe completed authentication operations | +| Endpoint customization | `OnConfigureEndpoints` | Advanced endpoint configuration | +| Session persistence | `ISessionStore` | Session storage | +| Refresh persistence | `IRefreshTokenStore` | Refresh token storage | + +## 22. Recommended Extension Strategy + +When an application requires customization, follow this order: + +1. **Use existing options.** Prefer supported configuration properties. +2. **Use mode overrides.** Apply `ConfigureMode()` when behavior must differ by authentication mode. +3. **Use application policies.** Extend authorization rules without replacing the authorization pipeline. +4. **Use lifecycle events.** Implement logging, monitoring, and notifications without changing authentication decisions. +5. **Use focused resolvers or providers.** Customize tenant identification, signing keys, or other specific infrastructure concerns. +6. **Replace authorities, validators, or stores only when necessary.** Preserve all relevant security invariants and verify the implementation through tests. + +The objective is not to replace as much of UltimateAuth as possible. + +It is to customize the smallest component necessary while retaining the guarantees of the surrounding system. -## ➑️ Next Step +## Next Steps -Return to **Auth Flows** or explore **Plugin Domains** +- [Configuration Overview](/docs/configuration/overview) β€” Understand registration, configuration sources, and architecture. +- [Server Options](/docs/configuration/server-options) β€” Configure authentication and server security behavior. +- [Client Options](/docs/configuration/client-options) β€” Configure SDK flows and client behavior. +- [Authentication Modes](/docs/fundamentals/auth-modes) β€” Understand supported authentication modes. +- [Authorization](/docs/client/authorization) β€” Use the client authorization API. +- [Session Management](/docs/client/session-management) β€” Manage sessions and revocation. \ No newline at end of file diff --git a/docs/content/configuration/client-options.md b/docs/content/configuration/client-options.md index 4ce9c7c3..b7d09678 100644 --- a/docs/content/configuration/client-options.md +++ b/docs/content/configuration/client-options.md @@ -1,125 +1,1148 @@ --- title: Client Options -order: 4 +order: 3 group: configuration --- +# Client Options -# 🧩 Client Options +`UAuthClientOptions` controls how an UltimateAuth client communicates with the authentication server, executes authentication flows, maintains client-side authentication state, and responds when authentication must be renewed. -Client Options define how UltimateAuth behaves on the **client side**. +The client configuration is intentionally separate from server security configuration. -πŸ‘‰ While Server controls the system, -πŸ‘‰ Client controls how it is **used** +The server determines which authentication operations are permitted, how credentials are validated, and when sessions expire. The client determines how those operations are initiated and how the application responds to their outcomes. -## 🧠 What Are Client Options? +This guide covers the current client configuration API, platform integration, defaults, practical examples, and important security considerations. -Client options configure: +## 1. Register the UltimateAuth Client -- Client profile (WASM, Server, MAUI, API) -- Endpoint communication -- PKCE behavior -- Token refresh -- Re-authentication behavior +UltimateAuth provides a platform-independent client SDK and platform-specific adapters. -
+For Blazor applications, use: -## βš™οΈ Basic Usage +```csharp +builder.Services.AddUltimateAuthClientBlazor(options => +{ + options.AutoDetectClientProfile = true; + options.DefaultReturnUrl = "/"; +}); +``` + +`AddUltimateAuthClientBlazor()` registers the underlying client services and the Blazor-specific infrastructure, including browser transport, storage, device identification, and authentication state integration. + +### Core Client Registration + +The platform-independent registration method is: ```csharp -builder.Services.AddUltimateAuthClientBlazor(o => +builder.Services.AddUltimateAuthClient(options => { - o.AutoDetectClientProfile = false; + options.AutoDetectClientProfile = true; }); ``` -
+This registers the shared client SDK. -## 🧩 Client Profile +However, it does not provide every platform-specific implementation needed to run authentication flows. -UltimateAuth automatically detects client type by default: +Applications should use an appropriate platform adapter rather than relying on the core client registration alone. -- Blazor WASM -- Blazor Server -- MAUI -- WebServer -- API +### Do Not Register Both Client Methods -You can override manually: +When using: ```csharp -o.ClientProfile = UAuthClientProfile.BlazorWasm; +builder.Services.AddUltimateAuthClientBlazor(); ``` -πŸ‘‰ Manual override is useful for testing or special scenarios +do not also call: -
+```csharp +builder.Services.AddUltimateAuthClient(); +``` + +The Blazor registration already includes the core client services. -## 🌐 Endpoints +### Server and Client in One Application -Defines where requests are sent: +A Blazor Server application can register both UltimateAuth Server and the Blazor client adapter: ```csharp -o.Endpoints.BasePath = "https://localhost:5001/auth"; +builder.Services.AddUltimateAuthServer(options => +{ + options.Session.Lifetime = TimeSpan.FromDays(7); +}); + +builder.Services.AddUltimateAuthClientBlazor(options => +{ + options.ClientProfile = UAuthClientProfile.BlazorServer; + options.AutoDetectClientProfile = false; +}); ``` -πŸ‘‰ Required for WASM / remote clients +These registrations configure different components. -
+The server controls authentication security and lifecycle rules. -## πŸ” PKCE Configuration +The client controls the application-side integration. -Used for browser-based login flows: +## 2. Client Options Architecture + +`UAuthClientOptions` contains the following properties: + +| Property | Type | Purpose | +|---|---|---| +| `ClientProfile` | `UAuthClientProfile` | Identifies the client platform | +| `AutoDetectClientProfile` | `bool` | Enables automatic profile detection | +| `DefaultReturnUrl` | `string?` | Default navigation destination after authentication | +| `Endpoints` | `UAuthClientEndpointOptions` | Authentication endpoint paths | +| `Login` | `UAuthClientLoginFlowOptions` | Login flow configuration | +| `Pkce` | `UAuthClientPkceLoginFlowOptions` | PKCE flow configuration | +| `AutoRefresh` | `UAuthClientAutoRefreshOptions` | Background refresh coordination | +| `Reauth` | `UAuthClientReauthOptions` | Reauthentication behavior | +| `MultiTenant` | `UAuthClientMultiTenantOptions` | Tenant propagation | +| `StateEvents` | `UAuthStateEventOptions` | Authentication state event handling | + +These options are configured through the same `UAuthClientOptions` instance. + +For example: ```csharp -o.Pkce.ReturnUrl = "https://localhost:5002/home"; +builder.Services.AddUltimateAuthClientBlazor(options => +{ + options.DefaultReturnUrl = "/dashboard"; + options.Pkce.Enabled = true; + options.AutoRefresh.Interval = TimeSpan.FromMinutes(5); + options.StateEvents.HandlingMode = UAuthStateEventHandlingMode.Patch; +}); ``` -πŸ‘‰ Required for WASM scenarios +## 3. Client Profiles + +The client profile identifies the application environment and influences authentication flow behavior. + +Configure it through: + +```csharp +options.ClientProfile +``` + +UltimateAuth defines profiles including: + +| Profile | Typical application | +|---|---| +| `BlazorServer` | Interactive Blazor Server applications | +| `BlazorWasm` | Browser-hosted Blazor WebAssembly | +| `Maui` | .NET MAUI applications | +| `WebServer` | Traditional ASP.NET Core web applications | +| `Api` | API-oriented clients | +| `UAuthHub` | UltimateAuth Hub | +| `NotSpecified` | Profile not explicitly selected | + +Different client profiles may result in different authentication modes on the server. + +For example, the default server mode resolver maps Blazor Server to `PureOpaque` and Blazor WebAssembly to `Hybrid`. -
+The client profile does not itself authorize an authentication mode. The server must support and permit the resolved mode. -## πŸ” Auto Refresh +## 4. Automatic Client Profile Detection -Controls token/session refresh behavior: +Automatic profile detection is enabled by default: ```csharp -o.AutoRefresh.Interval = TimeSpan.FromMinutes(1); +options.AutoDetectClientProfile = true; ``` -πŸ‘‰ Keeps authentication alive automatically +When enabled, UltimateAuth detects the client environment if `ClientProfile` remains `NotSpecified`. -
+The current detector checks for: -## πŸ”„ Re-authentication +1. UAuthHub registration. +2. .NET MAUI. +3. Blazor WebAssembly. +4. Blazor Server. +5. A fallback to `WebServer`. -Controls behavior when session expires: +### Default Configuration ```csharp -o.Reauth.Behavior = ReauthBehavior.RaiseEvent; +builder.Services.AddUltimateAuthClientBlazor(options => +{ + options.AutoDetectClientProfile = true; +}); ``` -πŸ‘‰ Allows silent or interactive re-login +No explicit profile is required for ordinary supported environments where detection succeeds. -
+### Manual Configuration -## 🧠 Mental Model +For applications that need predictable behavior independent of assembly-based detection: + +```csharp +builder.Services.AddUltimateAuthClientBlazor(options => +{ + options.AutoDetectClientProfile = false; + options.ClientProfile = UAuthClientProfile.BlazorWasm; +}); +``` -If you remember one thing: +Manual configuration can be useful for: -πŸ‘‰ Server decides -πŸ‘‰ Client adapts +- Specialized hosting environments. +- Integration tests. +- Applications with multiple hosting models. +- Cases where assembly-based detection is ambiguous. -## πŸ“Œ Key Takeaways +### Important Validation Rule -- Client options control runtime behavior -- Profile detection is automatic -- PKCE is required for public clients -- Refresh and re-auth are configurable -- Works together with Server options +This configuration is invalid: ---- +```csharp +builder.Services.AddUltimateAuthClientBlazor(options => +{ + options.AutoDetectClientProfile = false; + options.ClientProfile = UAuthClientProfile.NotSpecified; +}); +``` + +When automatic detection is disabled, an explicit client profile must be provided. + +### Explicit Profiles Take Precedence + +The post-configuration detector does not replace a profile that has already been explicitly selected. + +For example: + +```csharp +builder.Services.AddUltimateAuthClientBlazor(options => +{ + options.ClientProfile = UAuthClientProfile.BlazorServer; +}); +``` + +The explicit profile is retained even when automatic detection remains enabled. + +### Detection Limitations + +The current detector uses service registration and loaded assemblies to identify the environment. + +Assembly-based detection may not be completely reliable in every hosting arrangement. + +For complex Blazor Web App hosting scenarios, explicitly configuring the profile can make authentication behavior easier to reason about. + +## 5. Endpoint Configuration + +`options.Endpoints` defines the endpoint paths used by the client SDK. + +The default base path is: + +```text +/auth +``` + +### Configure a Remote Authentication Server + +For a client communicating with a separately hosted UAuthHub: + +```csharp +builder.Services.AddUltimateAuthClientBlazor(options => +{ + options.Endpoints.BasePath = "https://auth.example.com/auth"; +}); +``` + +For an application using same-origin authentication endpoints: + +```csharp +builder.Services.AddUltimateAuthClientBlazor(options => +{ + options.Endpoints.BasePath = "/auth"; +}); +``` + +The base path must match the deployed authentication server's routing configuration. + +Changing a client endpoint does not create or expose the corresponding server endpoint. + +### Default Endpoint Paths + +| Property | Default | +|---|---| +| `BasePath` | `/auth` | +| `Login` | `/login` | +| `TryLogin` | `/try-login` | +| `Logout` | `/logout` | +| `Refresh` | `/refresh` | +| `Reauth` | `/reauth` | +| `Validate` | `/validate` | +| `PkceAuthorize` | `/pkce/authorize` | +| `PkceTryComplete` | `/pkce/try-complete` | +| `PkceComplete` | `/pkce/complete` | +| `HubLoginPath` | `/uauthhub/entry` | + +### Customize Individual Paths + +```csharp +builder.Services.AddUltimateAuthClientBlazor(options => +{ + options.Endpoints.BasePath = "/auth"; + options.Endpoints.Login = "/login"; + options.Endpoints.Logout = "/logout"; + options.Endpoints.Refresh = "/refresh"; +}); +``` + +These values should reflect the actual routes registered by UltimateAuth Server. + +### Required Endpoint Validation + +The client options validator requires: + +- `Endpoints.BasePath` +- `Endpoints.Login` +- `Endpoints.Logout` +- `Endpoints.Refresh` +- `Endpoints.Validate` + +These properties must not be null, empty, or whitespace-only. + +The current validator does not apply the same required-value checks to every optional endpoint path. + +### Remote Clients and Browser Security + +When using a remote authentication server, also consider: + +- HTTPS. +- Cross-origin request configuration. +- Cookie `SameSite` and `Secure` attributes. +- Allowed client origins. +- PKCE redirects. +- Browser credential policies. + +A correct endpoint URL alone does not guarantee that cross-origin authentication will work. + +## 6. Login Flow Options + +`options.Login` controls client-side login behavior. + +The available properties are: + +| Property | Default | Purpose | +|---|---|---| +| `ReturnUrl` | `null` | Login-specific return destination | +| `AllowCredentialPost` | `false` | Allow direct credential posting | + +### Configure Login Navigation + +```csharp +builder.Services.AddUltimateAuthClientBlazor(options => +{ + options.Login.ReturnUrl = "/dashboard"; +}); +``` + +The login-specific return URL is used when the login flow does not provide a more specific destination. + +If it is not set, UltimateAuth can fall back to `DefaultReturnUrl` or the current location according to the flow. + +### Direct Credential Posting + +The default is: + +```csharp +options.Login.AllowCredentialPost = false; +``` + +This is an important security setting. + +Directly posting user credentials is not appropriate for public browser clients such as Blazor WebAssembly applications. + +Public clients should use the supported PKCE-based login flow. + +### Trusted Server-Hosted Clients + +A trusted server-hosted application may explicitly enable direct credential posting where its authentication architecture requires it: + +```csharp +builder.Services.AddUltimateAuthClientBlazor(options => +{ + options.ClientProfile = UAuthClientProfile.BlazorServer; + options.Login.AllowCredentialPost = true; +}); +``` + +Do not enable this setting for a public client merely to simplify login implementation. + +The server still determines whether the requested authentication operation is permitted. + +## 7. PKCE Configuration + +`options.Pkce` controls client-side PKCE login behavior. + +PKCE allows a public client to complete an authorization flow without relying on a confidential client secret. + +The current options are: + +| Property | Default | Description | +|---|---|---| +| `Enabled` | `true` | Enable client PKCE support | +| `ReturnUrl` | `null` | Post-login return destination | +| `AutoRedirect` | `true` | Automatically navigate after beginning PKCE | +| `OnAuthorized` | `null` | Callback after the authorization response is issued | + +### Basic Configuration + +```csharp +builder.Services.AddUltimateAuthClientBlazor(options => +{ + options.Pkce.Enabled = true; + options.Pkce.ReturnUrl = "/dashboard"; + options.Pkce.AutoRedirect = true; +}); +``` + +### Disable Automatic Redirect + +```csharp +builder.Services.AddUltimateAuthClientBlazor(options => +{ + options.Pkce.AutoRedirect = false; +}); +``` + +When `AutoRedirect` is disabled, `BeginPkceAsync` does not automatically navigate to the next location. + +The application is responsible for handling navigation. + +This can be useful when integrating UltimateAuth into an existing routing or navigation system. + +### Authorization Callback + +```csharp +builder.Services.AddUltimateAuthClientBlazor(options => +{ + options.Pkce.OnAuthorized = response => + { + // Inspect or process the authorization response. + return Task.CompletedTask; + }; +}); +``` + +The callback receives: + +```csharp +PkceAuthorizeResponse +``` + +It runs after the authorization response is issued and before the normal Hub navigation. + +Use the callback for application-specific integration, not to bypass PKCE verification. + +### Client and Server PKCE Settings Are Different + +Client configuration: + +```csharp +options.Pkce.AutoRedirect = true; +``` + +Server configuration: + +```csharp +options.Pkce.AuthorizationCodeLifetimeSeconds = 120; +``` + +The client controls how the flow is initiated and navigated. + +The server controls the authorization code's validity and verification. + +## 8. Return URL Configuration + +UltimateAuth supports several return URL configuration points. + +| Property | Purpose | +|---|---| +| `DefaultReturnUrl` | Global default for interactive authentication | +| `Login.ReturnUrl` | Login-specific default | +| `Pkce.ReturnUrl` | PKCE-specific default | + +### Global Default + +```csharp +builder.Services.AddUltimateAuthClientBlazor(options => +{ + options.DefaultReturnUrl = "/dashboard"; +}); +``` + +### Flow-Specific Defaults + +```csharp +builder.Services.AddUltimateAuthClientBlazor(options => +{ + options.DefaultReturnUrl = "/"; + options.Login.ReturnUrl = "/account"; + options.Pkce.ReturnUrl = "/dashboard"; +}); +``` + +Flow-specific values allow different authentication paths to use different destinations. + +An explicitly supplied return URL for an individual operation may take precedence over these defaults. + +### Security Considerations + +Return URLs are navigation destinations and must be treated carefully. + +Do not redirect users to arbitrary external URLs without appropriate validation. + +Open redirects can be abused for phishing and other attacks. + +Prefer local application routes unless cross-origin navigation is an intentional and validated part of the authentication architecture. + +## 9. Automatic Refresh + +`options.AutoRefresh` controls background refresh coordination. + +The available properties are: + +| Property | Description | +|---|---| +| `Enabled` | Enables or disables automatic background refresh | +| `Interval` | Interval between background refresh attempts | + +### Configure Automatic Refresh + +```csharp +builder.Services.AddUltimateAuthClientBlazor(options => +{ + options.AutoRefresh.Enabled = true; + options.AutoRefresh.Interval = TimeSpan.FromMinutes(5); +}); +``` + +### Disable Automatic Refresh + +```csharp +builder.Services.AddUltimateAuthClientBlazor(options => +{ + options.AutoRefresh.Enabled = false; +}); +``` + +### Default Interval + +If `Interval` is not supplied, the client registration applies: + +```csharp +options.AutoRefresh.Interval ??= TimeSpan.FromMinutes(5); +``` + +The resulting default interval is five minutes. + +### Platform Behavior + +The effective `Enabled` setting should be considered together with the platform's post-configuration and session coordination behavior. + +The current Blazor session coordinator checks `AutoRefresh.Enabled` before starting its background loop. + +When enabled, it periodically invokes: + +```csharp +UAuthClient.Flows.RefreshAsync(true); +``` + +### Automatic Refresh Is Not a Security Policy + +Automatic refresh does not control: + +- Server session lifetime. +- Refresh token lifetime. +- Session revocation. +- Credential recovery policies. +- Refresh token replay protection. + +These remain server-side responsibilities. + +### Automatic Refresh Does Not Guarantee Session Continuity + +Background refresh may fail because: + +- The session has expired. +- The refresh credential has been revoked. +- The browser is suspended. +- Network connectivity is unavailable. +- The server requires reauthentication. + +Automatic refresh is a convenience and continuity mechanism, not a guarantee that authentication remains valid indefinitely. + +### Coordinator Lifecycle + +The Blazor adapter provides an `ISessionCoordinator` implementation. + +The coordinator must be started by the application's supported initialization lifecycle. + +Setting `AutoRefresh.Enabled = true` does not mean that a background task automatically runs without the coordinator being started. + +Applications should follow the client initialization guidance for their hosting model. + +## 10. Reauthentication Options + +`options.Reauth` controls how the Blazor session coordinator responds when refresh reports that reauthentication is required. + +The available properties are: + +| Property | Default | +|---|---| +| `Behavior` | `ReauthBehavior.Redirect` | +| `RedirectPath` | `null` | + +### Available Behaviors + +```csharp +ReauthBehavior.Redirect +ReauthBehavior.None +ReauthBehavior.RaiseEvent +``` + +### Redirect + +```csharp +builder.Services.AddUltimateAuthClientBlazor(options => +{ + options.Reauth.Behavior = ReauthBehavior.Redirect; + options.Reauth.RedirectPath = "/login"; +}); +``` + +When the coordinator receives a `ReauthRequired` refresh outcome, it navigates to the configured redirect path. + +If `RedirectPath` is null, the coordinator falls back to `Endpoints.Login`. + +The current Blazor coordinator uses forced navigation for this redirect. + +### Raise an Event + +```csharp +builder.Services.AddUltimateAuthClientBlazor(options => +{ + options.Reauth.Behavior = ReauthBehavior.RaiseEvent; +}); +``` + +This allows an application to handle reauthentication through the coordinator's `ReauthRequired` event. + +For example, an application may display a reauthentication dialog rather than immediately navigating. + +### No Automatic Action + +```csharp +builder.Services.AddUltimateAuthClientBlazor(options => +{ + options.Reauth.Behavior = ReauthBehavior.None; +}); +``` + +With `None`, the coordinator does not automatically redirect or raise its reauthentication event. + +The application must still handle unauthenticated states appropriately. + +### Important + +Reauthentication behavior does not make an expired or revoked session valid. + +It only controls how the client responds when authentication must be reestablished. + +## 11. Multi-Tenant Client Configuration + +`options.MultiTenant` controls tenant propagation from the client to the authentication server. + +The client does not independently authorize or resolve tenant ownership. + +It carries tenant context using the configured transport. + +### Available Properties + +| Property | Default | Description | +|---|---|---| +| `Enabled` | `false` | Enable tenant propagation | +| `Tenant` | `null` | Tenant identifier to propagate | +| `Transport` | `TenantTransport.None` | Tenant transport mechanism | + +### Available Transports + +```csharp +TenantTransport.None +TenantTransport.Header +TenantTransport.Route +``` + +### Header-Based Tenant Propagation + +```csharp +builder.Services.AddUltimateAuthClientBlazor(options => +{ + options.MultiTenant.Enabled = true; + options.MultiTenant.Tenant = "tenant-a"; + options.MultiTenant.Transport = TenantTransport.Header; +}); +``` + +The server must be configured to resolve tenant information from the corresponding transport. + +For example: + +```csharp +builder.Services.AddUltimateAuthServer(options => +{ + options.MultiTenant.Enabled = true; + options.MultiTenant.EnableHeader = true; +}); +``` + +### Route-Based Tenant Propagation + +```csharp +builder.Services.AddUltimateAuthClientBlazor(options => +{ + options.MultiTenant.Enabled = true; + options.MultiTenant.Tenant = "tenant-a"; + options.MultiTenant.Transport = TenantTransport.Route; +}); +``` + +The server must expose routes compatible with tenant-based routing. + +### Security Boundary + +A client-provided tenant identifier is not proof of tenant membership. + +The server must validate the resolved tenant and enforce tenant isolation. + +Never rely on client-side tenant selection as an authorization boundary. + +## 12. Authentication State Events + +`options.StateEvents` controls how the client processes authentication-related state events. + +The available property is: + +```csharp +options.StateEvents.HandlingMode +``` + +Its default is: + +```csharp +UAuthStateEventHandlingMode.Patch +``` + +### Available Modes + +| Mode | Behavior | +|---|---| +| `Patch` | Apply a local authentication state update | +| `Validate` | Revalidate authentication state after the event | +| `None` | Do not update authentication state in response to the event | + +### Patch + +```csharp +builder.Services.AddUltimateAuthClientBlazor(options => +{ + options.StateEvents.HandlingMode = UAuthStateEventHandlingMode.Patch; +}); +``` + +`Patch` is the default. + +It allows the client to update local state without performing a full authentication validation for every event. + +### Validate + +```csharp +builder.Services.AddUltimateAuthClientBlazor(options => +{ + options.StateEvents.HandlingMode = UAuthStateEventHandlingMode.Validate; +}); +``` + +`Validate` causes the client to revalidate authentication state in response to events. + +This may introduce additional requests but can be appropriate when the application prefers server-confirmed state updates. + +### None + +```csharp +builder.Services.AddUltimateAuthClientBlazor(options => +{ + options.StateEvents.HandlingMode = UAuthStateEventHandlingMode.None; +}); +``` + +`None` disables automatic authentication state updates in response to these events. + +Applications using this mode must provide any required state synchronization through other mechanisms. + +### Local Events Are Not Distributed Notifications + +Client state events do not automatically notify every browser, device, or application instance. + +For example, an administrator changing a user's role does not automatically push an event to that user's open browser. + +Distributed synchronization requires additional application infrastructure. + +## 13. Configuration from appsettings.json + +UltimateAuth Client binds the configuration section: + +```text +UltimateAuth:Client +``` + +For example: + +```json +{ + "UltimateAuth": { + "Client": { + "AutoDetectClientProfile": true, + "DefaultReturnUrl": "/dashboard", + "Endpoints": { + "BasePath": "https://auth.example.com/auth" + }, + "Pkce": { + "Enabled": true, + "ReturnUrl": "/dashboard", + "AutoRedirect": true + }, + "AutoRefresh": { + "Enabled": true, + "Interval": "00:05:00" + }, + "Reauth": { + "Behavior": "Redirect", + "RedirectPath": "/login" + }, + "StateEvents": { + "HandlingMode": "Patch" + } + } + } +} +``` + +The example uses the standard ASP.NET Core configuration binding conventions. + +### Configuration Precedence + +The registration pipeline applies: + +1. Option type defaults. +2. The configuration delegate. +3. Values from `UltimateAuth:Client`. +4. Post-configuration processing. +5. Registered options validation when the options are resolved. + +For example: + +```csharp +builder.Services.AddUltimateAuthClientBlazor(options => +{ + options.DefaultReturnUrl = "/"; +}); +``` + +With: + +```json +{ + "UltimateAuth": { + "Client": { + "DefaultReturnUrl": "/dashboard" + } + } +} +``` + +The configured return URL becomes `/dashboard`. + +### Environment Variables + +ASP.NET Core supports double underscores for hierarchical configuration keys. + +For example: + +```text +UltimateAuth__Client__Endpoints__BasePath=https://auth.example.com/auth +``` + +This corresponds to: + +```text +UltimateAuth:Client:Endpoints:BasePath +``` + +Use environment-specific configuration for deployment-dependent values such as authentication server addresses and application return paths. + +## 14. Client Options Validation + +UltimateAuth registers validators for: + +```csharp +UAuthClientOptions +UAuthClientEndpointOptions +``` + +### Client Profile Validation + +The validator rejects a configuration where: + +```csharp +options.AutoDetectClientProfile = false; +options.ClientProfile = UAuthClientProfile.NotSpecified; +``` + +An explicit profile is required when detection is disabled. + +### Endpoint Validation + +The following endpoint properties must be present: + +```text +Endpoints.BasePath +Endpoints.Login +Endpoints.Logout +Endpoints.Refresh +Endpoints.Validate +``` + +For example, this configuration is invalid: + +```csharp +builder.Services.AddUltimateAuthClientBlazor(options => +{ + options.Endpoints.BasePath = ""; +}); +``` + +### Validation Timing + +Unlike the server registration, the current client registration does not explicitly call `ValidateOnStart()`. + +Validation therefore occurs through the normal options resolution lifecycle. + +Applications that require early validation can arrange for the configured options to be resolved during startup. + +### Validation Scope + +The current validators check essential client profile and endpoint requirements. + +They do not automatically validate every possible combination of client flow, server deployment, cookie behavior, and authentication mode. + +Applications should test their actual authentication flows against the deployed server configuration. + +## 15. Complete Blazor WebAssembly Example + +The following example configures a Blazor WebAssembly client using a remote authentication server. + +```csharp +builder.Services.AddUltimateAuthClientBlazor(options => +{ + // Client profile + options.AutoDetectClientProfile = false; + options.ClientProfile = UAuthClientProfile.BlazorWasm; + + // Authentication server + options.Endpoints.BasePath = "https://auth.example.com/auth"; + + // Navigation + options.DefaultReturnUrl = "/"; + options.Login.ReturnUrl = "/dashboard"; + + // Public client security + options.Login.AllowCredentialPost = false; + + // PKCE + options.Pkce.Enabled = true; + options.Pkce.ReturnUrl = "/dashboard"; + options.Pkce.AutoRedirect = true; + + // Background refresh + options.AutoRefresh.Enabled = false; + + // Reauthentication + options.Reauth.Behavior = ReauthBehavior.Redirect; + options.Reauth.RedirectPath = "/login"; + + // State updates + options.StateEvents.HandlingMode = UAuthStateEventHandlingMode.Patch; +}); +``` + +This example emphasizes PKCE-based authentication and avoids direct credential posting from a public browser client. + +The remote server must be configured with compatible endpoints, origins, authentication modes, and credential delivery policies. + +## 16. Complete Blazor Server Example + +The following example configures a Blazor Server client. + +```csharp +builder.Services.AddUltimateAuthClientBlazor(options => +{ + // Client profile + options.AutoDetectClientProfile = false; + options.ClientProfile = UAuthClientProfile.BlazorServer; + + // Same-origin authentication endpoints + options.Endpoints.BasePath = "/auth"; + + // Navigation + options.DefaultReturnUrl = "/"; + options.Login.ReturnUrl = "/dashboard"; + + // Background refresh + options.AutoRefresh.Enabled = true; + options.AutoRefresh.Interval = TimeSpan.FromMinutes(5); + + // Reauthentication + options.Reauth.Behavior = ReauthBehavior.Redirect; + options.Reauth.RedirectPath = "/login"; + + // Authentication state + options.StateEvents.HandlingMode = UAuthStateEventHandlingMode.Patch; +}); +``` + +Blazor Server applications can use session-oriented authentication and the corresponding server-side security policies. + +The application must still initialize the client and session coordinator through its supported lifecycle. + +## 17. Server and Client Configuration Together + +An application that hosts both UltimateAuth Server and the Blazor client adapter can configure them independently. + +```csharp +// Server: authentication and security policies +builder.Services.AddUltimateAuthServer(options => +{ + options.AllowedModes = + [ + UAuthMode.PureOpaque, + UAuthMode.Hybrid + ]; + + options.Session.Lifetime = TimeSpan.FromDays(7); + options.Login.MaxFailedAttempts = 5; +}); + +// Client: platform integration and flow behavior +builder.Services.AddUltimateAuthClientBlazor(options => +{ + options.ClientProfile = UAuthClientProfile.BlazorServer; + options.AutoDetectClientProfile = false; + options.Endpoints.BasePath = "/auth"; + options.AutoRefresh.Enabled = true; +}); +``` + +The two configuration objects have different responsibilities. + +| Requirement | Configure on | +|---|---| +| Session lifetime | Server | +| Access token lifetime | Server | +| Refresh token replay policy | Server | +| Credential recovery rules | Server | +| Allowed authentication modes | Server | +| Client profile | Client | +| PKCE navigation | Client | +| Automatic background refresh | Client | +| Reauthentication navigation | Client | +| Authentication state event handling | Client | +| Tenant validation | Server | +| Tenant propagation | Client | + +The client cannot override server-side security decisions. + +## 18. Common Configuration Mistakes + +### Registering the Core Client and Blazor Adapter Separately + +Use `AddUltimateAuthClientBlazor()` for Blazor applications. + +It already registers the underlying client SDK. + +### Disabling Profile Detection Without Selecting a Profile + +When `AutoDetectClientProfile` is false, set `ClientProfile` explicitly. + +### Relying on Automatic Detection in an Ambiguous Hosting Environment + +For complex hosting arrangements, consider specifying the client profile explicitly. + +### Enabling Direct Credential Posting in a Public Client + +Do not enable `AllowCredentialPost` for browser-hosted public clients. + +Use PKCE. + +### Assuming Client Auto-Refresh Extends Server Lifetimes + +Automatic refresh only initiates refresh operations. + +The server controls whether refresh is permitted. + +### Assuming Auto-Refresh Starts Automatically + +The Blazor session coordinator must be started through the supported application lifecycle. + +### Configuring Only the Client Endpoint + +The server must expose compatible routes and allow the intended cross-origin interaction. + +### Treating Tenant Propagation as Authorization + +A tenant identifier supplied by the client does not grant access to that tenant. + +### Using State Events as Distributed Notifications + +Client events update local client state; they do not automatically synchronize all connected devices. + +### Assuming Every Client Setting Is Validated at Startup + +The current client registration validates options when they are resolved, rather than explicitly requiring startup validation. + +## 19. Client Options Quick Reference + +| Option | Default | Description | +|---|---|---| +| `ClientProfile` | `NotSpecified` | Client environment | +| `AutoDetectClientProfile` | `true` | Automatic profile detection | +| `DefaultReturnUrl` | `null` | Global return URL | +| `Endpoints.BasePath` | `/auth` | Authentication endpoint base | +| `Login.ReturnUrl` | `null` | Login return URL | +| `Login.AllowCredentialPost` | `false` | Direct credential posting | +| `Pkce.Enabled` | `true` | PKCE flow support | +| `Pkce.ReturnUrl` | `null` | PKCE return URL | +| `Pkce.AutoRedirect` | `true` | Automatic PKCE navigation | +| `Pkce.OnAuthorized` | `null` | PKCE authorization callback | +| `AutoRefresh.Interval` | 5 minutes after post-configuration | Background refresh interval | +| `Reauth.Behavior` | `Redirect` | Reauthentication handling | +| `Reauth.RedirectPath` | `null` | Reauthentication redirect | +| `MultiTenant.Enabled` | `false` | Tenant propagation | +| `MultiTenant.Tenant` | `null` | Tenant identifier | +| `MultiTenant.Transport` | `None` | Tenant transport | +| `StateEvents.HandlingMode` | `Patch` | State event handling | + +`AutoRefresh.Enabled` should be evaluated with the applicable platform and post-configuration behavior rather than inferred solely from its property initializer. -## ➑️ Next Step +## Next Steps -Continue to **Configuration Sources** +- **Configuration Overview** β€” Understand the configuration architecture and source precedence. +- **Server Options** β€” Configure authentication security, sessions, tokens, and endpoints. +- **Advanced Configuration** β€” Explore effective options, mode resolution, and extension points. +- **Client Authentication** β€” Implement login, PKCE, refresh, and logout flows. +- **Client Session Management** β€” Manage authenticated sessions and revocation. \ No newline at end of file diff --git a/docs/content/configuration/configuration-overview.md b/docs/content/configuration/configuration-overview.md deleted file mode 100644 index fe9bd7a7..00000000 --- a/docs/content/configuration/configuration-overview.md +++ /dev/null @@ -1,172 +0,0 @@ ---- -title: Configuration Overview -order: 2 -group: configuration ---- - - -# 🧠 Configuration Overview - -UltimateAuth is not configured as a static system. - -πŸ‘‰ It is configured as a **runtime-adaptive system** - -## ⚠️ A Common Misunderstanding - -Many frameworks expect you to configure authentication once: - -- Choose JWT or cookies -- Set token lifetimes -- Configure behavior globally - -πŸ‘‰ And that configuration applies everywhere - -πŸ‘‰ UltimateAuth does NOT work like this - -
- -## 🧩 Layered Configuration Model - -UltimateAuth separates configuration into distinct layers: - -### πŸ”Ή Core (Behavior Definition) - -Core defines **what authentication means**: - -- Session lifecycle -- Token behavior -- PKCE rules -- Multi-tenant handling - -πŸ‘‰ Core is the foundation - -πŸ‘‰ But you typically do NOT configure it directly - -### πŸ”Ή Server (Application Configuration) - -Server is where you configure the system: - -```csharp -builder.Services.AddUltimateAuthServer(o => -{ - o.Login.MaxFailedAttempts = 5; -}); -``` - -This layer controls: - -- Allowed authentication modes -- Endpoint exposure -- Cookie behavior -- Security policies - -πŸ‘‰ This is your main configuration surface - -### πŸ”Ή Client (Runtime Behavior) - -Client configuration controls: - -- Client profile (WASM, Server, MAUI, API) -- PKCE behavior -- Auto-refresh -- Re-authentication - -πŸ‘‰ Client influences how flows are executed - -
- -## ⚑ Runtime Configuration (The Key Idea) - -Here is the most important concept: - -πŸ‘‰ UltimateAuth does NOT use configuration as-is - -Instead, it computes **Effective Configuration per request** - -### 🧠 How It Works - -At runtime: - -1. Client profile is detected -2. Flow type is determined (Login, Refresh, etc.) -3. Auth mode is resolved -4. Defaults are applied -5. Mode-specific overrides are applied - -πŸ‘‰ This produces: - -``` -EffectiveOptions -``` - -
- -## πŸ”„ From Static to Dynamic - -``` -UAuthServerOptions (startup) - ↓ -Mode Resolver - ↓ -Apply Defaults - ↓ -Mode Overrides - ↓ -EffectiveUAuthServerOptions (runtime) -``` - -πŸ‘‰ Every request can have a different effective configuration - -
- -## 🎯 Why This Matters - -This allows UltimateAuth to: - -- Use different auth modes per client -- Adapt behavior per flow -- Enforce security dynamically -- Avoid global misconfiguration - -πŸ‘‰ You don’t configure β€œone system” - -πŸ‘‰ You configure a **decision engine** - -
- -## πŸ›‘ Safety by Design - -Even with dynamic behavior: - -- Invalid combinations fail early -- Disallowed modes are rejected -- Security invariants are enforced - -πŸ‘‰ Flexibility does not reduce safety - -
- -## βš™οΈ Core vs Effective - -| Concept | Meaning | -|-------------------|---------------------------------| -| Core Options | Base behavior definitions | -| Server Options | Application-level configuration | -| Effective Options | Runtime-resolved behavior | - -πŸ‘‰ Effective options are what actually run - -
- -## 🧠 Mental Model - -If you remember one thing: - -πŸ‘‰ You don’t configure authentication - -πŸ‘‰ You configure how it is **resolved** - -## ➑️ Next Step - -- Control runtime β†’ Server Options -- Configure clients β†’ Client Options diff --git a/docs/content/configuration/configuration-sources.md b/docs/content/configuration/configuration-sources.md deleted file mode 100644 index 3386a379..00000000 --- a/docs/content/configuration/configuration-sources.md +++ /dev/null @@ -1,166 +0,0 @@ ---- -title: Configuration Sources -order: 5 -group: configuration ---- - - -# βš™οΈ Configuration Sources - -UltimateAuth supports multiple configuration sources. - -πŸ‘‰ But more importantly, it defines a **clear and predictable precedence model** - -## 🧠 Two Ways to Configure - -UltimateAuth can be configured using: - -### πŸ”Ή Code (Program.cs) - -```csharp -builder.Services.AddUltimateAuthServer(o => -{ - o.Login.MaxFailedAttempts = 5; -}); -``` - -### πŸ”Ή Configuration Files (appsettings.json) - -```json -{ - "UltimateAuth": { - "Server": { - "Login": { - "MaxFailedAttempts": 5 - } - } - } -} -``` - -## βš–οΈ Precedence Rules - -This is the most important rule: - -πŸ‘‰ **Configuration files override code** - -Execution order: - -``` -Program.cs configuration - ↓ -appsettings.json binding - ↓ -Final options -``` - -πŸ‘‰ This means: - -- Defaults can be defined in code -- Environments can override them safely - -
- -## 🌍 Environment-Based Configuration - -ASP.NET Core supports environment-specific configuration: - -- appsettings.Development.json -- appsettings.Staging.json -- appsettings.Production.json - -Example: - -```json -{ - "UltimateAuth": { - "Server": { - "Session": { - "IdleTimeout": "7.00:00:00" - } - } - } -} -``` - -πŸ‘‰ You can use different values per environment without changing code - -
- -## 🧩 Recommended Strategy - -For real-world applications: - -### βœ” Use Program.cs for: -- Defaults -- Development setup -- Local testing - -### βœ” Use appsettings for: -- Environment-specific overrides -- Production tuning -- Deployment configuration - -πŸ‘‰ This keeps your system flexible and maintainable - -
- -## πŸ›‘ Safety & Validation - -UltimateAuth validates configuration at startup: - -- Invalid combinations are rejected -- Missing required values fail fast -- Unsafe configurations are blocked - - -πŸ‘‰ You will not run with a broken configuration - - -## ⚠️ Common Pitfalls - -### ❌ Assuming code overrides config - -It does not. - -πŸ‘‰ appsettings.json always wins - -### ❌ Hardcoding production values - -Avoid: - -```csharp -o.Token.AccessTokenLifetime = TimeSpan.FromHours(1); -``` - -πŸ‘‰ Use environment config instead for maximum flexibility - - -### ❌ Mixing environments unintentionally - -Ensure correct environment is set: - -``` -ASPNETCORE_ENVIRONMENT=Production -``` - -
- -## 🧠 Mental Model - -If you remember one thing: - -πŸ‘‰ Code defines defaults -πŸ‘‰ Configuration defines reality - -## πŸ“Œ Key Takeaways - -- UltimateAuth supports code + configuration -- appsettings.json overrides Program.cs -- Environment-based configuration is first-class -- Validation prevents unsafe setups -- Designed for real-world deployment - -## ➑️ Next Step - -Continue to **Advanced Configuration** diff --git a/docs/content/configuration/index.md b/docs/content/configuration/index.md deleted file mode 100644 index 2aff7b31..00000000 --- a/docs/content/configuration/index.md +++ /dev/null @@ -1,114 +0,0 @@ ---- -title: Configuration -order: 1 -group: configuration ---- - - -# βš™οΈ Configuration & Extensibility - -UltimateAuth is designed to be flexible. - -But flexibility without structure leads to chaos. - -πŸ‘‰ Configuration in UltimateAuth is structured, layered, and safe by default. - -## 🧠 What You Configure - -In UltimateAuth, you don’t just configure values. - -πŸ‘‰ You configure behavior. - -This includes: - -- How sessions are created and managed -- How tokens are issued and refreshed -- How tenants are resolved -- How clients interact with the system -- Which features are enabled or restricted - -
- -## 🧩 Configuration Layers - -UltimateAuth separates configuration into three layers: - -### πŸ”Ή Core - -Defines authentication behavior: - -- Session lifecycle -- Token policies -- PKCE flows -- Multi-tenancy - -### πŸ”Ή Server - -Defines runtime behavior: - -- Allowed authentication modes -- Endpoint exposure -- Cookie and transport behavior -- Hub deployment - -### πŸ”Ή Client - -Defines client-side behavior: - -- Client profile -- PKCE configuration -- Auto-refresh and re-authentication - -πŸ‘‰ These layers are independent but work together. - -## βš™οΈ Configuration Sources - -UltimateAuth supports two configuration styles: - -### Code-based (Program.cs) - -```csharp -builder.Services.AddUltimateAuthServer(o => -{ - o.Login.MaxFailedAttempts = 5; -}); -``` - -### Configuration-based (appsettings.json) -```csharp -{ - "UltimateAuth": { - "Server": { - "Login": { - "MaxFailedAttempts": 5 - } - } - } -} -``` - -πŸ‘‰ appsettings.json overrides Program.cs - -This allows: - -- Environment-based configuration -- Centralized management -- Production-safe overrides - -## πŸ›‘ Safety by Design - -UltimateAuth does not allow unsafe configurations silently. - -- Invalid combinations fail at startup -- Unsupported modes are rejected -- Security invariants are enforced - -πŸ‘‰ Flexibility is allowed -πŸ‘‰ Unsafe behavior is not - -## 🎯 What’s Next? -- Understand configuration layers β†’ Configuration Overview -- Learn Core behavior β†’ Core Options -- Customize server β†’ Server Options -- Control clients β†’ Client Options -- Go deeper β†’ Advanced Configuration diff --git a/docs/content/configuration/overview.md b/docs/content/configuration/overview.md new file mode 100644 index 00000000..30861406 --- /dev/null +++ b/docs/content/configuration/overview.md @@ -0,0 +1,842 @@ +--- +title: Configuration Overview +order: 1 +group: configuration +--- + +# Configuration & Extensibility + +UltimateAuth provides a structured configuration system for controlling authentication behavior, session lifecycles, token policies, client integration, and server security. + +Configuration is organized around the application components that actually consume it. + +For most applications, the two primary configuration types are: + +- **`UAuthServerOptions`** β€” Controls authentication and security behavior on the UltimateAuth server. +- **`UAuthClientOptions`** β€” Controls how an application client communicates with UltimateAuth and executes authentication flows. + +UltimateAuth also defines reusable options in its Core package. These types form the foundation of server configuration, but they do not need to be configured independently when using `AddUltimateAuthServer()`. + +This guide explains where configuration belongs, how options are registered, how configuration sources interact, and how to customize behavior safely. + +## 1. Configuration Architecture + +UltimateAuth separates reusable option definitions from the runtime components that consume them. + +```text +UltimateAuth +β”‚ +β”œβ”€β”€ Core +β”‚ └── Shared option definitions +β”‚ β”œβ”€β”€ Login +β”‚ β”œβ”€β”€ Session +β”‚ β”œβ”€β”€ Token +β”‚ β”œβ”€β”€ PKCE +β”‚ └── Multi-tenancy +β”‚ +β”œβ”€β”€ Server +β”‚ └── UAuthServerOptions +β”‚ β”œβ”€β”€ Core-defined options +β”‚ β”œβ”€β”€ Authentication mode restrictions +β”‚ β”œβ”€β”€ Cookie and session resolution +β”‚ β”œβ”€β”€ Endpoints +β”‚ β”œβ”€β”€ Hub configuration +β”‚ β”œβ”€β”€ Credential policies +β”‚ └── Server-specific behavior +β”‚ +└── Client + └── UAuthClientOptions + β”œβ”€β”€ Client profile + β”œβ”€β”€ Login and PKCE flows + β”œβ”€β”€ Endpoint configuration + β”œβ”€β”€ Auto-refresh + β”œβ”€β”€ Reauthentication + └── Client state events +``` + +The important distinction is between **where an option type is defined** and **where its value is consumed**. + +For example, `UAuthSessionOptions` is defined in Core, but a normal UltimateAuth Server application configures it through: + +```csharp +builder.Services.AddUltimateAuthServer(options => +{ + options.Session.Lifetime = TimeSpan.FromHours(8); +}); +``` + +The server consumes `options.Session` from `UAuthServerOptions`. + +There is no need to configure a separate Core options instance for this setting. + +## 2. Configure the Server + +The main server registration method is: + +```csharp +builder.Services.AddUltimateAuthServer(options => +{ + // Server configuration. +}); +``` + +This registers the UltimateAuth server infrastructure and its configuration. + +The method accepts an optional configuration delegate: + +```csharp +Action +``` + +It also supports a separate optional delegate for registering access policies. + +### Example: Authentication and Session Settings + +```csharp +builder.Services.AddUltimateAuthServer(options => +{ + options.Login.MaxFailedAttempts = 5; + + options.Session.Lifetime = TimeSpan.FromHours(8); + + options.Token.AccessTokenLifetime = + TimeSpan.FromMinutes(15); +}); +``` + +These settings control different parts of the authentication lifecycle: + +| Option | Purpose | +|---|---| +| `Login.MaxFailedAttempts` | Maximum failed login attempts under the configured login policy | +| `Session.Lifetime` | Session lifetime | +| `Token.AccessTokenLifetime` | Access token lifetime | + +The server applies its validation rules to the resulting configuration. + +**Configuration values should be chosen together.** For example, token lifetime settings must remain compatible with the configured session lifetime limits. + +## 3. Core Options Are Composed into Server Options + +`UAuthServerOptions` includes the following Core-defined option groups: + +```csharp +public UAuthLoginOptions Login { get; set; } = new(); + +public UAuthSessionOptions Session { get; set; } = new(); + +public UAuthTokenOptions Token { get; set; } = new(); + +public UAuthPkceOptions Pkce { get; set; } = new(); + +public UAuthMultiTenantOptions MultiTenant { get; set; } = new(); +``` + +This is composition, not duplication. + +The Core package owns the shared option definitions, while the Server package uses those definitions as part of its effective server configuration. + +### Recommended + +```csharp +builder.Services.AddUltimateAuthServer(options => +{ + options.Session.Lifetime = TimeSpan.FromHours(8); + options.Pkce.Enabled = true; +}); +``` + +### Not Recommended for Server Configuration + +```csharp +builder.Services.AddUltimateAuth(options => +{ + options.Session.Lifetime = TimeSpan.FromHours(8); +}); + +builder.Services.AddUltimateAuthServer(); +``` + +The second example configures `UAuthOptions`, not the `UAuthServerOptions` instance consumed by the server's authentication runtime. + +**When using UltimateAuth Server, configure Core-defined behavior through `AddUltimateAuthServer()` or the `UltimateAuth:Server` configuration section.** + +Direct Core configuration is intended for scenarios that explicitly consume `UAuthOptions` rather than the server configuration model. + +## 4. Server Configuration Groups + +`UAuthServerOptions` organizes server behavior into focused option groups. + +| Property | Responsibility | +|---|---| +| `Login` | Login attempts and authentication behavior | +| `Session` | Session lifetime and lifecycle | +| `Token` | Token issuance, lifetimes, and refresh behavior | +| `Pkce` | PKCE flow configuration | +| `MultiTenant` | Tenant resolution and multi-tenant behavior | +| `AllowedModes` | Restricts permitted authentication modes | +| `HubDeploymentMode` | Hub deployment arrangement | +| `Hub` | Hub-specific configuration | +| `Cookie` | Authentication cookie behavior | +| `SessionResolution` | Resolving session identifiers from incoming requests | +| `Endpoints` | Server endpoint configuration | +| `CredentialSessionPolicy` | Session handling after credential changes and recovery | +| `ResetCredential` | Credential reset configuration | +| `PrimaryCredential` | Primary credential policy | +| `Identifiers` | Identifier management behavior | +| `IdentifierValidation` | Identifier validation rules | +| `LoginIdentifiers` | Identifier types permitted for login | +| `UserProfile` | User profile behavior | +| `Pagination` | Pagination limits and defaults | +| `AuthResponse` | Authentication response behavior | +| `Navigation` | Navigation and redirect behavior | +| `Diagnostics` | Diagnostic configuration | +| `Events` | Server event configuration | + +These groups allow applications to customize specific behavior without replacing the overall authentication architecture. + +The individual option types and their defaults are documented in [Server Options](/docs/configuration/server-options). + +## 5. Configure the Client + +UltimateAuth clients have their own configuration model. + +```csharp +builder.Services.AddUltimateAuthClient(options => +{ + options.AutoDetectClientProfile = true; + options.DefaultReturnUrl = "/"; +}); +``` + +The registration accepts: + +```csharp +Action +``` + +The client options control how the client behaves, not the server's security policy. + +For example: + +```csharp +builder.Services.AddUltimateAuthClient(options => +{ + options.AutoRefresh.Enabled = true; + options.DefaultReturnUrl = "/dashboard"; +}); +``` + +Enabling client auto-refresh does not change the server's refresh token lifetime, session expiration rules, or rotation policy. + +Those are server-side responsibilities. + +### Client Configuration Groups + +| Property | Responsibility | +|---|---| +| `ClientProfile` | Explicit client profile | +| `AutoDetectClientProfile` | Automatic client profile detection | +| `DefaultReturnUrl` | Default post-authentication navigation | +| `Login` | Client login flow behavior | +| `Pkce` | Client PKCE flow behavior | +| `AutoRefresh` | Automatic refresh behavior | +| `Reauth` | Reauthentication behavior | +| `Endpoints` | Server endpoint paths used by the client | +| `MultiTenant` | Client tenant propagation | +| `StateEvents` | Client state event handling | + +See [Client Options](/docs/configuration/client-options) for the full reference. + +## 6. Server and Client Configuration Are Independent + +A project may register both UltimateAuth Server and UltimateAuth Client. + +This is common in applications that host authentication services and also use the client SDK. + +For example: + +```csharp +builder.Services.AddUltimateAuthServer(options => +{ + options.Session.Lifetime = TimeSpan.FromHours(8); +}); + +builder.Services.AddUltimateAuthClient(options => +{ + options.AutoDetectClientProfile = true; + options.DefaultReturnUrl = "/"; +}); +``` + +These registrations configure different components. + +| Setting | Server | Client | +|---|---|---| +| Session lifetime | Controls | Does not control | +| Token lifetime | Controls | Does not control | +| Credential reset policy | Controls | Does not control | +| Allowed authentication modes | Controls | Does not control | +| Client profile | Does not control | Controls | +| Client auto-refresh | Does not control | Controls | +| Client navigation | Does not control | Controls | +| Server endpoint exposure | Controls | Uses configured routes | + +The client must be compatible with the server's configuration, but it cannot override server-side security requirements. + +For example, enabling PKCE in client options does not make a disabled or unsupported PKCE server flow available. + +## 7. Configuration Sources + +UltimateAuth supports configuration through registration delegates and the standard ASP.NET Core configuration system. + +### Code-Based Configuration + +Use `Program.cs` for explicit configuration. + +```csharp +builder.Services.AddUltimateAuthServer(options => +{ + options.Login.MaxFailedAttempts = 5; + options.Session.Lifetime = TimeSpan.FromHours(8); +}); +``` + +This is useful when configuration is closely tied to application behavior or requires strongly typed values. + +### Configuration-Based Setup + +The server binds settings from: + +```text +UltimateAuth:Server +``` + +For example, in `appsettings.json`: + +```json +{ + "UltimateAuth": { + "Server": { + "Login": { + "MaxFailedAttempts": 5 + }, + "Session": { + "Lifetime": "08:00:00" + } + } + } +} +``` + +The client binds settings from: + +```text +UltimateAuth:Client +``` + +For example: + +```json +{ + "UltimateAuth": { + "Client": { + "AutoDetectClientProfile": true, + "DefaultReturnUrl": "/dashboard", + "AutoRefresh": { + "Enabled": true + } + } + } +} +``` + +These sections are independent. + +### Using Both Sources + +You can combine code-based and configuration-based settings: + +```csharp +builder.Services.AddUltimateAuthServer(options => +{ + options.Login.MaxFailedAttempts = 5; + options.Session.Lifetime = TimeSpan.FromHours(8); +}); +``` + +With: + +```json +{ + "UltimateAuth": { + "Server": { + "Login": { + "MaxFailedAttempts": 3 + } + } + } +} +``` + +The effective `Login.MaxFailedAttempts` is `3`, because the registration binds the configuration section after applying the configuration delegate. + +The session lifetime remains eight hours because the configuration section does not override it. + +## 8. Configuration Precedence + +The current registration pipeline applies options in this order: + +1. Option type defaults. +2. Values supplied by the registration delegate. +3. Values bound from the corresponding configuration section. +4. Applicable post-configuration processing. +5. Options validation. + +For the server: + +```csharp +services.AddOptions() + .Configure(options => + { + configure?.Invoke(options); + }) + .BindConfiguration("UltimateAuth:Server") + .ValidateOnStart(); +``` + +For the client, the corresponding configuration section is `UltimateAuth:Client`. + +### Important Distinction + +When we say configuration values override `Program.cs`, we mean that the **later configuration binding takes precedence for the same property**. + +The exact value also depends on the normal ASP.NET Core configuration provider order. + +For example, environment-specific configuration files and environment variables may override values from the base `appsettings.json` file. + +### Environment Variable Example + +ASP.NET Core configuration supports double underscores as section separators. + +For example: + +```text +UltimateAuth__Server__Login__MaxFailedAttempts=3 +``` + +This maps to: + +```text +UltimateAuth:Server:Login:MaxFailedAttempts +``` + +For a client setting: + +```text +UltimateAuth__Client__DefaultReturnUrl=/dashboard +``` + +This makes it possible to provide environment-specific settings without modifying application code. + +## 9. Authentication Mode Configuration + +UltimateAuth supports multiple authentication mode concepts. + +The server exposes: + +```csharp +options.AllowedModes +``` + +This setting defines which modes the server is permitted to use. + +It is a **restriction**, not a direct mode selector. + +### Restrict Allowed Modes + +```csharp +builder.Services.AddUltimateAuthServer(options => +{ + options.AllowedModes = + [ + UAuthMode.PureOpaque, + UAuthMode.Hybrid + ]; +}); +``` + +The effective authentication mode is resolved by the server's authentication mode resolution mechanism. + +Setting `AllowedModes` does not directly choose which mode every client will use. + +### Unsupported Modes + +In the current implementation, `SemiHybrid` and `PureJwt` are not yet accepted as enabled server modes. + +The server options validator rejects them when explicitly included in `AllowedModes`. + +For example, this configuration is invalid: + +```csharp +builder.Services.AddUltimateAuthServer(options => +{ + options.AllowedModes = + [ + UAuthMode.PureJwt + ]; +}); +``` + +Do not enable an unsupported mode merely because it exists in the enum. + +For supported modes and their behavior, see [Authentication Modes](/docs/fundamentals/authentication-modes). + +## 10. Configure Credential Session Policies + +UltimateAuth supports configurable session invalidation after credential changes. + +For example: + +```csharp +builder.Services.AddUltimateAuthServer(options => +{ + options.CredentialSessionPolicy.OnSelfChange = + CredentialSessionBehavior.PreserveCurrent; + + options.CredentialSessionPolicy.OnAdminChange = + CredentialSessionBehavior.RevokeAll; + + options.CredentialSessionPolicy.OnRecovery = + CredentialSessionBehavior.RevokeAll; +}); +``` + +The available behaviors are: + +| Behavior | Meaning | +|---|---| +| `PreserveAll` | Preserve existing session chains | +| `PreserveCurrent` | Preserve the current chain and revoke others | +| `RevokeAll` | Revoke all applicable session chains | + +This is an example of configuration controlling **security behavior**, rather than merely adjusting a numeric value. + +The client does not decide which sessions remain valid after a credential change. + +That decision belongs to the server. + +## 11. Configure Endpoint Exposure + +UltimateAuth allows server applications to configure endpoint groups. + +The relevant options are available through: + +```csharp +options.Endpoints +``` + +For example: + +```csharp +builder.Services.AddUltimateAuthServer(options => +{ + options.Endpoints.BasePath = "/uauth"; +}); +``` + +The client must use compatible endpoint paths. + +```csharp +builder.Services.AddUltimateAuthClient(options => +{ + options.Endpoints.BasePath = "/uauth"; +}); +``` + +The server controls which endpoints are registered and exposed. + +The client controls where it sends requests. + +Changing a client endpoint path does not create or enable a server endpoint. + +See [Server Options](/docs/configuration/server-options) and [Client Options](/docs/configuration/client-options). + +## 12. Multi-Tenant Configuration + +UltimateAuth supports tenant-aware authentication and authorization. + +The server's multi-tenant configuration is available through: + +```csharp +options.MultiTenant +``` + +The server uses this configuration to determine how tenant information is resolved. + +Supported resolution mechanisms include: + +- Route-based resolution. +- Header-based resolution. +- Host-based resolution. + +The corresponding server options include: + +```csharp +options.MultiTenant.EnableRoute; +options.MultiTenant.EnableHeader; +options.MultiTenant.EnableDomain; +``` + +For example: + +```csharp +builder.Services.AddUltimateAuthServer(options => +{ + options.MultiTenant.EnableHeader = true; +}); +``` + +The client has its own multi-tenant options for propagating tenant information to the server. + +Client-side tenant selection does not authorize cross-tenant access. + +The server remains responsible for tenant resolution, validation, and isolation. + +For more details, see [Advanced Configuration](/docs/configuration/advanced-configuration). + +## 13. Resource API Configuration + +UltimateAuth also supports a separate Resource API integration. + +Resource APIs use their own configuration model: + +```csharp +UAuthResourceApiOptions +``` + +Register a Resource API with: + +```csharp +builder.Services.AddUltimateAuthResourceApi(options => +{ + options.UAuthHubBaseUrl = "https://auth.example.com"; +}); +``` + +The configuration section is: + +```text +UltimateAuth:ResourceApi +``` + +For example: + +```json +{ + "UltimateAuth": { + "ResourceApi": { + "UAuthHubBaseUrl": "https://auth.example.com" + } + } +} +``` + +A Resource API does not become a full UltimateAuth authentication server merely by registering this integration. + +Its role is to protect application resources using the configured UltimateAuth authentication infrastructure. + +### Important: Resource API CORS Configuration + +The current Resource API registration constructs its CORS policy using the values supplied to the registration delegate. + +Therefore, when configuring `AllowedClientOrigins`, prefer supplying them directly in the registration delegate rather than assuming values bound only from `appsettings.json` will automatically populate that CORS policy. + +For example: + +```csharp +builder.Services.AddUltimateAuthResourceApi(options => +{ + options.UAuthHubBaseUrl = "https://auth.example.com"; + options.AllowedClientOrigins.Add("https://app.example.com"); +}); +``` + +Keep CORS configuration explicit and restrict allowed origins to trusted applications. + +## 14. Validation and Safe Configuration + +UltimateAuth uses ASP.NET Core options validation to reject invalid configurations. + +The server registers validators for areas including: + +- General server configuration. +- Login settings. +- Session settings. +- Token settings. +- Multi-tenancy. +- User identifiers. +- Session resolution. +- Pagination. + +For example, the server validates that: + +- The endpoint base path is specified. +- The base path does not contain invalid double slashes. +- Allowed authentication modes are valid. +- Explicitly unsupported modes are not enabled. +- Session lifetimes satisfy applicable constraints. +- Access token lifetime does not exceed the configured session maximum lifetime. + +### Example: Invalid Session Lifetime + +```csharp +builder.Services.AddUltimateAuthServer(options => +{ + options.Session.Lifetime = TimeSpan.Zero; +}); +``` + +This configuration is rejected when the applicable session lifetime validation runs. + +### Example: Invalid Endpoint Path + +```csharp +builder.Services.AddUltimateAuthServer(options => +{ + options.Endpoints.BasePath = "//invalid"; +}); +``` + +This configuration fails server options validation. + +### Validation Timing + +Server options use `ValidateOnStart()`. + +This allows configuration errors to surface during application startup instead of waiting until the first authentication request. + +Client options also have registered validators and post-configuration processing, but their validation timing follows the client options registration and resolution lifecycle. + +**Validation is an important safeguard, not a guarantee that every logically undesirable configuration is automatically detected.** + +Applications must still choose appropriate production settings and securely implement custom extensions. + +## 15. Extensibility Through Dependency Injection + +UltimateAuth uses dependency injection to support customization of selected runtime behaviors. + +Configuration controls the behavior of existing components. + +Dependency injection allows applications to supply implementations for supported extension points. + +Examples include: + +| Extension point | Purpose | +|---|---| +| `ICredentialResetNotifier` | Deliver credential recovery proofs | +| `IUserIdentifierVerifier` | Validate identifier ownership proof | +| `ITenantIdResolver` | Customize tenant identification | +| `IClock` | Provide a clock implementation | +| `IJwtSigningKeyProvider` | Supply JWT signing keys | + +For example, a credential reset notifier can be registered as: + +```csharp +builder.Services.AddScoped(); +``` + +`ApplicationCredentialResetNotifier` represents an implementation supplied by your application. + +The server's supported registration and resolution behavior must be respected when replacing services. + +Not every internal service is intended to be replaced, and custom implementations must preserve UltimateAuth's security invariants. + +### Configuration Versus Extension + +Use configuration when you want to adjust a supported behavior: + +```csharp +options.Session.Lifetime = TimeSpan.FromHours(8); +``` + +Use an extension point when your application needs to provide its own implementation: + +```csharp +builder.Services.AddScoped(); +``` + +This separation helps keep ordinary configuration simple while allowing advanced integration where necessary. + +## 16. Endpoint Customization + +The server exposes an advanced endpoint configuration hook: + +```csharp +options.OnConfigureEndpoints +``` + +The hook receives an `IEndpointRouteBuilder`. + +It is intended for advanced endpoint configuration after UltimateAuth registers its default endpoints. + +For example, an application may use the hook to apply additional endpoint metadata or filters where appropriate. + +However, it must not remove or remap UltimateAuth endpoints in ways that invalidate expected security behavior. + +Endpoint customization should be treated as an advanced integration mechanism, not a replacement for the supported endpoint configuration options. + +See [Advanced Configuration](/docs/configuration/advanced-configuration). + +## 17. Recommended Configuration Strategy + +For most applications, use the following approach. + +### Keep Server Security Settings on the Server + +Configure authentication modes, token lifetimes, session lifetimes, credential policies, and tenant resolution through `UAuthServerOptions`. + +### Keep Client Behavior in Client Options + +Configure client profiles, navigation, auto-refresh, reauthentication, and endpoint usage through `UAuthClientOptions`. + +### Use Configuration Files for Environment Differences + +Use `appsettings.json`, environment-specific configuration, and environment variables for values that differ between development, staging, and production. + +### Prefer Strongly Typed Configuration + +Use the provided option types rather than inventing custom configuration keys for features already supported by UltimateAuth. + +### Preserve Secure Defaults + +Only change security-sensitive defaults when the application's requirements justify the change. + +### Avoid Duplicate Core Configuration + +When using `AddUltimateAuthServer()`, do not separately configure Core options and expect them to override the server's composed options. + +### Validate Production Configuration + +Test startup validation and authentication flows using the effective configuration that will be deployed. + +## 18. Configuration Reference + +| Application role | Registration method | Options type | Configuration section | +|---|---|---|---| +| Authentication server | `AddUltimateAuthServer()` | `UAuthServerOptions` | `UltimateAuth:Server` | +| UltimateAuth client | `AddUltimateAuthClient()` | `UAuthClientOptions` | `UltimateAuth:Client` | +| Resource API | `AddUltimateAuthResourceApi()` | `UAuthResourceApiOptions` | `UltimateAuth:ResourceApi` | +| Core-only infrastructure | `AddUltimateAuth()` | `UAuthOptions` | `UltimateAuth:Core` | + +For normal server applications, **`UAuthServerOptions` is the effective configuration entry point for both Core-defined authentication settings and server-specific behavior**. + +The Core-only registration exists for infrastructure scenarios that explicitly consume `UAuthOptions`; it should not be treated as an additional server configuration layer. + +## Next Steps + +- [Configuration Overview](/docs/configuration/configuration-overview) β€” Understand the overall configuration model. +- [Core Options](/docs/configuration/core-options) β€” Explore the reusable option definitions composed into server configuration. +- [Server Options](/docs/configuration/server-options) β€” Configure authentication and security behavior. +- [Client Options](/docs/configuration/client-options) β€” Configure client profiles, flows, and SDK behavior. +- [Advanced Configuration](/docs/configuration/advanced-configuration) β€” Customize integration, multi-tenancy, and runtime extension points. \ No newline at end of file diff --git a/docs/content/configuration/server-options.md b/docs/content/configuration/server-options.md index 1656c29a..62f53aa7 100644 --- a/docs/content/configuration/server-options.md +++ b/docs/content/configuration/server-options.md @@ -1,214 +1,1455 @@ --- title: Server Options -order: 3 +order: 2 group: configuration --- +# Server Options -# 🧩 Server Options +`UAuthServerOptions` is the primary configuration surface for UltimateAuth Server. -UltimateAuth is configured primarily through **Server Options**. +It controls authentication behavior, session lifecycles, token issuance, credential security, tenant resolution, endpoint exposure, and integration with UAuthHub. -πŸ‘‰ This is the main entry point for configuring authentication behavior. +UltimateAuth composes its Core-defined options directly into `UAuthServerOptions`. When using UltimateAuth Server, you configure both shared authentication behavior and server-specific features through the same options object. -## 🧠 What Are Server Options? +This guide covers the available configuration groups, their defaults, practical examples, and the distinction between configured and effective runtime behavior. -Server options define how UltimateAuth behaves **inside your application**. +## 1. Register UltimateAuth Server -They control: +Configure the server through `AddUltimateAuthServer()`: -- Authentication behavior -- Security policies -- Token issuance -- Session lifecycle -- Endpoint exposure +```csharp +builder.Services.AddUltimateAuthServer(options => +{ + options.Login.MaxFailedAttempts = 5; + options.Session.Lifetime = TimeSpan.FromDays(7); + options.Token.AccessTokenLifetime = TimeSpan.FromMinutes(10); +}); +``` -
+The configuration delegate receives: -## βš™οΈ Basic Usage +```csharp +UAuthServerOptions +``` -You configure server options in `Program.cs`: +The server registration also supports an optional policy configuration delegate. ```csharp -builder.Services.AddUltimateAuthServer(o => -{ - o.Login.MaxFailedAttempts = 5; - o.Session.IdleTimeout = TimeSpan.FromDays(7); -}); +builder.Services.AddUltimateAuthServer( + configure: options => + { + options.Login.MaxFailedAttempts = 5; + }, + configurePolicies: policies => + { + // Register application access policies. + }); ``` ---- +The server automatically registers its Core infrastructure. You do not need to call `AddUltimateAuth()` separately. + +### Configuration from appsettings.json + +UltimateAuth Server binds the configuration section: + +```text +UltimateAuth:Server +``` -You can also use `appsettings.json`: +For example: ```json { "UltimateAuth": { "Server": { "Login": { - "MaxFailedAttempts": 5 + "MaxFailedAttempts": 5, + "LockoutDuration": "00:15:00" }, "Session": { - "IdleTimeout": "07.00.00.00" + "Lifetime": "7.00:00:00" + }, + "Token": { + "AccessTokenLifetime": "00:10:00" } } } } ``` -πŸ‘‰ `appsettings.json` overrides `Program.cs` +ASP.NET Core represents `TimeSpan` values using strings such as `00:15:00` for fifteen minutes and `7.00:00:00` for seven days. + +The configuration section is bound after the registration delegate, so configuration provider values take precedence for matching properties. + +For configuration source ordering and environment-specific overrides, see [Configuration Overview](/docs/configuration/configuration). -## 🧩 Core Composition +## 2. Server Options Architecture -Server options include Core behavior: +`UAuthServerOptions` contains two categories of configuration. -- Login -- Session -- Token -- PKCE -- Multi-tenancy +### Core-defined options -πŸ‘‰ These are defined in Core +These types are defined in UltimateAuth Core and composed into the server options: -πŸ‘‰ But configured via Server +| Property | Type | Purpose | +|---|---|---| +| `Login` | `UAuthLoginOptions` | Login attempts and lockout behavior | +| `Session` | `UAuthSessionOptions` | Session lifetime and lifecycle | +| `Token` | `UAuthTokenOptions` | Access and refresh token behavior | +| `Pkce` | `UAuthPkceOptions` | PKCE authorization code lifetime | +| `MultiTenant` | `UAuthMultiTenantOptions` | Tenant identification and resolution | +| `Pagination` | `UAuthPaginationOptions` | Pagination defaults and limits | +| `Events` | `UAuthEvents` | Authentication lifecycle callbacks | -
+### Server-specific options + +| Property | Purpose | +|---|---| +| `AllowedModes` | Restrict permitted authentication modes | +| `HubDeploymentMode` | Describe the relationship between UAuthHub and the application | +| `Hub` | Hub integration and flow settings | +| `Cookie` | Cookie names, security attributes, and lifetime behavior | +| `SessionResolution` | Session identifier sources and precedence | +| `Endpoints` | Endpoint groups and disabled actions | +| `PrimaryCredential` | Primary credential selection for UI and API requests | +| `AuthResponse` | Authentication credential delivery and redirect behavior | +| `CredentialSessionPolicy` | Session invalidation after credential changes | +| `ResetCredential` | Credential recovery code and token settings | +| `Identifiers` | Identifier behavior, normalization, and uniqueness | +| `IdentifierValidation` | Username, email, and phone validation | +| `LoginIdentifiers` | Identifier types allowed for login | +| `UserProfile` | User profile capabilities | +| `Navigation` | Authentication navigation behavior | +| `Diagnostics` | Optional diagnostic response details | + +Advanced configuration hooks are also available, including `ConfigureMode()` and `OnConfigureEndpoints`. + +## 3. Login Options + +`options.Login` controls login attempt limits, lockout behavior, and related security settings. + +```csharp +builder.Services.AddUltimateAuthServer(options => +{ + options.Login.MaxFailedAttempts = 5; + options.Login.LockoutDuration = TimeSpan.FromMinutes(15); + options.Login.FailureWindow = TimeSpan.FromMinutes(15); + options.Login.ExtendLockOnFailure = false; +}); +``` + +### Available Properties + +| Property | Default | Description | +|---|---|---| +| `MaxFailedAttempts` | `10` | Failed login threshold; `0` disables lockout | +| `LockoutDuration` | 15 minutes | Lockout duration | +| `FailureWindow` | 15 minutes | Window for counting failed attempts | +| `ExtendLockOnFailure` | `false` | Extend lockout when another attempt fails during lockout | +| `IncludeFailureDetails` | `true` | Include detailed authentication failure information | +| `TryLoginDuration` | 180 seconds | Duration associated with the try-login flow | + +### Example: Restrict Login Failure Details + +```csharp +builder.Services.AddUltimateAuthServer(options => +{ + options.Login.IncludeFailureDetails = false; +}); +``` -## ⚠️ Important: You Don’t Configure Modes Directly +This setting is useful when an application does not want to expose detailed lockout information to callers. -UltimateAuth does NOT expect you to select a single auth mode. +### Important -Instead: +Lockout configuration is not equivalent to complete network-level rate limiting. -πŸ‘‰ Mode is resolved at runtime +Production deployments should also consider infrastructure protections against abusive traffic and distributed login attempts. -
+## 4. Session Options -## πŸ›‘ Allowed Modes (Guardrail) +`options.Session` controls session lifetime, expiration, sliding behavior, and session-chain limits. ```csharp -o.AllowedModes = new[] +builder.Services.AddUltimateAuthServer(options => { - UAuthMode.Hybrid, - UAuthMode.PureOpaque -}; + options.Session.Lifetime = TimeSpan.FromDays(7); + options.Session.SlidingExpiration = true; + options.Session.IdleTimeout = TimeSpan.FromDays(3); + options.Session.MaxLifetime = TimeSpan.FromDays(30); +}); ``` -πŸ‘‰ This does NOT select a mode -πŸ‘‰ It restricts which modes are allowed +### Session Lifetime Properties -If a resolved mode is not allowed: +| Property | Default | Description | +|---|---|---| +| `Lifetime` | 7 days | Standard session lifetime | +| `MaxLifetime` | `null` | Optional absolute lifetime limit | +| `SlidingExpiration` | `true` | Allow eligible activity or refresh operations to extend expiration | +| `IdleTimeout` | `null` | Optional inactivity timeout | +| `TouchInterval` | 5 minutes | Minimum interval between last-seen updates | -πŸ‘‰ Request fails early +These settings have different purposes. -
+`Lifetime` determines the standard lifetime assigned to a session. -## ⚑ Runtime Behavior (Effective Options) +`MaxLifetime` establishes an optional hard boundary that cannot be extended indefinitely. -Server options are not used directly. +`IdleTimeout` limits how long a session can remain inactive. -They are transformed into: +`TouchInterval` reduces unnecessary persistence operations caused by frequent activity updates. -πŸ‘‰ `EffectiveUAuthServerOptions` +### Session-Chain Properties -This happens per request: +| Property | Default | Description | +|---|---|---| +| `MaxChainsPerUser` | `0` | Reserved user-level chain limit | +| `MaxSessionsPerChain` | `100` | Session-chain rotation limit | +| `MaxChainsPerPlatform` | `null` | Reserved platform-specific chain limits | +| `PlatformCategories` | `null` | Reserved platform group definitions | +| `MaxChainsPerCategory` | `null` | Reserved category-specific chain limits | +| `EnableIpBinding` | `false` | Reserved IP binding setting | +| `EnableUserAgentBinding` | `false` | Reserved User-Agent binding setting | +| `DeviceMismatchBehavior` | `Reject` | Reserved device mismatch behavior | -- Mode is resolved -- Defaults are applied -- Overrides are applied +**Current implementation limitation:** The source identifies several device limits and binding settings as reserved for future enforcement. Setting these properties must not be treated as proof that the corresponding protection is active. -πŸ‘‰ What actually runs is **Effective Options** +For example, do not rely on: -
+```csharp +options.Session.MaxChainsPerUser = 1; +``` -## πŸ”„ Mode-Based Defaults +as a production guarantee that only one device chain can exist unless the current runtime implementation has been verified to enforce that restriction. -Each auth mode applies different defaults automatically: +### Mode-Specific Session Defaults -- PureOpaque β†’ session-heavy -- Hybrid β†’ session + token -- PureJwt β†’ token-only +Session settings can also be affected by authentication mode defaults. +For example, `PureOpaque` supplies a longer idle timeout and touch interval when those values have not been configured. -πŸ‘‰ You don’t need to manually configure everything +`Hybrid` explicitly sets `TouchInterval` to `null`. -
+See [Advanced Configuration](/docs/configuration/advanced-configuration) for the effective-options resolution order. -## πŸŽ› Endpoint Control +## 5. Token Options -You can control which features are enabled: +`options.Token` configures access and refresh token behavior. ```csharp -o.Endpoints.Authentication = true; -o.Endpoints.Session = true; -o.Endpoints.Authorization = true; +builder.Services.AddUltimateAuthServer(options => +{ + options.Token.Issuer = "MyApplication"; + options.Token.Audience = "MyApplicationApi"; + options.Token.AccessTokenLifetime = TimeSpan.FromMinutes(10); + options.Token.RefreshTokenLifetime = TimeSpan.FromDays(7); +}); ``` +### Available Properties -You can also disable specific actions: +| Property | Default | Description | +|---|---|---| +| `IssueJwt` | `true` | Enable JWT issuance where supported by the effective mode | +| `IssueOpaque` | `true` | Enable opaque token issuance where supported | +| `IssueRefresh` | `true` | Enable refresh token issuance where supported | +| `AccessTokenLifetime` | 10 minutes | Access token lifetime | +| `RefreshTokenLifetime` | 7 days | Refresh token lifetime | +| `OpaqueIdBytes` | `32` | Random byte length used for opaque token identifiers | +| `RefreshTokenConcurrentRequestWindow` | 2 seconds | Concurrent duplicate refresh classification window | +| `Issuer` | `UAuth` | JWT issuer | +| `Audience` | `UAuthClient` | JWT audience | +| `AddJwtIdClaim` | `false` | Include a JWT identifier claim | +| `KeyId` | `null` | Optional signing key identifier | + +### Refresh Token Concurrency Window ```csharp -o.Endpoints.DisabledActions.Add("UAuthActions.Users.Create.Anonymous"); +builder.Services.AddUltimateAuthServer(options => +{ + options.Token.RefreshTokenConcurrentRequestWindow = TimeSpan.FromSeconds(2); +}); ``` -πŸ‘‰ Useful for API hardening +This option controls how the server distinguishes a closely timed duplicate refresh request from a confirmed replay. + +A duplicate refresh request is still rejected. + +The window affects the replay-response classification, including whether the token family should be revoked. + +It does **not** allow the same consumed refresh token to be used successfully more than once. -
+### Mode Defaults May Override Token Values -## πŸͺ Cookie & Transport Behavior +The effective `Hybrid` configuration explicitly sets: -Server options define how credentials are transported: +```csharp +Token.AccessTokenLifetime = TimeSpan.FromMinutes(10); +Token.RefreshTokenLifetime = TimeSpan.FromDays(7); +``` -- Cookies -- Headers -- Tokens +Therefore, a different value configured globally may be replaced when the effective mode is resolved. -πŸ‘‰ Unsafe combinations are rejected at startup +To customize the lifetime for `Hybrid`, use: + +```csharp +builder.Services.AddUltimateAuthServer(options => +{ + options.ConfigureMode(UAuthMode.Hybrid, mode => + { + mode.Token.AccessTokenLifetime = TimeSpan.FromMinutes(5); + }); +}); +``` -
+This distinction is essential when configuring token lifetimes. -## 🌐 Hub Configuration +## 6. PKCE Options -If using UAuthHub: +`options.Pkce` configures the lifetime of PKCE authorization codes. ```csharp -o.HubDeploymentMode = UAuthHubDeploymentMode.Integrated; +builder.Services.AddUltimateAuthServer(options => +{ + options.Pkce.AuthorizationCodeLifetimeSeconds = 120; +}); ``` -πŸ‘‰ Defines how auth server is deployed +| Property | Default | Description | +|---|---|---| +| `AuthorizationCodeLifetimeSeconds` | `120` | Lifetime of a PKCE authorization code | + +PKCE is particularly important for browser-based clients such as Blazor WebAssembly. + +A shorter authorization code lifetime reduces the period during which an intercepted or exposed code could potentially be used. + +However, the value must remain long enough for legitimate authorization exchanges to complete. -
+The server's PKCE configuration is separate from the client's PKCE flow settings. -## πŸ” Session Resolution +## 7. Authentication Modes -Controls how session IDs are extracted: +UltimateAuth supports authentication mode resolution based on the client profile. -- Cookie -- Header -- Bearer -- Query +The server exposes: -πŸ‘‰ Fully configurable +```csharp +options.AllowedModes +``` -
+This property restricts the modes the server is permitted to use. -## 🧠 Mental Model +### Restrict Allowed Modes + +```csharp +builder.Services.AddUltimateAuthServer(options => +{ + options.AllowedModes = + [ + UAuthMode.PureOpaque, + UAuthMode.Hybrid + ]; +}); +``` -If you remember one thing: +`AllowedModes` is a restriction, not a mode selector. -πŸ‘‰ Server options define **what is allowed** -πŸ‘‰ Runtime determines **what is used** +The effective mode is resolved by `IEffectiveAuthModeResolver`. -## πŸ“Œ Key Takeaways +### Default Mode Resolution -- Server options are the main configuration entry -- Core behavior is configured via server -- Modes are not selected manually -- Effective options are computed per request -- Security is enforced by design +| Client profile | Default resolved mode | +|---|---| +| `BlazorServer` | `PureOpaque` | +| `WebServer` | `Hybrid` | +| `BlazorWasm` | `Hybrid` | +| `Maui` | `Hybrid` | +| `Api` | `PureJwt` | ---- +The current implementation has mode templates for `PureOpaque`, `Hybrid`, `SemiHybrid`, and `PureJwt`. + +However, `SemiHybrid` and `PureJwt` are not yet accepted as enabled modes by the current server options validator. + +**Do not enable an unsupported mode merely because it exists in the enum or has a mode-default template.** + +The default `Api` profile mapping to `PureJwt` is a current compatibility limitation to consider when deploying API-profile authentication flows. + +### Configure Mode-Specific Behavior + +Use `ConfigureMode()` for behavior that should differ by mode. + +```csharp +builder.Services.AddUltimateAuthServer(options => +{ + options.ConfigureMode(UAuthMode.PureOpaque, mode => + { + mode.Session.IdleTimeout = TimeSpan.FromDays(7); + }); + + options.ConfigureMode(UAuthMode.Hybrid, mode => + { + mode.Token.AccessTokenLifetime = TimeSpan.FromMinutes(5); + }); +}); +``` + +The mode override runs after the built-in mode defaults. + +See [Advanced Configuration](/docs/configuration/advanced-configuration) for details. + +## 8. Endpoint Options + +`options.Endpoints` controls endpoint paths, feature groups, and individual action restrictions. + +### Base Path + +The default server endpoint base path is: + +```text +/auth +``` + +Configure it through: + +```csharp +builder.Services.AddUltimateAuthServer(options => +{ + options.Endpoints.BasePath = "/auth"; +}); +``` + +Changing the base path can help avoid route conflicts when integrating UltimateAuth into an existing application. + +The client must use compatible endpoint paths. + +### Endpoint Groups + +| Property | Default | Purpose | +|---|---|---| +| `Authentication` | `true` | Authentication endpoints | +| `Pkce` | `true` | PKCE endpoints | +| `Session` | `true` | Session management endpoints | +| `UserLifecycle` | `true` | User lifecycle endpoints | +| `UserProfile` | `true` | User profile endpoints | +| `UserIdentifier` | `true` | Identifier management endpoints | +| `Credentials` | `true` | Credential management endpoints | +| `Authorization` | `true` | Authorization endpoints | + +For example: + +```csharp +builder.Services.AddUltimateAuthServer(options => +{ + options.Endpoints.Authentication = true; + options.Endpoints.Session = true; + options.Endpoints.Authorization = false; +}); +``` + +Disabling an endpoint group controls endpoint exposure. It does not automatically remove the underlying domain services or invalidate previously issued credentials. + +### Disable Individual Actions + +`DisabledActions` is a collection of action identifiers: + +```csharp +builder.Services.AddUltimateAuthServer(options => +{ + options.Endpoints.DisabledActions.Add("users.create.anonymous"); +}); +``` + +The string must match the action identifier used by the server's endpoint registration and action policy. + +Do not pass a C# constant name as a string unless that exact text is also the registered action value. + +For example, these are not necessarily equivalent: + +```csharp +"UAuthActions.Users.Create.Anonymous" +``` + +and: + +```csharp +"users.create.anonymous" +``` + +Use the actual action value defined by the corresponding `UAuthActions` constant. + +### Endpoint Hardening + +Disable endpoints that are not required by the application. + +For example, an application that delegates user provisioning to administrators may choose to disable anonymous user creation. + +Endpoint exposure is a separate concern from authorization: an exposed endpoint must still enforce the applicable security policy. + +## 9. Session Resolution + +`options.SessionResolution` controls how incoming requests provide session identifiers. + +UltimateAuth supports four resolution sources: + +- Bearer credentials. +- Custom headers. +- Cookies. +- Query parameters. + +### Default Configuration + +| Property | Default | +|---|---| +| `EnableBearer` | `true` | +| `EnableHeader` | `true` | +| `EnableCookie` | `true` | +| `EnableQuery` | `true` | +| `HeaderName` | `X-UAuth-Session` | +| `QueryParameterName` | `session_id` | + +The default precedence order is: + +```text +Bearer +Header +Cookie +Query +``` + +### Configure Resolution Sources + +```csharp +builder.Services.AddUltimateAuthServer(options => +{ + options.SessionResolution.EnableBearer = true; + options.SessionResolution.EnableHeader = false; + options.SessionResolution.EnableCookie = true; + options.SessionResolution.EnableQuery = false; +}); +``` + +This example disables custom-header and query-string session resolution. + +### Change Resolution Order + +```csharp +builder.Services.AddUltimateAuthServer(options => +{ + options.SessionResolution.Order = + [ + "Cookie", + "Bearer", + "Header", + "Query" + ]; +}); +``` + +Resolution order matters when a request contains multiple candidate credentials. + +The selected credential must still pass authentication and session validation. + +### Security Recommendation + +Avoid query-string credentials unless the application has a specific, well-understood requirement. + +URLs may be recorded in browser history, reverse-proxy logs, analytics systems, and other infrastructure. + +For most browser and API integrations, cookies or authorization headers are preferable. + +## 10. Cookie Configuration + +`options.Cookie` controls cookies used for authentication credentials. + +UltimateAuth defines three cookie groups: + +```csharp +options.Cookie.Session +options.Cookie.AccessToken +options.Cookie.RefreshToken +``` + +### Default Cookie Names + +| Cookie | Default name | +|---|---| +| Session | `uas` | +| Access token | `uat` | +| Refresh token | `uar` | + +The default cookie options use: + +- `HttpOnly = true` +- `SecurePolicy = CookieSecurePolicy.Always` +- `Path = "/"` +- `Domain = null` +- `MaxAge = null` + +`SameSite` is nullable and can be resolved through the applicable cookie and deployment policies. + +### Customize Cookie Names + +```csharp +builder.Services.AddUltimateAuthServer(options => +{ + options.Cookie.Session.Name = "myapp_session"; + options.Cookie.RefreshToken.Name = "myapp_refresh"; +}); +``` + +### Configure Cookie Scope + +```csharp +builder.Services.AddUltimateAuthServer(options => +{ + options.Cookie.Session.Path = "/"; + options.Cookie.Session.Domain = null; +}); +``` + +A null domain produces a host-only cookie. + +Setting a parent domain can broaden cookie availability to subdomains and should be done only when necessary. + +### Cookie Security + +Avoid disabling `HttpOnly` or relaxing secure transport requirements for authentication cookies. + +Cookie settings interact with: + +- The deployment mode. +- Cross-site requests. +- Browser cookie policies. +- Credential delivery configuration. +- HTTPS requirements. + +For cross-site deployments, configure cookie behavior together with CORS and the relevant browser security requirements. + +## 11. Hub Deployment Mode + +`options.HubDeploymentMode` describes how UAuthHub is deployed relative to the application. + +```csharp +builder.Services.AddUltimateAuthServer(options => +{ + options.HubDeploymentMode = UAuthHubDeploymentMode.Embedded; +}); +``` + +### Available Modes + +| Mode | Description | +|---|---| +| `Embedded` | Hub and application share the same origin | +| `Integrated` | Hub is separate but within the same-site boundary | +| `External` | Hub is hosted on a different site or domain | + +The default is: + +```csharp +UAuthHubDeploymentMode.Integrated +``` + +### Embedded + +Suitable for applications hosting UltimateAuth within the same application and origin. + +```csharp +options.HubDeploymentMode = UAuthHubDeploymentMode.Embedded; +``` + +### Integrated + +Suitable for deployments such as: + +```text +auth.example.com +app.example.com +``` + +where the authentication service and application are separate origins within the same site. + +### External + +Suitable for a separate authentication provider domain. + +```csharp +options.HubDeploymentMode = UAuthHubDeploymentMode.External; +``` + +External deployment requires careful coordination of cookie policies, redirects, allowed origins, and browser restrictions. + +The deployment mode influences authentication transport behavior; it is not merely descriptive metadata. + +## 12. Hub Options + +`options.Hub` configures UAuthHub integration. + +### Available Properties + +| Property | Default | Description | +|---|---|---| +| `ClientBaseAddress` | `null` | Optional client application base address | +| `AllowedClientOrigins` | Empty set | Allowed client origins | +| `FlowLifetime` | 5 minutes | Lifetime of Hub flow artifacts | +| `LoginPath` | `/login` | Hub login page path | + +### Example + +```csharp +builder.Services.AddUltimateAuthServer(options => +{ + options.Hub.ClientBaseAddress = "https://app.example.com"; + options.Hub.AllowedClientOrigins.Add("https://app.example.com"); + options.Hub.FlowLifetime = TimeSpan.FromMinutes(5); + options.Hub.LoginPath = "/login"; +}); +``` + +The Hub flow lifetime is not the same as the authenticated session lifetime. + +Hub artifacts support authentication orchestration and should generally be short-lived. + +Only trusted origins should be included in `AllowedClientOrigins`. + +## 13. Primary Credential Policy + +`options.PrimaryCredential` controls which credential kind is preferred for UI and API requests. + +The defaults are: + +| Property | Default | +|---|---| +| `Ui` | `PrimaryTokenKind.Session` | +| `Api` | `PrimaryTokenKind.AccessToken` | + +### Example + +```csharp +builder.Services.AddUltimateAuthServer(options => +{ + options.PrimaryCredential.Ui = PrimaryTokenKind.Session; + options.PrimaryCredential.Api = PrimaryTokenKind.AccessToken; +}); +``` + +This allows UltimateAuth to distinguish between session-oriented browser interactions and access-token-oriented API interactions. + +The primary credential policy does not bypass authentication validation. + +It determines which credential kind should be preferred within the applicable request-processing rules. + +## 14. Credential Session Policy + +`options.CredentialSessionPolicy` determines what happens to active session chains after credential changes. + +### Available Properties + +| Property | Default | Purpose | +|---|---|---| +| `OnSelfChange` | `PreserveCurrent` | User changes their own credential | +| `OnAdminChange` | `RevokeAll` | Administrator changes a user's credential | +| `OnRecovery` | `RevokeAll` | Credential recovery completes | + +### Example + +```csharp +builder.Services.AddUltimateAuthServer(options => +{ + options.CredentialSessionPolicy.OnSelfChange = CredentialSessionBehavior.PreserveCurrent; + options.CredentialSessionPolicy.OnAdminChange = CredentialSessionBehavior.RevokeAll; + options.CredentialSessionPolicy.OnRecovery = CredentialSessionBehavior.RevokeAll; +}); +``` + +### Available Behaviors + +| Behavior | Effect | +|---|---| +| `PreserveAll` | Keep existing session chains | +| `PreserveCurrent` | Keep the current chain and revoke other applicable chains | +| `RevokeAll` | Revoke all applicable session chains | + +For recovery flows, `PreserveCurrent` is not always meaningful because the user may not have an authenticated current session. + +The current implementation treats this situation using the more restrictive `RevokeAll` behavior. + +### Security Recommendation + +Preserving the current session after a normal password change can provide a reasonable user experience. + +Revoking all sessions after administrative credential changes or account recovery is generally more conservative. + +Choose the policy according to the application's security requirements. + +## 15. Credential Reset Options + +`options.ResetCredential` configures credential recovery proof lifetimes and attempt limits. + +### Defaults + +| Property | Default | Description | +|---|---|---| +| `TokenValidity` | 30 minutes | Reset token lifetime | +| `CodeValidity` | 10 minutes | Numeric reset code lifetime | +| `MaxAttempts` | `3` | Maximum permitted recovery attempts | +| `CodeLength` | `6` | Numeric reset code length | + +### Example + +```csharp +builder.Services.AddUltimateAuthServer(options => +{ + options.ResetCredential.TokenValidity = TimeSpan.FromMinutes(20); + options.ResetCredential.CodeValidity = TimeSpan.FromMinutes(5); + options.ResetCredential.MaxAttempts = 3; + options.ResetCredential.CodeLength = 6; +}); +``` + +`CodeLength` applies to numeric reset codes, not token-based recovery proofs. + +### Important Security Boundary + +Reset proofs must be delivered through a trusted server-side mechanism. + +Do not return recovery tokens to anonymous callers simply to simplify a client-side reset form. + +The application should provide the appropriate `ICredentialResetNotifier` implementation for supported recovery flows. + +## 16. Login Identifier Options + +`options.LoginIdentifiers` determines which identifier types can be used for login. + +The default allowed types are: + +```csharp +UserIdentifierType.Username +UserIdentifierType.Email +UserIdentifierType.Phone +``` + +### Restrict Login Identifier Types + +```csharp +builder.Services.AddUltimateAuthServer(options => +{ + options.LoginIdentifiers.AllowedTypes = + new HashSet + { + UserIdentifierType.Email + }; +}); +``` + +This configures email as the allowed login identifier type. + +### Require Verification + +```csharp +builder.Services.AddUltimateAuthServer(options => +{ + options.LoginIdentifiers.RequireVerificationForEmail = true; + options.LoginIdentifiers.RequireVerificationForPhone = true; +}); +``` + +These settings control whether the corresponding identifiers must be verified before being accepted for login. + +### Custom Resolvers + +| Property | Default | +|---|---| +| `EnableCustomResolvers` | `true` | +| `CustomResolversFirst` | `true` | + +These options control the participation and ordering of custom login identifier resolvers. + +An identifier existing on a user account does not necessarily mean it is permitted for login. + +Identifier management and login eligibility are separate concerns. + +## 17. Identifier Validation + +`options.IdentifierValidation` provides rules for usernames, email addresses, and phone numbers. + +### Username Rules + +```csharp +builder.Services.AddUltimateAuthServer(options => +{ + options.IdentifierValidation.UserName.Enabled = true; + options.IdentifierValidation.UserName.MinLength = 3; + options.IdentifierValidation.UserName.MaxLength = 32; + options.IdentifierValidation.UserName.AllowedRegex = "^[a-zA-Z0-9._-]+$"; +}); +``` + +### Default Validation Rules + +| Identifier | Enabled | Minimum length | Maximum length | +|---|---|---|---| +| Username | `true` | `3` | `64` | +| Email | `true` | `3` | `256` | +| Phone | `true` | `3` | `24` | + +Username validation additionally supports `AllowedRegex`. + +The default username expression is: + +```regex +^[a-zA-Z0-9._-]+$ +``` + +### Identifier Behavior + +`options.Identifiers` contains additional groups: + +```csharp +options.Identifiers.Behavior +options.Identifiers.Validation +options.Identifiers.Normalization +options.Identifiers.Uniqueness +``` + +These configure identifier behavior beyond the top-level validation rules. + +Identifier normalization and uniqueness are particularly important because they affect how duplicate identifiers are detected. + +Use the provided options rather than implementing inconsistent normalization independently in the client. + +## 18. User Profile Options + +`options.UserProfile` controls profile-related capabilities. + +The current option is: + +```csharp +options.UserProfile.EnableMultiProfile +``` + +Its default is `false`. + +### Enable Multiple Profiles + +```csharp +builder.Services.AddUltimateAuthServer(options => +{ + options.UserProfile.EnableMultiProfile = true; +}); +``` + +This allows applications to use UltimateAuth's multiple-profile capability where supported by the user domain. + +For example, an application may maintain separate profile contexts for different purposes. + +Multi-profile support does not create separate user identities or independent authentication accounts. + +## 19. Multi-Tenant Options + +`options.MultiTenant` controls whether tenant-aware behavior is enabled and how tenant identifiers are resolved. + +### Available Properties + +| Property | Default | Description | +|---|---|---| +| `Enabled` | `false` | Enable multi-tenant behavior | +| `NormalizeToLowercase` | `true` | Normalize tenant identifiers | +| `EnableRoute` | `false` | Resolve tenants from routes | +| `EnableHeader` | `false` | Resolve tenants from headers | +| `EnableDomain` | `false` | Resolve tenants from hosts | +| `HeaderName` | `X-Tenant` | Tenant header name | + +### Example: Header-Based Tenant Resolution + +```csharp +builder.Services.AddUltimateAuthServer(options => +{ + options.MultiTenant.Enabled = true; + options.MultiTenant.EnableHeader = true; + options.MultiTenant.HeaderName = "X-Tenant"; +}); +``` + +### Example: Domain-Based Resolution + +```csharp +builder.Services.AddUltimateAuthServer(options => +{ + options.MultiTenant.Enabled = true; + options.MultiTenant.EnableDomain = true; +}); +``` + +Multiple resolution mechanisms can be enabled. + +The server composes the configured tenant resolvers according to its resolution infrastructure. + +### Security Boundary + +Tenant identification is not tenant authorization. + +A request providing another tenant identifier must not gain access to that tenant without appropriate authorization. + +The server remains responsible for tenant isolation. + +## 20. Pagination Options + +`options.Pagination` controls pagination defaults and maximum page sizes. + +| Property | Default | +|---|---| +| `DefaultPageSize` | `250` | +| `MaxPageSize` | `1000` | + +### Example + +```csharp +builder.Services.AddUltimateAuthServer(options => +{ + options.Pagination.DefaultPageSize = 50; + options.Pagination.MaxPageSize = 200; +}); +``` + +These settings affect paginated queries exposed by UltimateAuth domain services. + +For administrative interfaces, choosing a moderate page size can reduce unnecessary data transfer and processing. + +## 21. Navigation Options + +`options.Navigation` configures automatic authentication navigation behavior. + +### Available Properties + +| Property | Default | +|---|---| +| `EnableAutomaticNavigationRedirect` | `true` | +| `LoginResolver` | `null` | +| `AccessDeniedResolver` | `null` | + +### Disable Automatic Redirects + +```csharp +builder.Services.AddUltimateAuthServer(options => +{ + options.Navigation.EnableAutomaticNavigationRedirect = false; +}); +``` + +### Custom Login Redirect + +```csharp +builder.Services.AddUltimateAuthServer(options => +{ + options.Navigation.LoginResolver = context => + { + return "/account/login"; + }; +}); +``` + +### Custom Access Denied Redirect + +```csharp +builder.Services.AddUltimateAuthServer(options => +{ + options.Navigation.AccessDeniedResolver = context => + { + return "/account/access-denied"; + }; +}); +``` + +These delegates receive the current `HttpContext`. + +The application should return valid, trusted navigation destinations. + +Avoid constructing redirects from unvalidated user-controlled URLs. + +## 22. Authentication Response Options + +`options.AuthResponse` controls how authentication credentials and redirect behavior are represented in responses. + +The available groups are: + +```csharp +options.AuthResponse.SessionIdDelivery +options.AuthResponse.AccessTokenDelivery +options.AuthResponse.RefreshTokenDelivery + +options.AuthResponse.Login +options.AuthResponse.Logout +``` + +These settings govern credential delivery and authentication response behavior. + +### Important: Mode-Specific Response Defaults + +UltimateAuth provides response templates for authentication modes. + +For example, `PureOpaque` uses a session-oriented cookie response, while `Hybrid` combines session and token delivery behavior. + +The effective response may therefore depend on the resolved authentication mode and flow. + +Do not assume that a single globally configured response delivery setting applies unchanged to every authentication mode. + +### Security Considerations + +Credential delivery settings determine whether sensitive authentication material is exposed through cookies, headers, or other response mechanisms. + +Only customize them when the application has a clear requirement and the selected transport is appropriate for the client platform. + +For advanced response customization, see [Advanced Configuration](/docs/configuration/advanced-configuration). + +## 23. Diagnostics Options + +`options.Diagnostics` provides optional diagnostic behavior. + +The current property is: + +```csharp +options.Diagnostics.EnableRefreshDetails +``` + +Its default is `false`. + +### Example + +```csharp +builder.Services.AddUltimateAuthServer(options => +{ + options.Diagnostics.EnableRefreshDetails = false; +}); +``` + +When enabled, the option allows additional refresh-related diagnostic response information, such as the `X-UAuth-Refresh` header. + +This is primarily useful during development and debugging. + +Avoid enabling detailed authentication diagnostics in production unless the exposure is understood and justified. + +## 24. Authentication Lifecycle Events + +`options.Events` exposes server-side authentication lifecycle callbacks. + +### Available Events + +```csharp +options.Events.OnAnyEvent +options.Events.OnUserLoggedIn +options.Events.OnUserLoggedOut +``` + +### Example: Login Logging + +```csharp +builder.Services.AddUltimateAuthServer(options => +{ + options.Events.OnUserLoggedIn = context => + { + // Record the successful login. + return Task.CompletedTask; + }; +}); +``` + +### Example: Global Event Handler + +```csharp +builder.Services.AddUltimateAuthServer(options => +{ + options.Events.OnAnyEvent = context => + { + // Process an authentication lifecycle event. + return Task.CompletedTask; + }; +}); +``` + +Lifecycle events are intended for lightweight reactions such as logging, metrics, and notifications. + +They should not replace security decisions, authentication validation, or application policies. + +See [Advanced Configuration](/docs/configuration/advanced-configuration) for event and extension guidance. + +## 25. Effective Server Options + +The configured `UAuthServerOptions` instance is the base configuration. + +At runtime, UltimateAuth can resolve: + +```csharp +EffectiveUAuthServerOptions +``` + +The current resolution process is: + +```text +Configured UAuthServerOptions + | + v + Resolve auth mode + | + v + Clone base options + | + v + Apply mode defaults + | + v + Apply ConfigureMode overrides + | + v + EffectiveUAuthServerOptions +``` + +This means that the effective settings used for a `Hybrid` client can differ from those used for a `PureOpaque` client. + +### Example + +```csharp +builder.Services.AddUltimateAuthServer(options => +{ + options.Session.Lifetime = TimeSpan.FromDays(7); + + options.ConfigureMode(UAuthMode.Hybrid, mode => + { + mode.Token.AccessTokenLifetime = TimeSpan.FromMinutes(5); + }); + + options.ConfigureMode(UAuthMode.PureOpaque, mode => + { + mode.Session.IdleTimeout = TimeSpan.FromDays(3); + }); +}); +``` + +The original options remain the common starting point. + +Mode defaults and overrides determine the effective values for the resolved mode. + +For implementation details and inspection of effective options, see [Advanced Configuration](/docs/configuration/advanced-configuration). + +## 26. Validation and Configuration Safety + +UltimateAuth Server registers multiple options validators and enables startup validation. + +These validators cover areas including: + +- Login settings. +- Session settings. +- Token settings. +- PKCE. +- Multi-tenancy. +- Identifier configuration. +- Session resolution. +- Pagination. +- General server configuration. + +### Example: Invalid Session Lifetime + +```csharp +builder.Services.AddUltimateAuthServer(options => +{ + options.Session.Lifetime = TimeSpan.Zero; +}); +``` + +The server rejects an invalid session lifetime under the applicable validation rules. + +### Example: Invalid Base Path + +```csharp +builder.Services.AddUltimateAuthServer(options => +{ + options.Endpoints.BasePath = "//auth"; +}); +``` + +The general server validator rejects paths containing `//`. + +### Unsupported Modes + +```csharp +builder.Services.AddUltimateAuthServer(options => +{ + options.AllowedModes = + [ + UAuthMode.PureJwt + ]; +}); +``` + +The current validator rejects explicitly enabling `PureJwt` because that mode is not yet implemented as a supported server mode. + +### Validation Limitations + +Startup validation protects the configured options according to the registered validators. + +It does not guarantee that every custom service implementation or mode-specific override is secure. + +When using advanced customization, test the resulting effective configuration and authentication flows. + +## 27. Complete Configuration Example + +The following example combines several commonly used server settings. + +```csharp +builder.Services.AddUltimateAuthServer(options => +{ + // Authentication modes + options.AllowedModes = + [ + UAuthMode.PureOpaque, + UAuthMode.Hybrid + ]; + + // Login security + options.Login.MaxFailedAttempts = 5; + options.Login.LockoutDuration = TimeSpan.FromMinutes(15); + options.Login.IncludeFailureDetails = false; + + // Sessions + options.Session.Lifetime = TimeSpan.FromDays(7); + options.Session.MaxLifetime = TimeSpan.FromDays(30); + + // Token identity + options.Token.Issuer = "MyApplication"; + options.Token.Audience = "MyApplicationApi"; + + // Hybrid-specific token lifetime + options.ConfigureMode(UAuthMode.Hybrid, mode => + { + mode.Token.AccessTokenLifetime = TimeSpan.FromMinutes(10); + }); + + // PKCE + options.Pkce.AuthorizationCodeLifetimeSeconds = 120; + + // Hub deployment + options.HubDeploymentMode = UAuthHubDeploymentMode.Integrated; + options.Hub.ClientBaseAddress = "https://app.example.com"; + options.Hub.AllowedClientOrigins.Add("https://app.example.com"); + + // Credential security + options.CredentialSessionPolicy.OnSelfChange = CredentialSessionBehavior.PreserveCurrent; + options.CredentialSessionPolicy.OnAdminChange = CredentialSessionBehavior.RevokeAll; + options.CredentialSessionPolicy.OnRecovery = CredentialSessionBehavior.RevokeAll; + + // Credential recovery + options.ResetCredential.TokenValidity = TimeSpan.FromMinutes(30); + options.ResetCredential.MaxAttempts = 3; + + // Identifier login rules + options.LoginIdentifiers.RequireVerificationForEmail = true; + + // Session transport + options.SessionResolution.EnableCookie = true; + options.SessionResolution.EnableBearer = true; + options.SessionResolution.EnableQuery = false; + + // Endpoint exposure + options.Endpoints.Authentication = true; + options.Endpoints.Session = true; + options.Endpoints.Credentials = true; + options.Endpoints.Authorization = true; + + // Pagination + options.Pagination.DefaultPageSize = 50; + options.Pagination.MaxPageSize = 200; + + // Diagnostics + options.Diagnostics.EnableRefreshDetails = false; +}); +``` + +This is an illustrative configuration, not a universal production preset. + +Applications should choose session lifetimes, authentication modes, credential transport, tenant behavior, and endpoint exposure according to their own deployment and security requirements. + +## 28. Common Configuration Mistakes + +### Configuring Core Options Separately + +When using `AddUltimateAuthServer()`, configure Core-defined options through `UAuthServerOptions`. + +A separate `UAuthOptions` configuration does not automatically override the server's composed options. + +### Assuming AllowedModes Selects the Mode + +`AllowedModes` is a restriction. + +Mode selection is performed by the authentication mode resolver. + +### Ignoring Mode Defaults + +Mode-specific defaults can replace globally configured values. + +Use `ConfigureMode()` for explicit mode-specific behavior. + +### Enabling Unsupported Modes + +An authentication mode appearing in an enum does not guarantee runtime support. + +### Treating Reserved Session Options as Enforced Security Features + +Some device and session-chain options are reserved for future enforcement. + +Verify actual runtime support before relying on them. + +### Exposing Unnecessary Endpoints + +Disable endpoint groups or actions that the application does not require. + +### Allowing Query-String Credentials Without a Specific Need + +Query-string credentials can leak through URLs and infrastructure logs. + +### Weakening Cookie Security + +Do not relax secure transport or cookie protections without understanding the consequences. + +### Assuming Configuration Validation Covers Everything + +Startup validation does not replace testing, infrastructure security, or validation of custom extension implementations. + +## 29. Server Options Quick Reference + +| Configuration group | Typical use | +|---|---| +| `Login` | Login attempts and lockout | +| `Session` | Session expiration and lifecycle | +| `Token` | Token issuance and lifetime | +| `Pkce` | PKCE code lifetime | +| `AllowedModes` | Permitted authentication modes | +| `MultiTenant` | Tenant resolution | +| `Pagination` | Query page limits | +| `Endpoints` | Endpoint exposure | +| `SessionResolution` | Credential source precedence | +| `Cookie` | Cookie security and scope | +| `HubDeploymentMode` | Hub deployment topology | +| `Hub` | Hub integration | +| `PrimaryCredential` | UI/API primary credential selection | +| `AuthResponse` | Credential delivery | +| `CredentialSessionPolicy` | Session invalidation after credential changes | +| `ResetCredential` | Credential recovery rules | +| `Identifiers` | Identifier behavior | +| `IdentifierValidation` | Identifier validation | +| `LoginIdentifiers` | Login identifier eligibility | +| `UserProfile` | Multi-profile capability | +| `Navigation` | Authentication redirects | +| `Diagnostics` | Refresh diagnostics | +| `Events` | Authentication lifecycle callbacks | +| `ConfigureMode()` | Mode-specific option overrides | +| `OnConfigureEndpoints` | Advanced endpoint customization | -## ➑️ Next Step +## Next Steps -Continue to **Client Options** +- [Configuration Overview](/docs/configuration/configuration) β€” Understand the configuration architecture and precedence. +- [Client Options](/docs/configuration/client-options) β€” Configure client SDK behavior. +- [Advanced Configuration](/docs/configuration/advanced-configuration) β€” Explore effective options, mode resolution, and extension points. +- [Session Management](/docs/client/session-management) β€” Manage sessions through the client. +- [Authentication](/docs/client/authentication) β€” Use the client authentication flows. \ No newline at end of file diff --git a/docs/content/fundamentals/auth-model.md b/docs/content/fundamentals/auth-model.md index 0616637f..7dfb4953 100644 --- a/docs/content/fundamentals/auth-model.md +++ b/docs/content/fundamentals/auth-model.md @@ -4,186 +4,405 @@ order: 2 group: fundamentals --- +# Authentication Model -# 🧠 Authentication Model +UltimateAuth is built around a fundamental principle: **authentication is a trusted process for establishing identity, not simply the presence of a token, cookie, or session identifier.** -UltimateAuth is built around a simple but powerful idea: +An authentication credential provides evidence of identity. The server must evaluate that evidence according to the active authentication mode before treating a request as authenticated. -> Authentication is not just a token. -> It is a **structured, server-controlled session model**. +UltimateAuth provides a common authentication architecture that supports both server-side sessions and cryptographically verifiable tokens. -At the core of this model are three concepts: +The authentication mode determines how identity is established, what state must be validated, and which lifecycle guarantees apply. -- **Root** -- **Chain** -- **Session** +## Authentication and identity -Together, they form what we call: +Authentication answers a specific question: -πŸ‘‰ **Authentication Lineage** +**Who is making this request, and what evidence establishes that identity?** -
+An authentication operation may involve credentials such as passwords, existing sessions, access tokens, refresh tokens, or other supported authentication proofs. -## πŸ”‘ The Big Picture +These credentials have different purposes. -Instead of treating authentication as a single token or cookie, UltimateAuth models it as a hierarchy: +A password can establish a new authenticated context. An access token can represent an already established identity. A refresh token can continue an existing authentication context without repeating the original login. +None of these credentials should be trusted merely because a client presents them. + +UltimateAuth evaluates authentication evidence through framework-controlled services and produces a trusted identity context only after the applicable validation succeeds. + +Authorization is a separate responsibility. It determines what an authenticated actor is allowed to do. + +## Server authority + +UltimateAuth is server-authoritative. + +This means the server defines the authentication rules and determines whether the presented evidence satisfies them. + +Server authority does not require every request to access a server-side session store. + +For example: + +- In PureOpaque, the server validates the session represented by the client's opaque session credential. +- In Hybrid, the server resolves an opaque access token and validates its associated session. +- In SemiHybrid, normal requests authenticate through JWT validation, while server-side sessions govern authentication continuation and lifecycle operations. +- In PureJwt, authentication can be established through JWT validation without requiring session persistence or lookup. + +In all cases, the effective authentication mode, validation requirements, and security policies remain under trusted server control. + +The client cannot establish its own authenticated identity merely by supplying claims, identifiers, or an arbitrary authentication mode. + +## Authentication state + +Authentication state can be represented in different ways. + +**Server-side authentication state** is maintained by the authentication system and evaluated when required. It enables centralized session management and immediate session-based revocation. + +**Token-based authentication state** can be represented by cryptographically verifiable credentials. A JWT can carry sufficient information to establish identity without a session lookup, provided its signature and applicable validation rules are satisfied. + +**Combined authentication state** uses both approaches for different purposes. For example, an application can validate JWTs independently while retaining server-side state for refresh and session management. + +These are different authentication strategies, not separate authentication architectures. + +UltimateAuth preserves common trust boundaries while allowing the effective mode to determine how authentication state is represented and evaluated. + +## Authentication lineage + +For session-backed authentication, UltimateAuth models authentication state through three related concepts: + +- **Root:** User-level authentication authority within a tenant. +- **Chain:** A grouping of related authentication sessions, typically associated with a device or client context. +- **Session:** An individual authenticated session. + +Together, these form the authentication lineage. + +```text +Root + | + +-- Chain A + | | + | +-- Session 1 + | | + | +-- Session 2 + | + +-- Chain B + | + +-- Session 3 ``` -Root (user authority) -β”œβ”€β”€ Chain (device context) -β”‚ β”œβ”€β”€ Session (login instance) -β”‚ β”œβ”€β”€ Session -β”‚ -β”œβ”€β”€ Chain -β”‚ β”œβ”€β”€ Session (login instance) + +The lineage provides a structured way to manage authentication state at different scopes. + +It allows the system to distinguish between terminating one session, terminating sessions associated with a particular chain, and applying a broader user-level security change. + +The lineage is central to session-backed authentication. It must not be interpreted as a mandatory persistence requirement for every authentication mode. + +In particular, PureJwt does not require Root, Chain, or Session records to authenticate requests. + +## Root: user-level authentication authority + +Root represents the user-level authority within a tenant's session-backed authentication lineage. + +It provides a common security boundary for related chains and sessions. + +Depending on the configured policies and supported operations, Root can participate in: + +- Tracking user-level authentication security state. +- Coordinating security changes affecting multiple sessions. +- Applying broader session invalidation. +- Maintaining a security version or epoch used by session-backed validation. + +For example, a security-sensitive credential change may trigger a policy that invalidates existing sessions. + +Rather than treating each session as unrelated, the lineage provides a common authority through which the affected authentication state can be managed. + +### Security versions + +A security version, sometimes called a security epoch, can be used to determine whether an existing session still belongs to the current authentication security state. + +A session may retain the version associated with its creation. When the relevant authority changes, validation can reject sessions associated with an outdated version. + +This mechanism is meaningful when authentication validation consults the relevant server-side state. + +It does not automatically invalidate a previously issued JWT that is validated without a server-side lookup. + +The exact invalidation behavior depends on the authentication mode and configured security policies. + +## Chain: related session context + +Chain groups related authentication sessions under a common lifecycle context. + +A chain commonly represents a device or client context, but its purpose is broader than storing a device identifier. + +It provides a boundary for managing related authentication activity. + +A chain can support: + +- Grouping related sessions. +- Device-oriented session management. +- Chain-level expiration and invalidation. +- Coordinating refresh-token lifecycle where applicable. +- Applying security policies to related authentication sessions. + +For example, an application may allow a user to terminate authentication associated with a particular device without terminating every session belonging to the user. + +A chain provides a suitable scope for that operation. + +A chain is not itself proof of authentication. Its validity and relationship to the relevant session must be evaluated according to the active mode. + +## Session: an authenticated instance + +Session represents an individual authenticated instance maintained by the server. + +It can contain or reference the information required to manage that instance, such as: + +- The authenticated user and tenant. +- Its relationship to a chain. +- Creation and expiration information. +- Revocation or lifecycle status. +- Applicable security-version information. +- Session metadata required by supported policies. + +The role of Session differs by authentication mode. + +### Session-authoritative authentication + +In PureOpaque and Hybrid, session validity is part of the authentication decision. + +A request cannot be treated as authenticated merely because it presents a structurally valid credential. The associated session must also satisfy the applicable validation requirements. + +In these modes, session invalidation affects subsequent authentication evaluations. + +### Session-managed authentication + +In SemiHybrid, sessions support lifecycle operations such as refresh, logout, device management, and session revocation. + +However, normal JWT authentication does not require session validation. + +Consequently, revoking a session can prevent future refresh operations without automatically invalidating an already issued JWT. + +### Session-independent authentication + +PureJwt does not require a Session record. + +Authentication relies on cryptographic token validation and applicable claims and token-validation policies. + +There is no mandatory session authority or session lookup in its authentication pipeline. + +## Establishing a new authentication context + +Authentication begins when the system receives evidence sufficient to establish a new authenticated identity. + +A typical login operation follows these conceptual steps: + +1. Resolve the effective authentication context and security policies. +2. Validate the presented authentication proof. +3. Establish the authenticated identity. +4. Create the authentication state required by the selected mode. +5. Issue the appropriate authentication credentials. +6. Deliver those credentials through the configured transport mechanism. + +The fourth and fifth steps are mode-dependent. + +For session-backed modes, establishing authentication can involve creating or resolving the relevant Root, Chain, and Session state. + +For PureJwt, issuing a JWT must not require creating a server-side session. + +The shared authentication pipeline does not imply that every mode performs identical persistence operations. + +## Authenticating subsequent requests + +After login, a client presents the credential appropriate for its authentication mode. + +The request pipeline validates that credential and establishes the trusted authentication state. + +```text +Incoming Request + | + v +Effective Authentication Context + | + v +Credential Resolution + | + v +Mode-Specific Authentication Validation + | + +-- PureOpaque: Session validation + | + +-- Hybrid: Opaque token + session validation + | + +-- SemiHybrid: JWT validation + | + +-- PureJwt: JWT validation + | + v +Authenticated Identity + | + v +AccessContext + | + v +Authorization + | + v +Protected Operation ``` -Each level has a distinct responsibility. +The authentication mode determines which validation steps are required. -
+The resulting identity must satisfy the same framework-level trust requirements regardless of the credential format. -## 🧩 Root β€” The Authority +An application must not construct a trusted identity directly from unvalidated client input. -**Root** represents the authentication authority of a user within a tenant. +## Authentication context and authorization context -- There is **only one active Root per user per tenant** -- It defines the **global security state** -- It controls all chains and sessions +UltimateAuth distinguishes the context used to execute an authentication flow from the context used to evaluate authorization. -### What Root Does +### AuthFlowContext -- Tracks security version (security epoch) -- Invalidates all sessions when needed -- Acts as the **source of truth** +`AuthFlowContext` represents the effective authentication context for an operation. -### Example +It includes the relevant mode, client characteristics, tenant context, and other applicable runtime information. -If a user changes their password: +The framework establishes this context through controlled boundaries. -πŸ‘‰ Root is updated -πŸ‘‰ All existing sessions can become invalid +Once established, the effective context remains stable throughout the operation. -
+Application code and individual domain services must not independently replace or reconstruct it to change the security rules. -## πŸ”— Chain β€” The Device Context +### AccessContext -**Chain** represents a device or client context. +`AccessContext` represents the trusted actor and authorization context after authentication has been evaluated. -- Each device typically has its own chain -- Multiple logins from the same device belong to the same chain +It provides the identity and contextual information needed for authorization and policy decisions. -πŸ‘‰ Think of Chain as: +`AccessContext` does not authenticate credentials. -> β€œWhere is the user logged in from?” +Authentication must be resolved before a trusted authorization context is established. -### What Chain Does +This separation allows the authentication mechanism to vary without requiring a different authorization architecture for each mode. -- Groups sessions by device -- Enables **device-level control** -- Allows actions like: +## Authentication lifecycle - - Logout from one device - - Revoke a specific device +Authentication does not end when credentials are issued. -
+Depending on the mode, an authenticated context may be continued, renewed, expired, or revoked. -## 🧾 Session β€” The Authentication Instance +UltimateAuth distinguishes several lifecycle operations. -**Session** is the actual authentication instance. +### Authentication -- Created when the user logs in -- Represents a single authenticated state -- Carries a snapshot of the Root security version +Establishes a new authenticated context after the required proof has been validated. -πŸ‘‰ This is what gets validated on each request. +### Refresh -### What Session Does +Continues an existing authentication context when its continuation requirements are satisfied. -- Proves the user is authenticated -- Can be refreshed, revoked, or expired -- Is tied to a specific chain +Refresh is mode-dependent. -
+PureOpaque uses session continuation and touch semantics rather than refresh-token rotation. -## πŸ”„ How They Work Together +Hybrid and SemiHybrid use refresh-token rotation together with the applicable session validation requirements. -When a user logs in: +PureJwt may support session-independent token renewal when refresh tokens are enabled. -1. Root is resolved (or created) -2. A Chain is identified (device context) -3. A new Session is created +### Logout -`Login β†’ Root β†’ Chain β†’ Session` +Ends or discards the relevant authentication context according to the mode. +In session-authoritative modes, server-side session revocation can prevent subsequent authentication. -On each request: +In JWT-based modes, removing credentials from the client does not by itself invalidate already issued JWTs. -1. Session is validated -2. Chain context is checked -3. Root security version is verified +### Revocation -πŸ‘‰ If any level is invalid β†’ authentication fails +Invalidates authentication state or credentials according to the guarantees supported by the selected mode. -
+Session revocation and JWT revocation are not equivalent operations. -## πŸ›‘ Why This Model Matters +A server-side session can be revoked immediately, but a JWT validated without session lookup can remain valid until expiration. -Traditional systems rely on: +Applications requiring stronger access-token invalidation guarantees must select a mode and validation strategy that explicitly provides them. -- Cookies -- JWT tokens -- Stateless validation +## Authentication security boundaries -These approaches have limitations: +UltimateAuth's authentication architecture relies on several principles that apply across modes. -- No real session control -- Weak revocation -- No device awareness +### Credentials are evidence, not authority ---- +The presence of a token, cookie, or session identifier does not automatically establish identity. + +The required validation must succeed first. + +### Authentication modes have explicit guarantees + +A mode must not silently weaken its validation or revocation guarantees because of client runtime, transport selection, or performance optimization. + +### Session authority is mode-dependent + +Server-side sessions are authoritative where the mode requires them. + +Stateless authentication does not become session-backed merely because other parts of the framework support session management. + +### Client context is not inherently trusted + +Device identifiers, tenant identifiers, and client-provided runtime information must be handled according to the applicable trust boundaries. + +They must not allow a client to bypass authentication requirements. + +### Authentication and authorization remain separate + +A valid identity does not automatically imply permission to access a resource. + +Authorization policies must still evaluate the authenticated actor's access rights. + +### Credential protection remains essential + +Opaque session credentials, opaque access tokens, JWTs, and refresh tokens are all sensitive. + +Their safe use depends on appropriate transport, storage, expiration, and validation policies. + +## How the model relates to authentication modes -UltimateAuth solves this by: +The authentication model establishes the shared principles and boundaries. -### βœ” Server-Controlled Authentication +Authentication modes determine how those principles are executed. -- Sessions are always validated server-side -- No blind trust in tokens +| Mode | Authentication authority | Role of session state | +|---|---|---| +| PureOpaque | Server-side session | Required for authentication and lifecycle | +| Hybrid | Server-side session, accessed through an opaque token | Required for authentication and lifecycle | +| SemiHybrid | Cryptographically validated JWT for normal requests | Required for session lifecycle, not default request validation | +| PureJwt | Cryptographically validated JWT | Not required | -### βœ” Instant Revocation +This distinction is essential to understanding UltimateAuth. -- Revoke a session β†’ immediate effect -- Revoke a chain β†’ device logged out -- Revoke root β†’ disable user (global logout) +Root, Chain, and Session provide structured lifecycle control where server-side authentication state is required. -### βœ” Device Awareness +They are not universal prerequisites for authenticating a request. -- Each device has its own chain -- Sessions are bound to context +Likewise, JWT authentication does not eliminate server authority. The server still defines which tokens are acceptable and which validation rules must be enforced. -### βœ” Strong Security Model +## Summary -- Session carries Root security version -- Old sessions automatically become invalid +UltimateAuth defines authentication as a controlled process that establishes trusted identity from validated evidence. -
+Its architecture separates: -## 🧠 Mental Model +- Authentication proof and credential validation. +- Authentication state and lifecycle management. +- Authentication context and authorization context. +- Shared security boundaries and mode-specific execution strategies. -If you remember only one thing, remember this: +Root, Chain, and Session form the authentication lineage used by session-backed modes. -πŸ‘‰ **Root = authority** -πŸ‘‰ **Chain = device** -πŸ‘‰ **Session = login** +PureOpaque and Hybrid rely on that lineage as part of their authentication authority. -## πŸ“Œ Key Takeaways +SemiHybrid retains session lifecycle management while allowing JWT authentication without request-time session lookup. -- Authentication is not just a token -- Sessions are first-class citizens -- The server always remains in control -- Device and security context are built-in +PureJwt does not require session persistence or validation. -## ➑️ Next Step +These distinctions allow UltimateAuth to support different authentication strategies without changing the fundamental trust and authorization model. -Now that you understand the core model: +## Next step -πŸ‘‰ Continue to **Flow-Based Authentication** +Continue to **Flow-Based Authentication** to understand how UltimateAuth establishes and executes authentication operations through its controlled flow pipeline. \ No newline at end of file diff --git a/docs/content/fundamentals/auth-modes.md b/docs/content/fundamentals/auth-modes.md index 756c202f..0c6dadfd 100644 --- a/docs/content/fundamentals/auth-modes.md +++ b/docs/content/fundamentals/auth-modes.md @@ -4,164 +4,597 @@ order: 4 group: fundamentals --- +# Auth Modes -> Note: SemiHybrid and PureJwt modes will be available on future releases. For now you can safely use PureOpaque and Hybrid modes. +UltimateAuth provides four authentication modes: **PureOpaque**, **Hybrid**, **SemiHybrid**, and **PureJwt**. -# πŸ” Authentication Modes +Each mode defines how authentication credentials are issued, validated, renewed, and revoked. The modes share the same authentication and authorization architecture, but differ in their reliance on server-side sessions, credential formats, and lifecycle guarantees. -UltimateAuth supports multiple authentication modes. +The choice of authentication mode affects more than performance. It determines where authentication state is maintained, whether a server-side session must be consulted during an authenticated request, and how quickly previously issued credentials can be invalidated. -Each mode represents a different balance between: +> **Availability** +> +> PureOpaque and Hybrid are supported in the initial release. SemiHybrid and PureJwt are defined by the UltimateAuth architecture but are not yet available as completed, production-supported modes. Their behavior is documented here to explain the intended authentication model. -- Security -- Performance -- Control -- Client capabilities +## Auth credentials -πŸ‘‰ You don’t always choose a single model. -UltimateAuth can adapt based on context. +UltimateAuth distinguishes three credential concepts. -
+**Session credential** -## 🧩 Available Modes +A session credential identifies an authenticated session maintained by the server. In PureOpaque, it is the credential presented by the client to authenticate requests. -### PureOpaque -Fully server-controlled session model. +A session credential may be represented by an opaque, randomly generated value. It does not need to expose the internal database identifier of the session. -### Hybrid -Combines session control with token-based access. +**Access token** -### SemiHybrid -JWT-based with server-side metadata awareness. +An access token is a credential used to authenticate requests to protected resources. -### PureJwt -Fully stateless token-based authentication. +Depending on the mode, it may be: -
+- An opaque token resolved and validated by the server. +- A JWT validated cryptographically without a session lookup. -## βš–οΈ Mode Comparison +An access token is not necessarily a session identifier. In Hybrid, the opaque access token is a separate credential associated with a server-side session. -| Feature | PureOpaque | Hybrid | SemiHybrid | PureJwt | -|----------------|------------|------------|----------------|--------------| -| SessionId | Required | Required | Metadata only | None | -| Access Token | ❌ | βœ” | βœ” | βœ” | -| Refresh Token | ❌ | βœ” | βœ” | βœ” | -| Revocation | Immediate | Immediate | Metadata-based | Not immediate| -| Statefulness | Full | Hybrid | Semi | Stateless | -| Server Control | Full | High | Medium | Low | -| Performance* | Medium | Medium | High | Highest | -| Offline Support| ❌ | Partial | βœ” | βœ” | +**Refresh token** -> ⚑ **Performance Note** -> -> All modes in UltimateAuth are designed for production use and are highly optimized. -> -> The differences here are about **trade-offs**, not absolute speed: -> -> πŸ‘‰ Even the most server-controlled mode is performant enough for real-world applications. +A refresh token allows an existing authentication context to continue by obtaining replacement credentials without requiring the user to authenticate again. + +Refresh tokens have their own validation, expiration, rotation, and replay-protection requirements. + +Not every authentication mode requires all three credential types. + +## Mode comparison + +| Characteristic | PureOpaque | Hybrid | SemiHybrid | PureJwt | +|---|---|---|---|---| +| Client authentication credential | Opaque session credential | Opaque access token | JWT access token | JWT access token | +| Separate access token | No | Yes | Yes | Yes | +| Access token format | Not applicable | Opaque | JWT | JWT | +| Refresh token | No | Yes | Yes | Optional | +| Server-side session | Required | Required | Required | Not required | +| Session lookup on authenticated requests | Required | Required | Not required by default | Not required | +| Session role | Authentication authority | Authentication authority | Lifecycle management | None | +| Access token validation | Session validation | Token and session validation | Cryptographic JWT validation | Cryptographic JWT validation | +| Session continuation | Session touch | Refresh token rotation and session policies | Refresh token rotation with session validation | Independent token renewal, if enabled | +| Immediate session revocation | Yes | Yes | Prevents continuation; does not automatically invalidate issued JWTs | Not applicable | +| Immediate access-token revocation | Through session invalidation | Through session invalidation | Requires an additional revocation check | Not supported by the pure stateless model | +| Device/session management | Yes | Yes | Yes | No session-based device management | +| Server-side state for authentication | Required | Required | Required for lifecycle, not normal JWT validation | Not required | +| Main advantage | Simple, centrally controlled sessions | Separate token lifecycle with session authority | Stateless access validation with session management | Minimal authentication-state dependencies | +| Main trade-off | Session lookup on each authenticated request | More credentials and lifecycle complexity | Existing JWTs can remain valid after session revocation | No immediate server-side session revocation | + +The comparison describes authentication behavior, not guaranteed throughput rankings. Actual performance depends on token validation costs, storage infrastructure, caching, network latency, request volume, and application architecture. + +## PureOpaque + +### Fully session-based authentication + +PureOpaque is UltimateAuth's simplest stateful authentication mode. + +Authentication is represented by a server-side session. The client receives an opaque session credential and presents it on subsequent authenticated requests. + +The server resolves the credential, validates the session and its applicable security policies, and establishes the authenticated identity. + +```text +Client + | + | Session Credential + v +Session Resolution + | + v +Session Validation + | + v +Authenticated Identity + | + v +AccessContext +``` + +PureOpaque does not issue a separate access token or refresh token. + +The session credential itself is sufficient to identify the authentication context, provided the server determines that the session remains valid. + +### Session continuation + +PureOpaque does not use refresh-token rotation. + +Instead, an existing session may continue through session touch operations, subject to the configured session policies. + +Depending on the configuration, these policies may include: + +- Session lifetime. +- Sliding expiration. +- Idle timeout. +- Touch interval. +- Absolute expiration limits. +- Session revocation. + +A touch operation does not necessarily extend the session lifetime. It updates session state only when the applicable policy requires it. + +An expired or revoked session cannot be restored merely by touching it. + +### Advantages + +**Minimal credential complexity** + +There is no separate access-token lifecycle, refresh-token storage, or refresh-token rotation. + +**Immediate session revocation** + +When a session is revoked, subsequent authentication evaluations that validate that session reject the credential. + +**Centralized authentication control** + +The server remains authoritative for session validity and can enforce current session policies. + +**Natural integration with server-rendered applications** + +Applications already maintaining server-side authentication state can use a single session credential without introducing additional token-management mechanisms. + +### Limitations + +**Session validation is required** + +Every authenticated request must validate the server-side session. Depending on the deployment, this can involve a database or distributed cache lookup. + +**Availability depends on session infrastructure** + +A distributed deployment needs reliable access to shared session state or an equivalent session-validation mechanism. + +**The session credential is sensitive** + +Possession of a valid session credential may be sufficient to authenticate as the session owner. It must be protected against disclosure, replay, and session fixation. + +**Long-lived connections require special handling** + +In environments such as Blazor Server, an established SignalR circuit may outlive the authentication state initially used to establish it. Appropriate revalidation and authorization checks remain necessary. + +### Recommended applications + +PureOpaque is generally a strong default for: + +- Blazor Server applications. +- ASP.NET Core MVC and Razor Pages applications. +- Internal applications using centrally managed sessions. +- Browser applications using secure, HttpOnly session cookies. +- Systems that prioritize simple session lifecycle management and immediate server-side revocation. + +PureOpaque is not restricted to server-to-server communication. It can also be used safely with browser and mobile clients when credentials are stored, transported, and validated appropriately. + +## Hybrid + +### Opaque access tokens with authoritative sessions + +Hybrid combines separate access and refresh tokens with mandatory server-side session validation. + +Unlike PureOpaque, the client does not use the session credential as its primary authentication credential. + +Instead, the client receives an **opaque access token** associated with a server-side session. + +The access token contains no readable JWT claims. The server resolves the token and validates the associated session before establishing the authenticated identity. + +```text +Client + | + | Opaque Access Token + v +Access Token Resolution + | + v +Associated Session + | + v +Session Validation + | + v +Authenticated Identity + | + v +AccessContext +``` + +The opaque access token and internal session identifier represent different concepts. + +The access token is a client-facing authentication credential with its own lifetime. The session represents the server-authoritative authentication state. + +### Refresh and session lifecycle + +Hybrid uses refresh tokens to continue authentication without requiring a new login. + +A successful refresh validates the relevant session and refresh-token state, applies the configured session policies, and issues replacement credentials. + +Access-token lifetime and session lifetime can therefore be managed separately. + +For example, an application may use a short-lived opaque access token while maintaining a longer-lived session, subject to refresh-token expiration and revocation rules. + +A valid refresh token must not be allowed to restore an expired or revoked session. + +### Advantages + +**Separate credential lifetimes** + +Access tokens can expire independently of the underlying session. + +**Immediate session-based revocation** + +Because session validity is enforced during authentication, revoking the session prevents associated access tokens from authenticating on subsequent validations. + +**Opaque access credentials** + +The access token does not disclose identity claims, roles, tenant information, or other application data through a readable token payload. + +**Centralized security policies** + +Session status and related security conditions can be enforced as part of authentication. + +**Suitable for API-oriented clients** + +Applications can use access and refresh tokens while retaining server-side control over the authenticated session. + +### Limitations + +**More complex lifecycle** + +Hybrid requires access-token issuance, refresh-token management, rotation, replay protection, and session lifecycle coordination. + +**Server-side validation remains mandatory** + +Opaque access tokens do not provide stateless JWT validation. The server must resolve the token and enforce the associated session state. + +**Additional infrastructure dependencies** + +Token resolution and session validation may require storage or cache access. + +**Credential theft remains a risk** + +Opaque tokens conceal claims but remain bearer credentials. Their format does not prevent unauthorized use if they are stolen. + +### Recommended applications + +Hybrid is generally appropriate for: + +- Blazor WebAssembly applications communicating with an API. +- Web applications with a separate backend API. +- Mobile and desktop applications requiring refresh-token continuity. +- Full-stack applications needing centrally controlled authentication. +- Systems requiring separate access-token expiration and immediate session revocation. + +Hybrid is particularly useful when the application benefits from access/refresh token semantics but does not want to sacrifice server-authoritative session validation. + +## SemiHybrid + +### Stateless JWT validation with server-side session lifecycle + +SemiHybrid combines JWT access tokens with server-side session management. + +Its defining characteristic is the separation between **access-token validation** and **session lifecycle operations**. + +For normal authenticated requests, the server validates the JWT cryptographically. A session lookup is not required by default. + +```text +Client + | + | JWT Access Token + v +JWT Validation + | + v +Authenticated Identity + | + v +AccessContext +``` + +The server-side session is not consulted during this normal authentication path. + +However, the session remains relevant when the authentication lifecycle must be managed. + +### What session metadata is used for + +SemiHybrid maintains server-side session state for operations such as: + +- Refresh-token validation and rotation. +- Logout and session termination. +- Revoking an individual session. +- Revoking all sessions belonging to a user. +- Listing active sessions or devices. +- Enforcing session expiration and continuation policies. +- Recording session lifecycle information. + +This state is a **lifecycle control mechanism**, not a mandatory source of identity validation on every API request. + +For example, when a user logs out, the server can revoke the session and prevent subsequent refresh operations from continuing it. + +However, a previously issued JWT may remain valid until its expiration because normal JWT validation does not consult that session. + +### Revocation semantics + +SemiHybrid must distinguish two separate guarantees. + +**Session revocation** + +Revoking the session prevents authentication continuation through that session, including refresh operations that require it. + +**Access-token revocation** + +Revoking the session does not automatically invalidate an already issued JWT. + +Immediate rejection of an issued JWT requires an additional mechanism, such as a revocation lookup against a distributed cache. + +Such a mechanism can be supported as an explicit policy, but it introduces an additional request-time dependency and validation cost. + +Without that policy, JWT expiration remains the boundary for an already issued access token. + +### Advantages + +**Stateless normal request validation** + +Protected API requests can authenticate JWTs without consulting the session store. + +**Centralized lifecycle management** + +Applications retain the ability to manage sessions, devices, refresh operations, and future authentication continuity. + +**Reduced request-time storage dependencies** + +Session infrastructure is not part of the default JWT validation path. + +**Configurable revocation trade-offs** + +Applications can choose whether to introduce additional request-time revocation enforcement when stronger invalidation guarantees are required. + +### Limitations + +**Session revocation does not automatically revoke issued JWTs** + +An access token can remain valid after logout until its expiration unless an additional revocation mechanism is enforced. + +**Lifecycle state is still required** + +SemiHybrid is not a session-free mode. Refresh and session management depend on server-side state. + +**More complex expiration semantics** + +JWT expiration, refresh-token expiration, session expiration, and session activity policies must be coordinated. + +**Optional revocation checks reduce statelessness** + +If immediate access-token revocation is enabled, the request pipeline may need an additional cache or store lookup. + +### Recommended applications + +SemiHybrid is intended for: + +- High-throughput APIs that still require centralized session management. +- Distributed applications using short-lived JWT access tokens. +- Applications offering device and active-session management. +- Systems that need JWT validation performance without giving up refresh and session lifecycle control. +- Architectures where access-token expiration is an acceptable default revocation boundary. + +SemiHybrid should not be described as providing immediate JWT revocation unless an additional enforcement mechanism is enabled. + +## PureJwt + +### Session-independent JWT authentication + +PureJwt is UltimateAuth's fully stateless JWT authentication mode. + +It does not require server-side session creation, persistence, resolution, or validation. + +The client receives a JWT access token. Protected resources validate the token using its signature and applicable validation rules, including issuer, audience, expiration, and other security-relevant claims. + +```text +Client + | + | JWT Access Token + v +Cryptographic JWT Validation + | + v +Authenticated Identity + | + v +AccessContext +``` + +No session lookup is involved. + +PureJwt must not require session infrastructure indirectly through token issuance, validation, or other authentication pipeline components. + +### Token renewal + +PureJwt can operate without refresh tokens. + +When refresh tokens are enabled, their lifecycle must remain independent of server-side sessions. + +Secure refresh-token rotation and replay protection may require dedicated refresh-token state. This does not imply a dependency on the session model. + +The distinction is important: + +- **Session independence** means that authentication does not require server-side sessions. +- **Stateless access validation** means that protected requests do not require server-side token or session lookups. +- **Refresh-token persistence**, when enabled, is a separate lifecycle concern. + +An application seeking a completely stateless credential lifecycle can disable refresh tokens and require reauthentication when access tokens expire. + +### Revocation semantics + +PureJwt does not provide session-based immediate revocation. + +A correctly signed, unexpired JWT remains valid under the configured validation rules unless a relevant validation condition changes, such as the availability of its signing key. + +PureJwt does not maintain a mandatory blacklist or server-side session revocation mechanism. + +Applications requiring immediate per-session access revocation should consider Hybrid, or SemiHybrid with an explicit revocation enforcement strategy. + +### Advantages + +**No mandatory session infrastructure** + +Authentication does not depend on session stores or session-management services. + +**Independent resource validation** + +Services with the necessary verification keys and validation configuration can validate access tokens locally. + +**Suitable for distributed architectures** + +Protected services do not need to contact a central authentication session store for each request. + +**Reduced lifecycle complexity when refresh is disabled** + +Applications can avoid session management and refresh-token rotation entirely. + +**Predictable validation behavior** + +Authentication is determined by the token and the applicable cryptographic and claim-validation rules. + +### Limitations + +**No immediate per-session revocation** + +An issued JWT can remain usable until expiration. + +**Token lifetime becomes particularly important** + +Long-lived access tokens increase the period during which a stolen token may remain usable. + +**Claims can become stale** + +Identity or authorization-related claims embedded in a JWT do not automatically change when server-side user data changes. + +**JWT payloads are readable** + +Signed JWTs are not encrypted by default. Sensitive information should not be placed in their payloads. + +**Key management is critical** + +Signing-key protection, key rotation, issuer and audience validation, and accepted algorithm configuration are essential. + +### Recommended applications + +PureJwt is intended for: + +- Stateless resource APIs. +- Distributed services and microservices. +- External API integrations. +- Workloads requiring independent JWT validation. +- Systems that do not require centralized session management. +- Applications prioritizing minimal authentication-state dependencies. + +PureJwt is not automatically the fastest mode in every deployment. Cryptographic validation has a cost, while well-cached stateful authentication can also be efficient. Its primary architectural advantage is eliminating mandatory session dependencies. + +## Choosing a mode + +The most useful selection criterion is not the client technology alone, but the authentication guarantees the application requires. + +| Application or requirement | Recommended mode | Reason | +|---|---|---| +| Blazor Server | PureOpaque | Server-controlled sessions without a separate access/refresh token lifecycle | +| ASP.NET Core MVC or Razor Pages | PureOpaque | Simple cookie-based session authentication | +| Internal administrative application | PureOpaque or Hybrid | Centralized session control and immediate revocation | +| Blazor WebAssembly with a separate API | Hybrid | Access/refresh tokens with authoritative session validation | +| Mobile or desktop client | Hybrid | Independent token lifetimes with centralized session control | +| Web application with separate frontend and API | Hybrid | Token-based access without losing server-side revocation | +| High-throughput API with session management | SemiHybrid | Stateless JWT validation alongside managed session lifecycle | +| Multi-device application with session controls | SemiHybrid or Hybrid | Centralized device/session management; different access-validation costs | +| Distributed resource APIs | PureJwt | Independent JWT validation without mandatory session lookup | +| External service integration | PureJwt | Stateless token validation across service boundaries | +| Immediate access revocation is essential | PureOpaque or Hybrid | Every authentication evaluation enforces server-side session validity | +| No session infrastructure is desired | PureJwt | Authentication does not require session persistence | + +These are recommendations, not restrictions. + +For example, Blazor WebAssembly can use PureOpaque through a secure cookie-based backend-for-frontend architecture. Similarly, a server-rendered application can use JWT authentication when its broader system requirements justify it. + +The runtime does not inherently determine the security model. + +## Client Profiles and authentication modes + +UltimateAuth supports different client environments through Client Profiles. + +A Client Profile can provide suitable defaults for a runtime, including credential delivery and authentication-mode selection. + +However, a Client Profile and an authentication mode represent different concerns. + +**Client Profile** + +Describes how the application interacts with the authentication system, including runtime characteristics and credential transport. + +**Authentication Mode** -
+Defines the authentication credential model, session dependencies, validation strategy, and lifecycle guarantees. -## 🧠 How to Think About Auth Modes -It’s important to understand that authentication modes in UltimateAuth are not rigid system-wide choices. +A Blazor Server profile may default to PureOpaque, while a Blazor WebAssembly profile may default to Hybrid. -πŸ‘‰ You are not expected to pick a single mode and use it everywhere. +These defaults do not mean the modes are exclusive to those environments. -Instead: +Applications can override the effective mode through supported configuration and context-resolution mechanisms. -- Different clients can use different modes on a single UAuthHub -- The mode can change **per request** -- UltimateAuth selects the most appropriate mode based on **Client Profile and runtime context** +When different clients share the same UAuthHub, the server must resolve the effective mode through trusted configuration and framework-controlled context. An untrusted client must not be allowed to select a weaker authentication policy arbitrarily. -
+The effective mode must remain stable throughout a single authentication operation. -### πŸ”„ Runtime-Driven Behavior -In a typical application: +## Security considerations -- Blazor Server β†’ PureOpaque -- Blazor WASM β†’ Hybrid -- API β†’ PureJwt +All four modes can support secure applications when implemented and configured correctly. None should be described as universally more secure than the others. -πŸ‘‰ All can coexist in the same system. +Their security guarantees differ. -You don’t split your architecture β€” UltimateAuth adapts automatically. +### Credential confidentiality -### βš™οΈ You Can Override Everything -UltimateAuth provides **safe defaults**, but nothing is locked. +Opaque credentials do not expose readable claims, but possession of a valid bearer credential may still permit authentication. -You can: +JWT signatures protect integrity and authenticity; they do not conceal the payload. -- Force a specific auth mode -- Customize behavior per client -- Replace default strategies +All credentials require appropriate transport and storage protections. -πŸ‘‰ The system is designed to be flexible without sacrificing safety. +### Revocation guarantees -### πŸ›‘ Safe by Default -The comparison table shows trade-offs β€” not risks. +PureOpaque and Hybrid enforce server-side session validity during authenticated requests. -- All modes are **valid and supported** -- Choosing a different mode will not β€œbreak” security -- Incompatible configurations will **fail fast** +SemiHybrid enforces session validity during lifecycle operations but does not require it during normal JWT validation. -πŸ‘‰ You are always operating within a safe boundary. +PureJwt does not depend on sessions and does not provide session-based revocation. -### πŸ’‘ Mental Model +These differences must be considered when selecting token lifetimes and logout behavior. -Think of auth modes as: +### Browser security -> Different execution strategies β€” not different systems. +When authentication credentials are delivered through cookies, appropriate `HttpOnly`, `Secure`, and `SameSite` settings should be applied. -UltimateAuth remains consistent. -Only the **behavior adapts**. +Cookie-based authentication also requires appropriate CSRF protections. -
+When tokens are accessible to browser application code, XSS and token-exfiltration risks require particular attention. -## πŸ” PureOpaque -- Fully session-based -- Every request validated on server -- Maximum security -- Touch semantics instead of refresh rotation +The authentication mode does not eliminate these browser security requirements. -πŸ‘‰ Best for: +### Authorization remains separate -- Blazor Server -- Internal apps +Authentication establishes the identity of the current actor. -## ⚑ Hybrid -- Access token as opaque session id -- Refresh token with rotate semantics -- Server control with API performance +Authorization determines what that actor is permitted to do. -πŸ‘‰ Best for: +All modes must produce trusted authentication state before authorization policies are evaluated. -- Blazor WASM -- Web + API systems -- Full-stack apps +Choosing a stateless mode does not remove the need for authorization checks or appropriate handling of stale authorization claims. -## πŸš€ SemiHybrid -- JWT-based access -- Server-side metadata control +## Summary -πŸ‘‰ Best for: +UltimateAuth's four authentication modes represent four distinct strategies: -- High-performance APIs -- Zero-trust systems +**PureOpaque** uses a server-side session as the authentication authority and avoids a separate access/refresh token lifecycle. -## 🌐 PureJwt -- Fully stateless -- No server-side session control +**Hybrid** uses separate opaque access and refresh tokens while requiring authoritative session validation for authenticated requests. -πŸ‘‰ Best for: +**SemiHybrid** uses JWT access tokens for stateless request authentication while retaining server-side sessions for refresh, logout, revocation, and device management. -- External APIs -- Microservices +**PureJwt** uses JWT access tokens without requiring server-side session infrastructure. -## 🎯 Which Mode Should You Use? +The correct mode is the one whose validation, lifecycle, revocation, and operational guarantees match the application's requirements. -| Scenario | Recommended Mode | -|------------------------|------------------| -| Blazor Server | PureOpaque | -| Web + API | Hybrid | -| High-scale API | SemiHybrid | -| External microservices | PureJwt | +UltimateAuth preserves a consistent authentication and authorization architecture while allowing these underlying strategies to differ. \ No newline at end of file diff --git a/docs/content/fundamentals/client-profiles.md b/docs/content/fundamentals/client-profiles.md index cfa72dd0..30531da9 100644 --- a/docs/content/fundamentals/client-profiles.md +++ b/docs/content/fundamentals/client-profiles.md @@ -4,148 +4,396 @@ order: 5 group: fundamentals --- +# Client Profiles -# 🧩 Client Profiles -UltimateAuth adapts its authentication behavior based on the client. +UltimateAuth is designed to support different application environments through a unified authentication architecture. -πŸ‘‰ This is powered by **Client Profiles**. +A Blazor Server application, a browser-based WebAssembly application, a native mobile client, and a server-side application do not interact with authentication credentials in exactly the same way. -
+They may differ in how credentials are stored, how requests are sent, whether browser cookies are available, and how authentication results are delivered. -## πŸ”‘ What is a Client Profile? -A Client Profile defines how authentication behaves for a specific client type. +**Client Profiles allow UltimateAuth to account for these runtime differences without requiring separate authentication architectures for each client type.** -It affects: +A Client Profile describes the environment in which a client operates. It helps the framework resolve suitable authentication behavior and defaults, while the server remains responsible for enforcing the effective security policy. -- Authentication mode -- Flow behavior -- Token usage -- Security constraints +## What is a Client Profile? -Unlike traditional systems: +A Client Profile identifies a supported client environment and its relevant runtime characteristics. -πŸ‘‰ You don’t configure authentication globally -πŸ‘‰ You let the system adapt per client +It can influence how UltimateAuth resolves: -## 🧠 The Key Idea -> Authentication behavior is not static -> It is determined **per client and per request** +- Default authentication mode. +- Credential delivery and transport. +- Client-specific authentication flow behavior. +- Browser and non-browser integration requirements. +- Applicable server configuration and compatibility constraints. -
+A profile does not authenticate the client or grant permissions. -## πŸ”„ Runtime Detection -By default, UltimateAuth automatically detects the client profile. +For example, identifying a request as originating from a Blazor WebAssembly application does not establish the identity of its user. -This is done using runtime signals such as: +Likewise, a client declaring itself to be a server-side application must not automatically receive privileges associated with a trusted server. -- Loaded assemblies -- Hosting environment -- Registered services +The distinction is fundamental: -### Example Detection Logic -- MAUI assemblies β†’ `Maui` -- WebAssembly runtime β†’ `BlazorWasm` -- Server components β†’ `BlazorServer` -- UAuthHub marker β†’ `UAuthHub` -- Fallback β†’ `WebServer` +**Client Profile describes the environment. Authentication Mode defines the authentication strategy. Server policy determines what is permitted.** -πŸ‘‰ Detection happens inside the client at startup. +## Why Client Profiles exist -
+Consider an application that includes a browser frontend, a native mobile application, and a server-rendered administrative interface. -## πŸ“‘ Client β†’ Server Propagation -The detected client profile is sent to the server on each request. +All three clients may authenticate against the same UAuthHub. -This can happen via: +However, their runtime characteristics differ. -- Request headers -- Form fields (for flow-based operations) +A browser application can use cookies managed by the browser. A native application may need to manage credentials through platform-specific secure storage. A server-rendered application may be able to maintain authentication through a server-controlled session without introducing a separate refresh-token lifecycle. -```text -Client β†’ (ClientProfile) β†’ Server +Without a client abstraction, these differences tend to spread throughout application code. + +Developers may need separate configuration paths, credential-handling logic, and authentication integration code for every runtime. + +Client Profiles provide a consistent way to describe those differences. + +They allow UltimateAuth to select appropriate integration behavior while preserving the same underlying authentication and authorization architecture. + +## Built-in Client Profiles + +UltimateAuth defines profiles for its supported client environments. + +| Profile | Environment | Typical characteristics | +|---|---|---| +| `BlazorServer` | Blazor Server applications | Server-side execution, browser connection, server-managed authentication context | +| `BlazorWasm` | Blazor WebAssembly applications | Browser execution, HTTP-based API communication, browser credential constraints | +| `Maui` | .NET MAUI applications | Native execution, platform-specific credential storage and HTTP communication | +| `WebServer` | ASP.NET Core MVC, Razor Pages, and other server-hosted web applications | Server-side execution with browser-oriented authentication | +| `Api` | API-oriented integrations | HTTP request authentication without assuming a browser UI | +| `UAuthHub` | UltimateAuth authentication host | Authentication-host-specific runtime integration | +| `NotSpecified` | No explicit profile resolved | Requires the applicable server-side fallback or resolution policy | + +These profiles describe runtime environments rather than independent authentication systems. + +For example, `BlazorWasm` does not inherently mean JWT authentication, and `BlazorServer` does not inherently prohibit access tokens. + +A profile may provide a recommended default, but the authentication mode remains a separate decision. + +### API profile versus resource API + +An API-oriented client and an API acting as a protected resource are not necessarily the same thing. + +A protected resource API may receive requests from browser applications, mobile applications, or other services. + +The calling client's profile should not be confused with the hosting environment of the resource API. + +Resource authentication must follow the applicable server-side configuration and token-validation requirements, regardless of what an untrusted caller claims its runtime to be. + +## Automatic profile detection + +UltimateAuth supports automatic Client Profile detection in compatible client integrations. + +Detection is intended to reduce configuration requirements for common application environments. + +Depending on the client implementation, the runtime can provide information such as: + +- The current execution environment. +- Available framework capabilities. +- Hosting-specific registrations. +- Runtime and platform characteristics. + +For example, a Blazor WebAssembly client can identify that it is executing in a browser environment, while a native MAUI client can identify its native application context. + +This resolution occurs within the supported client integration. + +It should not be confused with a server independently discovering the true identity or trust level of a remote application. + +### Detection is a convenience, not a security boundary + +Runtime detection helps choose appropriate defaults. + +It is not an authentication proof. + +A malicious HTTP client can imitate headers, request parameters, and other client-provided metadata. + +Consequently, the server must not grant additional authentication privileges solely because a request claims a particular Client Profile. + +The server remains responsible for validating the profile against its own configuration and security requirements. + +## Client-to-server propagation + +For supported operations, the client can communicate its resolved profile to the authentication server. + +The transport may depend on the integration and operation, including request metadata such as headers or flow-specific form fields. + +```text id="0mrrkv" +Client Runtime + | + v +Client Profile Detection + | + v +Profile Metadata + | + v +UAuthHub + | + v +Server-Side Profile Resolution + | + v +Effective Authentication Context ``` -On the server: -- The profile is read from the request -- If not provided β†’ NotSpecified -- Server applies defaults or resolves behavior +The important distinction is between a **declared profile** and an **effective profile**. + +A declared profile is information supplied by the client. + +An effective profile is the result accepted or resolved by the server under its configured rules. + +The server must not treat arbitrary client-provided profile values as authority to weaken authentication requirements. + +### Missing profile information + +Some requests may not provide an explicit Client Profile. + +In those cases, UltimateAuth can represent the unresolved client profile as `NotSpecified` and apply the relevant server-side resolution or fallback behavior. -
+A missing profile must not automatically imply a privileged or less restrictive environment. + +If the server cannot establish a permitted configuration for the operation, it should reject the operation rather than inventing an unsafe authentication context. + +## Automatic and explicit configuration + +Client Profile resolution can be automatic or explicitly configured, depending on the supported client integration. + +### Automatic configuration + +For supported Blazor client registrations, automatic detection can reduce the need to specify the runtime manually. -## βš™οΈ Automatic vs Manual Configuration -### Automatic (Default) ```csharp builder.Services.AddUltimateAuthClientBlazor(); ``` -It means: -- AutoDetectClientProfile = true -- Profile is resolved automatically -### Manual Override -You can explicitly set the client profile: +The client integration can resolve the appropriate profile according to its runtime detection configuration. + +This is generally the preferred approach when the application uses a standard, supported hosting environment. + +### Explicit configuration + +Applications can also configure the profile directly when their integration supports it. + ```csharp -builder.Services.AddUltimateAuthClientBlazor(o => +builder.Services.AddUltimateAuthClientBlazor(options => { - o.ClientProfile = UAuthClientProfile.Maui; - o.AutoDetectClientProfile = false; // optional + options.ClientProfile = UAuthClientProfile.BlazorWasm; + options.AutoDetectClientProfile = false; }); ``` -πŸ‘‰ This is useful when: -- Running in custom hosting environments -- Detection is ambiguous -- You want full control +Explicit configuration may be useful when: + +- An application uses a custom hosting environment. +- Runtime detection is ambiguous. +- A shared integration requires a specific client profile. +- The application needs predictable profile selection across environments. + +Explicitly configuring a client profile does not override the server's authentication policies. + +It only determines what profile the client integration declares or uses locally. + +The server must still resolve and enforce the effective authentication context. + +## Client Profiles and Authentication Modes + +Client Profiles and Authentication Modes are related but independent concepts. + +A Client Profile describes the execution environment and its integration requirements. + +An Authentication Mode defines how authentication credentials are issued, validated, continued, and revoked. + +For example, a Blazor Server application commonly benefits from PureOpaque because it can use server-controlled sessions without managing separate access and refresh tokens. + +A Blazor WebAssembly application communicating with a separate API may benefit from Hybrid because it can use access and refresh tokens while retaining server-authoritative session validation. + +These are architectural recommendations, not inherent limitations of the runtimes. + +### Recommended defaults + +| Client environment | Recommended mode | Reason | +|---|---|---| +| Blazor Server | PureOpaque | Simple session-based authentication without separate token rotation | +| Blazor WebAssembly with API | Hybrid | Access/refresh credentials with authoritative session control | +| .NET MAUI | Hybrid | Token lifecycle suitable for native clients | +| MVC / Razor Pages | PureOpaque | Natural integration with secure browser cookies and server-side sessions | +| API-oriented service client | PureJwt, when stateless authentication is appropriate | Independent JWT validation without mandatory session infrastructure | +| UAuthHub | Depends on its configured role and operation | Hosting the authentication system is not itself an authentication mode | + +These recommendations describe suitable architectural choices. + +They are not a guarantee that every listed mode is the current implementation default for that profile. + +In particular, the `UAuthHub` profile should not be interpreted as an authentication mode. UAuthHub is the authentication host, and its supported operations may serve clients using different modes. + +For the full comparison of validation and lifecycle guarantees, see [Authentication Modes](/docs/fundamentals/auth-modes). + +## Profile resolution and request context + +UltimateAuth can serve clients with different profiles through the same authentication infrastructure. + +For example: + +```text id="yktx8s" +Blazor Server Client + | + | Profile: BlazorServer + | + +--------------------+ + | +Blazor WASM Client | + | | + | Profile: BlazorWasm | + | v + +---------------> UAuthHub + | +MAUI Client | + | | + | Profile: Maui | + | | + +--------------------+ + | + v + AuthFlowContext + | + v + Effective Mode + Effective Policy + Response Behavior +``` + +The server resolves the effective authentication context for each applicable operation. + +This can include the Client Profile, authentication mode, tenant, security options, and credential delivery requirements. + +Different clients can therefore operate under different authentication strategies without requiring separate authentication architectures. + +### Per-request resolution does not mean arbitrary mode switching + +The effective profile and mode may differ between requests from different clients. + +However, they must remain consistent throughout an individual authentication operation. + +A request must not begin under one authentication policy and complete under another because different components interpreted its profile differently. + +Likewise, a client must not be allowed to change its declared profile to bypass validation requirements associated with an existing credential or session. + +Authentication continuation must preserve the relevant security relationships established by the original authentication context. + +The framework-controlled `AuthFlowContext` provides a consistent boundary for these decisions. + +See [Flow-Based Authentication](/docs/fundamentals/flow-based-auth) for the conceptual model and [Auth Flows](/docs/auth-flows/overview) for the technical request pipeline. + +## Credential delivery and runtime capabilities + +Different clients have different capabilities for receiving and storing authentication credentials. + +Client Profiles help the framework account for those differences. + +### Browser environments + +Browser applications can use cookies for authentication. + +When cookies carry sensitive authentication credentials, appropriate `HttpOnly`, `Secure`, and `SameSite` policies should be applied. + +Cookie-based authentication also requires appropriate CSRF protection. + +In browser environments, a credential stored in an HttpOnly cookie is not directly readable by JavaScript or WebAssembly application code, although the browser can attach it to applicable requests. + +### Native applications + +Native applications generally communicate through HTTP clients and can use platform-specific secure credential storage. + +Their credential transport and storage requirements differ from those of a browser. + +A native Client Profile can help select appropriate integration behavior, but secure storage remains an application and platform responsibility. + +### Server-hosted applications + +Server-hosted applications can manage authentication state without exposing all authentication implementation details to browser application code. + +This often makes session-oriented authentication attractive. + +However, server execution alone does not guarantee security. Cookie protection, session validation, authorization, and long-lived connection behavior must still be handled correctly. + +### Resource APIs + +Resource APIs validate the credentials presented by callers. + +Their authentication requirements must be enforced by trusted server configuration. + +A caller's declared Client Profile must not determine whether token signatures, session validity, tenant boundaries, or authorization requirements can be bypassed. + +## Security and trust boundaries + +Client Profiles must preserve a clear separation between runtime adaptation and security authority. + +### Profile declarations are not trusted identity + +A Client Profile does not prove that the caller is a particular application, device, or user. + +Additional authentication mechanisms are required when an application needs to establish client identity. + +### Mode selection is server-controlled + +A client may provide information relevant to mode resolution, but the server determines which authentication modes are permitted. + +A profile must not provide a path to downgrade authentication requirements. + +### Credential validity is independent of profile labels + +A valid-looking credential must still satisfy the validation requirements of its authentication mode. + +Changing a profile label must not turn an invalid credential into a valid one. + +### Transport configuration is not authorization + +Selecting cookies, headers, or another supported delivery mechanism does not grant access to protected resources. + +Authentication and authorization decisions remain separate from credential transport. + +### Defaults are not universal guarantees + +A secure default depends on the application's deployment, runtime, authentication mode, and security requirements. + +UltimateAuth can provide suitable defaults, but developers remain responsible for selecting and configuring the policies appropriate to their applications. + +## Custom environments and extensibility + +Not every application fits a predefined runtime category. -
+A custom host, embedded client, hybrid application, or specialized integration may need explicit profile configuration or additional integration behavior. -## 🧩 Built-in Profiles +The purpose of Client Profiles is to keep these differences at a defined architectural boundary. -UltimateAuth includes predefined profiles: -| Profile | Description | -| ------------ | ---------------------------- | -| BlazorServer | Server-rendered apps | -| BlazorWasm | Browser-based WASM apps | -| Maui | Native mobile apps | -| WebServer | MVC / Razor / generic server | -| Api | API-only backend | -| UAuthHub | Central auth server | +New integrations should reuse the existing authentication model and flow infrastructure wherever possible, rather than introducing independent authentication implementations. -### πŸ›‘ Safe Defaults -If no profile is specified (and auto detection is false): +Custom profile resolution and integration behavior must continue to respect server-controlled mode selection and authentication security policies. -- Client β†’ NotSpecified β†’ Server resolves safely -- Defaults are applied -- Unsafe combinations are prevented -- System remains consistent +## Summary -### πŸ” Why This Matters -Client Profiles enable: +Client Profiles allow UltimateAuth to adapt authentication integration to different application environments. -- Multi-client systems (Web + Mobile + API) -- Runtime adaptation -- Safe defaults without manual configuration +They provide a consistent way to describe runtime characteristics and influence suitable defaults without turning client-provided metadata into a security authority. -Without Client Profiles You would need: -- Separate auth setups per client -- Complex branching logic -- Manual security handling +The fundamental distinctions are: -## 🧠 Mental Model +**Client Profile** describes the runtime environment and integration requirements. -If you remember one thing: +**Authentication Mode** defines credential validation, session dependencies, and authentication lifecycle guarantees. -πŸ‘‰ Client defines behavior -πŸ‘‰ Server enforces rules +**AuthFlowContext** establishes the effective security context for an authentication operation. -## πŸ“Œ Key Takeaways -- Client Profiles are automatically detected -- They are sent to the server on each request -- Behavior adapts per request -- You can override everything when needed -- Safe defaults are always applied +**Server policy** determines which behavior is permitted. -## ➑️ Next Step +Together, these concepts allow different clients to share the same UltimateAuth infrastructure while preserving explicit authentication boundaries. -Now that you understand runtime behavior: +## Next step -πŸ‘‰ Continue to Runtime Architecture +Continue to [Runtime Architecture](/docs/fundamentals/runtime-architecture) to understand how UltimateAuth's client, server, and hosting components work together across different application environments. \ No newline at end of file diff --git a/docs/content/fundamentals/flow-based-auth.md b/docs/content/fundamentals/flow-based-auth.md index 6e60d06a..ec1af006 100644 --- a/docs/content/fundamentals/flow-based-auth.md +++ b/docs/content/fundamentals/flow-based-auth.md @@ -1,205 +1,335 @@ --- -title: Flow Based Authentication +title: Flow-Based Authentication order: 3 group: fundamentals --- +# Flow-Based Authentication -# πŸ”„ Flow-Based Authentication -UltimateAuth is not cookie-based or token-based. +UltimateAuth organizes authentication around explicit, controlled operations called **authentication flows**. -πŸ‘‰ It is **flow-based**. +A flow represents an authentication intention, such as establishing access, continuing an existing authentication context, or terminating it. -
+Rather than allowing individual endpoints or client applications to independently decide how authentication should work, UltimateAuth evaluates each operation within a consistent, server-controlled security context. -## πŸ”‘ What Does β€œFlow-Based” Mean? -In traditional systems, authentication is treated as: +This approach is independent of the credential format. -- A cookie -- A JWT token +A flow may work with a server-side session, an opaque access token, a JWT, or a refresh token. The effective authentication mode determines which credentials and validation rules apply. -Once issued, the system simply checks: +**Flow-based authentication is not an alternative to session-based or token-based authentication. It is the architecture that coordinates those strategies.** -> β€œIs this token valid?” +## Why authentication flows matter -
+Authentication involves more than issuing and validating credentials. -UltimateAuth takes a different approach: +A complete authentication operation may need to determine: -πŸ‘‰ Authentication is a **series of controlled flows**, not a static artifact. +- Which tenant the operation belongs to. +- Which authentication mode is permitted. +- Which client profile and runtime characteristics apply. +- What authentication proof is required. +- Whether an existing authentication context can continue. +- Which security policies must be enforced. +- Whether server-side state must be created, updated, or revoked. +- Which credentials or other results should be returned to the client. -
+These decisions should not be independently reconstructed by every endpoint, client SDK, or authentication service. -## 🧭 Authentication as Flows -Every authentication operation is an explicit flow: +UltimateAuth establishes a shared context for the operation and delegates the relevant decisions to the components responsible for them. -- **Login** -- **Logout** -- **Validate** -- **Refresh** -- **Re-authentication** +The result is a predictable authentication process whose behavior can vary by mode without changing its fundamental security boundaries. -Each flow: +## What is an authentication flow? -- Is initiated intentionally -- Is processed on the server -- Produces a controlled result +An authentication flow is a controlled operation with a defined purpose, applicable security requirements, and an explicit outcome. -
+Three primary flows illustrate the model. -## πŸ” Example: Login Flow -Instead of: +### Login -> β€œGenerate a token and store it” +Login establishes a new authenticated context after the required authentication proof has been accepted. -UltimateAuth does: -``` -Login Request -β†’ Validate credentials -β†’ Resolve Root -β†’ Resolve or create Chain -β†’ Create Session -β†’ Issue authentication grant -``` +Depending on the effective mode, the operation may create server-side authentication state, issue access credentials, or perform both actions. -πŸ‘‰ Login is not a single step β€” it is a **managed process** +### Refresh -
+Refresh attempts to continue an existing authenticated context without requiring the original authentication proof again. -## πŸ”„ Example: Refresh Flow -Traditional systems: +Its behavior depends on the mode. -> Refresh = issue new token +For example, PureOpaque can continue a session through touch semantics, while token-based modes may issue replacement credentials. -UltimateAuth: -``` -Refresh Request -β†’ Validate session -β†’ Check security constraints -β†’ Apply policies (if any) -β†’ Optionally rotate tokens -β†’ Update session state (if needed) -``` +Refresh must not bypass expiration, revocation, or other continuation requirements. -πŸ‘‰ The server decides what actually happens +### Logout -
+Logout terminates or discards the applicable authentication context. -## πŸ” Example: Validate Flow -On each request: -``` -Incoming Request -β†’ Extract session/token -β†’ Validate session -β†’ Check chain (device context) -β†’ Verify root security version -β†’ Build auth state +In session-authoritative modes, this can involve revoking server-side sessions. + +In stateless JWT authentication, client-side credential removal does not automatically invalidate an already issued access token. + +These operations share architectural boundaries but do not perform identical work. + +For their implementation details, see [Auth Flows](/docs/auth-flows/overview). + +## The flow execution model + +At a conceptual level, an authentication flow follows a common progression: + +```text +Authentication Intent + | + v +Resolve Effective Security Context + | + v +Validate Required Evidence + | + v +Evaluate Applicable Security Rules + | + v +Execute Mode-Specific Operation + | + v +Produce Authentication Outcome + | + v +Deliver Result to Client ``` -πŸ‘‰ Validation is not just β€œtoken valid?” +This diagram represents responsibilities, not a fixed implementation sequence. -
+For example, credential validation, authority decisions, and state transitions may involve different components or ordering depending on the operation. -## ⚠️ Why Token-Based Thinking Falls Short -Token-based systems assume: +Some flows create sessions. Others update existing state, rotate tokens, revoke credentials, or perform no persistent state change. -- The token contains truth -- The server trusts the token +The important guarantee is that each operation executes within a controlled context and follows the security rules applicable to its purpose and authentication mode. -This leads to: +## AuthFlowContext -- Weak revocation -- No device awareness -- Limited control +A central concept in this architecture is `AuthFlowContext`. -
+`AuthFlowContext` represents the effective authentication environment in which an operation executes. -## βœ… UltimateAuth Approach -UltimateAuth treats tokens as: +It brings together the information required to apply consistent authentication behavior, including: -πŸ‘‰ **transport artifacts**, not sources of truth +- The intended authentication flow. +- The effective authentication mode. +- The resolved tenant context. +- The applicable client profile. +- Effective server options and security policies. +- Relevant device and session context, when applicable. +- Credential delivery and response behavior. -The real authority is: +The context is established through framework-controlled boundaries. -- Root -- Chain -- Session +Once established, it must remain stable throughout the operation. Individual services and endpoints must not independently replace it or reconstruct it using different assumptions. -
+This matters particularly in applications serving multiple tenants, client runtimes, and authentication modes. -## 🧠 Key Idea -> Tokens carry data -> Flows enforce rules +A flow must not begin under one security policy and complete under another merely because different components interpreted the request differently. -
+### Context is not authorization -## πŸ” Server-Controlled by Design +`AuthFlowContext` describes the environment and rules under which an operation executes. -All flows are: +Its existence does not mean that the operation has been authorized or that the presented credentials are valid. -- Executed on the server -- Evaluated against policies -- Subject to security constraints +The relevant authentication evidence and security requirements must still be evaluated. -πŸ‘‰ The client does not control authentication state +After authentication has established trusted identity, authorization can operate through the appropriate `AccessContext`. -
+The distinction between these contexts is explained in [Authentication Model](/docs/fundamentals/auth-model). -## βš™οΈ Flow Examples in Code +## Flows and authentication modes -Using `IUAuthClient`: +Authentication flows describe **what operation is being performed**. -```csharp -await UAuthClient.Flows.LoginAsync(request); -await UAuthClient.Flows.RefreshAsync(); -await UAuthClient.Flows.LogoutAsync(); -``` -πŸ‘‰ Each method represents a server-driven flow +Authentication modes determine **how the operation establishes or continues trust**. + +Consider login. + +In PureOpaque, a successful login establishes a server-side session and provides a session credential. + +In Hybrid, login establishes server-side session authority and issues separate opaque access and refresh tokens. + +In SemiHybrid, login establishes the session lifecycle state required by the mode and issues JWT-based access credentials. + +In PureJwt, login issues JWT credentials without requiring a server-side session. + +All four are login operations, but their persistence and credential-issuance requirements differ. + +The same principle applies to refresh and logout. + +| Flow | What remains consistent | What varies by mode | +|---|---|---| +| Login | Establish authenticated access from validated evidence | Session creation, token format, credential issuance | +| Refresh | Determine whether authentication may continue | Session touch, session validation, token rotation, renewal state | +| Logout | End or discard the applicable authentication context | Session revocation, refresh-token invalidation, client credential cleanup | +| Request authentication | Establish trusted identity from presented credentials | Session validation, opaque token resolution, JWT validation | + +A flow must never introduce a session dependency merely because another authentication mode requires one. + +In particular, PureJwt must remain independent of mandatory session persistence and validation. + +For the complete mode definitions and their guarantees, see [Authentication Modes](/docs/fundamentals/auth-modes). + +## Server-controlled decisions + +UltimateAuth treats authentication as a server-controlled process. + +The client can request an operation and provide the evidence needed to perform it. + +However, the client does not independently determine: + +- Whether the authentication evidence is acceptable. +- Which security policies can be bypassed. +- Whether an expired or revoked authentication context may continue. +- Whether a new session or token may be issued. +- Whether a particular authentication mode is permitted. + +These decisions belong to trusted framework and application components operating under the effective server configuration. + +This does not mean every authentication decision requires a database lookup. + +For stateless JWT authentication, the server can establish identity through cryptographic verification and the applicable token-validation rules. + +For session-authoritative authentication, the server must also validate the required session state. + +Server authority describes **who controls the rules**, not whether every request must consult persistent storage. + +## Separation of responsibilities + +Flow-based authentication is also a separation-of-concerns model. + +The components participating in an authentication operation have distinct responsibilities. + +**Request and context resolution** + +Establish the trusted execution environment, including tenant, mode, client profile, and effective policies. + +**Authentication validation** + +Evaluate the evidence required by the operation and authentication mode. + +**Security decisions** + +Determine whether the requested operation is permitted under the applicable rules and authoritative state. + +**Orchestration** + +Coordinate the services and state transitions needed to complete the operation. + +**Persistence and credential issuance** + +Create, update, or revoke the relevant authentication state and credentials when required. + +**Response delivery** + +Communicate the result through the appropriate cookie, header, redirect, or other supported delivery mechanism. + +Separating these responsibilities prevents transport behavior from becoming authentication policy. + +For example, whether an access token is delivered through a cookie or header should not change the security requirements used to issue it. + +The technical components and request pipeline implementing these responsibilities are documented in [Auth Flows](../auth-flows/). + +## Flows across client runtimes + +UltimateAuth supports different application environments through a common authentication architecture. + +A browser application, Blazor Server application, Blazor WebAssembly application, mobile client, and resource API may interact with authentication differently. + +For example: + +- A browser application may rely on secure cookies. +- A mobile application may manage credentials through secure device storage. +- A resource API may validate bearer access tokens. +- A Blazor Server application may use a server-controlled session without managing refresh-token rotation. + +These differences affect client integration and credential transport. + +They do not require independent implementations of the fundamental authentication model. + +Client Profiles provide runtime-appropriate behavior and defaults, while authentication modes define the validation and lifecycle strategy. + +See [Client Profiles](/docs/fundamentals/client-profiles) for how runtime characteristics influence authentication configuration. + +## Predictable outcomes + +An authentication flow should produce an explicit result that reflects what the operation actually accomplished. + +For example, a refresh operation may: + +- Continue an existing session without changing persistent state. +- Update session activity or expiration when permitted. +- Rotate refresh credentials and issue replacement tokens. +- Reject continuation and require reauthentication. + +These outcomes are not interchangeable. + +A successful operation should not imply that a token was issued when no token was needed. + +Likewise, an unsuccessful refresh must not silently create a new authenticated context or bypass the original authentication requirements. + +Explicit outcomes allow the client SDK and server components to respond consistently without assuming that every mode behaves the same way. + +## Extensibility and security boundaries + +A flow-based architecture provides controlled extension points for authentication behavior. + +Depending on the supported features and configured services, applications may integrate additional requirements such as: + +- Multi-factor authentication. +- Additional credential verification. +- Tenant-specific authentication policies. +- Device-related security checks. +- Custom authentication proof providers. +- Additional security decisions before issuing or continuing access. + +Extensions must preserve the security guarantees of the effective authentication mode. + +For example, a custom flow must not bypass mandatory session validation in Hybrid. + +Similarly, an extension must not make session persistence an undocumented prerequisite for PureJwt. -
+Flexibility must not silently change the meaning of an authentication mode. -## 🧩 How This Changes Development -Instead of thinking: +## Flow-based does not mean session-based -❌ β€œI need to manage tokens” +An important distinction is that authentication flows and authentication lineage are related but separate concepts. -You think: +**Authentication flows** describe controlled operations. -βœ… β€œI need to trigger flows” +**Authentication lineage** describes the Root, Chain, and Session relationships used by session-backed authentication. -
+A flow may operate on that lineage when required. -## πŸ“Œ Benefits of Flow-Based Authentication -### βœ” Predictable Behavior -- Every action is explicit and controlled. +However, not every flow or mode requires persisted Root, Chain, or Session state. -### βœ” Better Security -- No blind token trust -- Server-side validation -- Policy-driven decisions +This separation allows UltimateAuth to support both stateful and stateless authentication without maintaining two unrelated authentication architectures. -### βœ” Extensibility -Flows can be extended with: +See [Authentication Model](/docs/fundamentals/auth-model) for the lineage model and its role in authentication. -- MFA -- Risk-based checks -- Custom policies +## Summary -### βœ” Consistent Across Clients -Same flows work for: -- Blazor Server -- WASM (PKCE) -- APIs +UltimateAuth uses authentication flows to coordinate security-sensitive operations through a consistent, trusted execution context. -
+A flow: -## 🧠 Mental Model -If you remember one thing: +- Represents an explicit authentication intention. +- Operates under a resolved and stable `AuthFlowContext`. +- Applies the validation and security rules required by the effective mode. +- Coordinates only the state transitions and credential operations necessary for its purpose. +- Produces an explicit outcome. +- Delivers that outcome according to the applicable client and response configuration. -πŸ‘‰ Authentication is not a token β€” it is a process +The architecture does not require every mode to use the same credentials, persistence model, or validation strategy. -## ➑️ Next Step +**Flows define the operation. Modes define its authentication behavior. Client Profiles adapt its integration with the runtime.** -Now that you understand flows: +## Next step -πŸ‘‰ Continue to Auth Modes +Continue to [Authentication Modes](/docs/fundamentals/auth-modes) to understand how PureOpaque, Hybrid, SemiHybrid, and PureJwt implement different authentication and session lifecycle strategies. \ No newline at end of file diff --git a/docs/content/fundamentals/index.md b/docs/content/fundamentals/overview.md similarity index 91% rename from docs/content/fundamentals/index.md rename to docs/content/fundamentals/overview.md index fbe6c93b..56485b6f 100644 --- a/docs/content/fundamentals/index.md +++ b/docs/content/fundamentals/overview.md @@ -1,5 +1,5 @@ --- -title: Fundamental Overview +title: Fundamentals Overview order: 1 group: fundamentals --- diff --git a/docs/content/fundamentals/request-lifecycle.md b/docs/content/fundamentals/request-lifecycle.md index 94ca5f76..f6582ade 100644 --- a/docs/content/fundamentals/request-lifecycle.md +++ b/docs/content/fundamentals/request-lifecycle.md @@ -4,160 +4,339 @@ order: 7 group: fundamentals --- +# Request Lifecycle -# πŸ”„ Request Lifecycle -This section explains what happens when a request enters UltimateAuth. +UltimateAuth distinguishes between establishing authentication, validating an existing identity, and authorizing access to protected operations. -πŸ‘‰ From the moment an HTTP request arrives -πŸ‘‰ Until authentication state is established or a flow is executed +These responsibilities are related, but they do not necessarily execute through the same pipeline. -
+A login request may establish a new authentication context. A protected API request may only need to validate an existing credential. A server-side application operation may execute without making an additional HTTP request. -## 🧠 Two Types of Requests -UltimateAuth processes requests in two different ways: +Understanding these differences is essential when integrating UltimateAuth into real applications. -### 1. Passive Requests -Regular application requests (page load, API call) +This page focuses on **how authentication is evaluated during application requests and where the security boundaries are enforced**. -### 2. Active Flow Requests -Authentication flows (login, refresh, logout) +For authentication flow orchestration, see [Auth Flows](/docs/auth-flows/overview). -πŸ‘‰ Both share the same foundation, but diverge at the flow level. +## Two request categories -
+At a conceptual level, requests interacting with UltimateAuth fall into two categories. -## 🧩 Middleware Pipeline -Every request passes through the UltimateAuth middleware pipeline: +| Category | Purpose | Typical examples | +|---|---|---| +| Authentication operations | Establish, continue, or terminate authentication | Login, refresh, logout | +| Protected resource operations | Access application functionality using an existing identity | API calls, protected pages, application commands | -``` -Tenant β†’ Session Resolution β†’ (Validation) β†’ User Resolution -``` +The distinction matters because an authentication operation may change authentication state, while a protected resource operation generally consumes an existing authentication context. + +Both require appropriate security checks, but they do not need to execute identical flows. -### 🏒 Tenant Resolution -The system determines the tenant: +## Authentication operations -- Multi-tenant β†’ resolved via `ITenantResolver` -- Single-tenant β†’ default context applied +Authentication operations are explicitly initiated through supported authentication endpoints or application integrations. -πŸ‘‰ If tenant cannot be resolved β†’ request fails early +Examples include login, refresh, and logout. -### πŸ” Session Resolution -The system attempts to extract a session: +For HTTP authentication endpoints, UltimateAuth uses its flow infrastructure to resolve the effective authentication context and execute the requested operation. -- From headers, cookies, or tokens -- Converted into a `SessionContext` +Conceptually: +```text +Authentication Request + | + v +Authentication Flow Boundary + | + v +Effective AuthFlowContext + | + v +Operation-Specific Validation + | + v +Authentication Operation + | + v +Authentication Result ``` -SessionId β†’ SessionContext + +The operation may create or update sessions, issue credentials, rotate refresh tokens, revoke authentication state, or produce a result without persistent changes. + +These details depend on the flow and authentication mode. + +The internal execution pipeline is described in [Auth Flows](/docs/auth-flows/overview), while individual operations are documented in their respective flow guides. + +## Protected resource operations + +Most application requests do not initiate a login, refresh, or logout flow. + +Instead, they attempt to access a protected resource using an existing authentication credential or established server-side authentication context. + +A typical protected HTTP request follows this conceptual lifecycle: + +```text +Incoming Request + | + v +Resolve Applicable Authentication Scheme + | + v +Extract Authentication Credential + | + v +Validate Credential and Required State + | + v +Establish Trusted Identity + | + v +Evaluate Authorization + | + +---- Denied + | + v +Execute Protected Operation + | + v +Return Response ``` -πŸ‘‰ If no session is found β†’ request becomes anonymous +The exact framework components and ordering depend on the application integration. -### βœ” Session Validation (Resource APIs) -For API scenarios: +However, the security distinction remains the same: -- Session is validated immediately -- Device context is considered -- A validation result is attached to the request +**Authentication establishes identity. Authorization determines whether that identity may perform the requested operation.** -πŸ‘‰ This enables stateless or semi-stateful validation +A successfully authenticated request is not automatically authorized. -### πŸ‘€ User Resolution -The system resolves the current user: +## Credential validation -- Based on validated session -- Using `IUserAccessor` +The most important mode-dependent difference in the request lifecycle is how authentication credentials are validated. -πŸ‘‰ This produces the final user context +UltimateAuth supports different authentication strategies. -
+### PureOpaque -## πŸ”„ Passive Request Flow -For normal requests: +The client presents an opaque session credential. -``` -Request -β†’ Middleware pipeline -β†’ Session resolved -β†’ User resolved -β†’ Application executes -``` +The server resolves the associated session and evaluates the applicable session, chain, and security requirements. -πŸ‘‰ No flow execution happens +The session remains authoritative for authentication. -
+A revoked or expired session must not continue to authenticate subsequent requests. -## πŸ” Active Flow Requests -For auth endpoints (login, refresh, etc.): +### Hybrid -The lifecycle continues beyond middleware. +The client presents an opaque access token. -### Step 1: Flow Detection -``` -Endpoint β†’ FlowType -``` +The server resolves the token and validates its relationship to the authoritative server-side session. -### Step 2: Context Creation -An `AuthFlowContext` is created. +The access token and session are separate concepts. -It includes: +Authentication requires both a valid access credential and the applicable session validation requirements to succeed. -- Client profile -- Effective mode -- Tenant -- Device -- Session -- Response configuration +### SemiHybrid -πŸ‘‰ This defines the execution environment +The client presents a JWT access token. -### Step 3: Flow Execution -``` -AuthFlowContext β†’ Flow Service β†’ Orchestrator β†’ Authority -``` +Normal resource authentication validates the JWT cryptographically and applies the relevant token-validation rules. -### Step 4: State Mutation -Depending on the flow: +A server-side session remains relevant for lifecycle operations such as refresh and session management, but normal JWT validation does not require a session lookup. -- Session may be created, updated, or revoked -- Tokens may be issued -- Security state may change +Revoking a session therefore does not automatically invalidate an already issued JWT. -### Step 5: Response Generation -The system writes the response: +### PureJwt -- SessionId -- Access token -- Refresh token -- Redirect (if needed) +The client presents a JWT access token. -
+The resource validates the token cryptographically and applies the configured issuer, audience, lifetime, and other relevant validation requirements. -## πŸ” Example: Login Request -``` -HTTP Request -β†’ Tenant resolved -β†’ Session resolved (anonymous) -β†’ Flow detected (Login) -β†’ AuthFlowContext created -β†’ Credentials validated -β†’ Session created -β†’ Tokens issued -β†’ Response returned -``` +Authentication does not require a server-side session. + +Previously issued JWTs may remain valid until expiration unless an additional invalidation mechanism is explicitly configured. + +For the complete guarantees and tradeoffs, see [Authentication Modes](/docs/fundamentals/auth-modes). + +## When identity becomes trusted + +An incoming request may contain user identifiers, claims, tokens, cookies, device information, or tenant-related metadata. + +These values must not automatically be treated as a trusted identity. + +The relevant authentication mechanism must validate the presented evidence before the application relies on it. + +In session-authoritative modes, this includes the required server-side session checks. + +In JWT-based modes, it includes the applicable cryptographic and token-validation checks. + +Only after successful authentication should the application treat the resulting identity as authenticated. + +A failed authentication attempt must not leave behind an identity that appears successfully authenticated to downstream application code. + +### Anonymous requests + +Not every application request requires authentication. + +Public endpoints may allow anonymous access. + +A request without valid authentication credentials can remain anonymous when the endpoint permits it. + +For protected endpoints, missing or invalid authentication must prevent access according to the applicable authentication and authorization configuration. + +An anonymous request is not necessarily an error. It becomes an access problem when the requested operation requires an authenticated identity. + +## Authentication context and AccessContext + +UltimateAuth separates authentication-flow execution from authorization context. + +`AuthFlowContext` is associated with authentication operations and describes the effective environment in which those operations execute. + +It includes the applicable mode, client profile, tenant, and other flow-related information. + +`AccessContext` represents the trusted actor and contextual information used for access and authorization decisions. + +These contexts serve different purposes. + +An application request does not need to execute a login flow merely to establish an identity from an already valid authentication credential. + +Likewise, the existence of an `AuthFlowContext` does not mean the operation has already been authorized. + +The authentication model and these trust boundaries are introduced in [Authentication Model](/docs/fundamentals/auth-model). + +## Authorization boundary + +After authentication establishes the caller's identity, the application must determine whether the requested operation is permitted. + +Authorization may depend on: + +- The authenticated actor. +- The requested resource or operation. +- Tenant boundaries. +- Roles and permissions. +- Applicable application policies. +- Resource ownership or other domain-specific requirements. + +UltimateAuth integrates with the relevant server-side authorization mechanisms, but authentication and authorization remain separate responsibilities. + +For ASP.NET Core applications, endpoint and policy-based authorization can protect HTTP resources. + +However, an authorization attribute applied to an HTTP endpoint does not automatically protect arbitrary direct calls to the underlying application service. + +Security requirements must be enforced at the boundary through which the operation is actually accessible. + +## HTTP requests and in-process operations + +Not every protected application operation crosses an HTTP boundary. + +This distinction is especially important for server-hosted applications. + +### HTTP resource requests + +A remote client sends an HTTP request to a protected application or resource API. + +The receiving application must authenticate the request and enforce authorization before performing the protected operation. + +Client-side authentication state alone is insufficient. + +### In-process application operations + +A server-hosted application may invoke application services directly without making an HTTP request. + +For example, a Blazor Server component may call a registered service within the same process. + +In this case, the application must not assume that HTTP middleware or endpoint authorization filters execute automatically. + +The service invocation must operate with an appropriately established identity and enforce the relevant authorization requirements. + +This is an architectural distinction, not a separate authentication mode. + +See [Runtime Architecture](/docs/fundamentals/runtime-architecture) for integrated and distributed deployment models. + +## Long-lived connections + +Some application environments maintain connections or execution contexts that outlive individual HTTP requests. + +Blazor Server is an important example. + +An interactive circuit may remain active after its initial authentication state was established. + +During that time, the underlying authentication state may change. + +A session may expire, be revoked, or become invalid because of a security-sensitive operation. + +Consequently, applications must account for authentication-state revalidation and authorization during long-lived interactions. + +An initially authenticated connection must not be treated as permanent proof that every later operation remains authorized. + +The appropriate mechanism depends on the runtime integration and authentication mode. + +## Authentication failures and authorization failures + +Authentication and authorization failures represent different conditions. + +| Condition | Meaning | Typical HTTP outcome | +|---|---|---| +| Missing required authentication | No acceptable authenticated identity was established | 401 Unauthorized | +| Invalid or expired credential | Presented evidence failed authentication | 401 Unauthorized | +| Authenticated but insufficient permission | Identity is valid, but access is denied | 403 Forbidden | +| Public endpoint accessed anonymously | Authentication is not required | Request proceeds | + +These are typical HTTP semantics, not guarantees that every integration returns identical status codes. + +Browser applications may use redirects or other configured challenge behavior. + +Application services operating outside HTTP may express failures through typed results or exceptions rather than HTTP responses. + +The essential distinction is that authentication failure and authorization denial must not be confused. + +## Request isolation + +Authentication state must be associated with the correct execution context. + +A request must not inherit another request's authenticated identity, tenant context, or authorization state. + +This matters in multi-tenant systems, applications with concurrent requests, and server-side environments where services may have different lifetimes. + +Request-specific context must be resolved and propagated through the appropriate framework boundaries. + +Long-lived or singleton services must not accidentally retain request-specific authentication information. + +The same principle applies to direct application service calls and background operations. + +A background operation without an authenticated HTTP request must establish its execution and authorization context through an explicitly supported mechanism rather than assuming a user identity exists. + +## What the request lifecycle does not guarantee + +The request lifecycle should not be interpreted as a promise that every UltimateAuth operation: + +- Creates an `AuthFlowContext`. +- Resolves a server-side session. +- Queries authentication persistence. +- Rotates or issues tokens. +- Executes the same orchestrator or authority method. +- Passes through an HTTP endpoint. +- Uses identical authorization mechanisms. + +Those requirements depend on the operation, authentication mode, and runtime integration. + +The shared guarantee is architectural: authentication evidence must be validated according to the applicable security model, and protected operations must enforce their authorization requirements. + +## Summary + +UltimateAuth distinguishes between authentication operations and requests that consume an existing authenticated identity. + +Authentication operations establish, continue, or terminate authentication through controlled flows. + +Protected resource operations validate existing authentication evidence, establish trusted identity, and enforce authorization before allowing access. -
+The validation strategy depends on the authentication mode. -## πŸ” Flow Execution Boundary -Authentication flows are only executed for endpoints explicitly marked with flow metadata. +The execution boundary depends on the runtime and deployment architecture. -- Regular requests do not create an AuthFlowContext -- AuthFlowContext is only created during flow execution +**A request is not trusted because it contains a credential. It becomes authenticated only when the required validation succeeds, and it becomes authorized only when the applicable access rules permit the operation.** -πŸ‘‰ This ensures that authentication logic does not interfere with normal application behavior. +## Next step -
+You have completed the Fundamentals section. -## 🧠 Mental Model -πŸ‘‰ Middleware prepares the request -πŸ‘‰ Flows change the state +Continue to [Auth Flows](/docs/auth-flows/overview) to explore the technical authentication pipeline and the individual login, refresh, and logout operations. \ No newline at end of file diff --git a/docs/content/fundamentals/runtime-architecture.md b/docs/content/fundamentals/runtime-architecture.md index 992ed5a7..6ff9c67c 100644 --- a/docs/content/fundamentals/runtime-architecture.md +++ b/docs/content/fundamentals/runtime-architecture.md @@ -4,162 +4,554 @@ order: 6 group: fundamentals --- +# Runtime Architecture -# πŸ— Runtime Architecture +UltimateAuth is designed to provide a unified authentication architecture across different .NET application environments. -UltimateAuth processes authentication through a structured execution pipeline. +A Blazor Server application, a standalone Blazor WebAssembly application, a native MAUI application, and a resource API may have different execution models, but they should not require entirely separate authentication systems. -πŸ‘‰ It is not just middleware-based authentication -πŸ‘‰ It is a **flow-driven execution system** +UltimateAuth addresses this through a separation between **authentication infrastructure, client integration, hosting, and protected resources**. -## 🧠 The Big Picture +This separation allows authentication behavior to remain consistent while adapting to the capabilities and responsibilities of each runtime. -When a request reaches an auth endpoint: +## Architectural overview + +At a high level, UltimateAuth consists of several cooperating layers: + +- **Core:** Shared authentication contracts, models, and foundational abstractions. +- **Server:** Authentication operations, security decisions, credential management, and persistence coordination. +- **Client:** Client-side integration, authentication requests, and credential lifecycle handling. +- **UAuthHub:** The application hosting UltimateAuth's authentication endpoints and services. +- **Resource applications:** Applications and APIs that consume authenticated identity and enforce authorization. + +These are architectural responsibilities, not necessarily separate processes or deployments. + +A single application may host multiple responsibilities. Alternatively, they can be distributed across independently deployed services. + +```text + Application Clients + β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” + β”‚ β”‚ β”‚ + Blazor WASM MAUI Other Clients + β”‚ β”‚ β”‚ + β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ + β”‚ HTTP + β–Ό + UAuthHub + β”‚ + UltimateAuth Server + β”‚ + Authentication Services + β”‚ + Stores / Providers + β”‚ + β–Ό + Persistent State + + + Protected Resources + β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” + β”‚ β”‚ β”‚ + Resource API Web App Other Services + β”‚ β”‚ β”‚ + β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ + Authentication Validation + β”‚ + Authorization +``` + +This diagram illustrates the separation of responsibilities. + +It does not imply that every authenticated resource request must pass through UAuthHub. + +Whether a protected resource needs to consult server-side authentication infrastructure depends on the effective authentication mode and its validation requirements. + +## Core: shared authentication foundations + +The Core layer defines the common concepts used throughout UltimateAuth. + +Its responsibility is to provide stable contracts and abstractions without depending on a particular client runtime or application host. + +These foundations include concepts such as: + +- Authentication modes. +- Authentication and security contexts. +- Authentication results and contracts. +- Domain models and shared abstractions. +- Security-related options and definitions. + +The Core layer provides the common vocabulary used by client integrations, server services, and other framework components. + +It should not contain assumptions that authentication always occurs through an HTTP endpoint, a browser cookie, or a particular hosting model. + +This separation is important because UltimateAuth supports both stateful and stateless authentication strategies. + +A shared authentication contract must not implicitly require session persistence when the selected mode does not use sessions. + +## Server: authentication execution and lifecycle + +The Server layer implements the server-side authentication capabilities. + +It coordinates the operations required to establish, continue, validate, and terminate authentication according to the effective mode and security policies. + +Its responsibilities include: + +- Authentication flow execution. +- Credential validation and management. +- Session and token lifecycle operations. +- Authentication security decisions. +- Coordination with persistence providers. +- Credential issuance and validation. +- Integration with server-side authorization infrastructure. + +The Server layer contains multiple cooperating services rather than one universal authentication engine responsible for every decision. + +For example, session lifecycle operations, credential verification, and authorization may follow different execution paths while sharing common security contracts. + +The architecture separates orchestration from the underlying services that perform domain-specific work. + +For the detailed execution pipeline, see [Auth Flows](/docs/auth-flows/overview). + +## Client: runtime integration + +The Client layer provides the integration between an application and UltimateAuth's authentication services. + +Its purpose is to reduce the amount of authentication infrastructure that application developers need to implement themselves. + +Depending on the supported runtime and configuration, client integration may handle: + +- Initiating authentication flows. +- Communicating with UAuthHub. +- Processing authentication outcomes. +- Managing supported credential delivery mechanisms. +- Coordinating authentication state with the application. +- Integrating authenticated requests with protected resources. + +The client SDK does not replace server-side authentication validation. + +It provides an application-facing interface to the authentication infrastructure. + +### Client integration is runtime-aware + +Different client runtimes have different capabilities. + +A browser-based WebAssembly application executes within browser security restrictions. + +A native MAUI application has access to platform-specific capabilities. + +A Blazor Server application executes application logic on the server while maintaining an interactive connection with the browser. + +UltimateAuth accounts for these differences through runtime-specific integrations and Client Profiles. + +The authentication model remains shared even when the implementation of credential transport or application state differs. + +See [Client Profiles](/docs/fundamentals/client-profiles). + +## UAuthHub: the authentication host + +UAuthHub is the application hosting UltimateAuth's server-side authentication infrastructure. + +It provides the environment in which authentication endpoints and the required server services are available. + +Depending on the deployment, UAuthHub can be responsible for: + +- Accepting authentication requests. +- Resolving the applicable tenant and authentication context. +- Executing authentication flows. +- Managing credentials and sessions. +- Issuing authentication credentials. +- Applying configured authentication policies. +- Exposing the supported authentication endpoints. + +UAuthHub is a hosting role, not an authentication mode. + +A single UAuthHub can support multiple client environments and authentication strategies, subject to its configuration. + +### Centralized hosting + +In a centralized deployment, several applications can use the same UAuthHub. + +```text +Blazor WASM Application ──┐ + β”‚ +MAUI Application ─────────┼──► UAuthHub + β”‚ β”‚ +Web Application β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ + β–Ό + Authentication Services + β”‚ + β–Ό + Authentication Storage +``` + +This allows applications to share authentication infrastructure without requiring each application to implement its own credential management and authentication lifecycle. + +However, centralized authentication hosting does not mean that all protected resource requests must be routed through UAuthHub. + +The resource authentication strategy remains mode-dependent. + +## Resource applications and APIs + +A resource application is an application or service that exposes functionality requiring authentication and authorization. + +Examples include: + +- A protected Web API. +- An administrative web application. +- A business application backend. +- An independently deployed service. +- A server-rendered application with protected operations. + +A resource application must establish the caller's authenticated identity before allowing access to protected functionality. + +It must then enforce the relevant authorization policies. + +### Authentication validation depends on the mode + +In PureOpaque and Hybrid, authentication requires server-side session authority. + +The resource authentication path must therefore have access to the infrastructure necessary to enforce the relevant session validation requirements. + +In SemiHybrid and PureJwt, normal access-token authentication uses JWT validation without a mandatory session lookup. + +This can allow a resource API to validate access tokens locally when it has the required verification keys and token-validation configuration. + +The authentication mode therefore affects the resource application's infrastructure dependencies. + +### Authorization remains local to the protected operation + +Authentication establishes who the caller is. + +Authorization determines whether that caller may perform the requested operation. + +A valid authentication credential does not automatically grant permission to every resource. + +Resource applications must continue to enforce their authorization requirements through the applicable framework and application policies. + +UltimateAuth's authentication integration should preserve these boundaries rather than replacing resource authorization with client-side assumptions. + +## Deployment topology + +UltimateAuth separates architectural responsibilities from physical deployment boundaries. + +This means the same authentication architecture can support different deployment topologies. + +### Integrated deployment + +An integrated deployment hosts authentication infrastructure and application functionality within the same server application. + +```text +ASP.NET Core Application + β”‚ + β”œβ”€β”€ UltimateAuth Server + β”‚ + β”œβ”€β”€ Authentication Services + β”‚ + β”œβ”€β”€ Application Services + β”‚ + └── Protected Endpoints +``` + +In this model, authentication services and protected application functionality may share the same dependency injection container and process. + +Where supported, internal operations can interact with server-side services directly without requiring an HTTP call to the same application. + +This can simplify deployment and avoid unnecessary network communication. + +However, direct service access must still respect the required authentication and authorization boundaries. + +### Distributed deployment + +A distributed deployment separates the authentication host from one or more resource applications. ```text -Request - β†’ Endpoint Filter - β†’ AuthFlowContext - β†’ Endpoint Handler - β†’ Flow Service - β†’ Orchestrator - β†’ Authority - β†’ Stores / Issuers - β†’ Response + UAuthHub + β”‚ + β”‚ Authentication + β”‚ + β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” + β”‚ β”‚ + Web Application Mobile Client + β”‚ β”‚ + β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ + β–Ό + Resource API + β”‚ + β–Ό + Application Services ``` -Each step has a clearly defined responsibility. -## πŸ”„ Request Entry Point -Authentication begins at the endpoint level. +In this model, clients communicate with UAuthHub for the applicable authentication operations and access resource APIs using the supported credentials. + +Resource APIs validate those credentials according to the configured authentication mode. + +This architecture supports independently deployed applications while maintaining shared authentication policies and identity infrastructure. + +### Choosing a topology + +Integrated and distributed deployments are not different authentication modes. -An endpoint filter detects the flow: +They describe where application components execute and how they communicate. -`Endpoint β†’ FlowType (Login, Refresh, Logout…)` +The appropriate topology depends on: -πŸ‘‰ The system knows which flow is being executed before any logic runs. +- Application boundaries. +- Deployment and operational requirements. +- Network and infrastructure constraints. +- Authentication mode dependencies. +- Service ownership and scaling requirements. -
+A distributed deployment is not inherently more secure or more scalable than an integrated deployment. -## 🧾 AuthFlowContext β€” The Core State -Before any operation starts, UltimateAuth creates an AuthFlowContext. +Both require correct authentication validation, authorization enforcement, and infrastructure configuration. -This is the central object that carries: +## Blazor Server architecture -- Client profile -- Effective authentication mode -- Tenant information -- Device context -- Session state -- Response configuration +Blazor Server has a distinct execution model. -πŸ‘‰ This context defines the entire execution environment +The browser maintains an interactive connection to an application whose component logic executes on the server. -
+This makes it possible to keep many application operations and authentication-related services within the server environment. -## βš™οΈ Flow Service β€” Entry Layer -After the context is created, the request is passed to the Flow Service. +```text +Browser + β”‚ + β”‚ Interactive Connection + β–Ό +Blazor Server Application + β”‚ + β”œβ”€β”€ UI Components + β”‚ + β”œβ”€β”€ Application Services + β”‚ + β”œβ”€β”€ UltimateAuth Integration + β”‚ + └── Protected Operations +``` + +PureOpaque is a natural default for this environment because it provides server-controlled authentication without requiring a separate access/refresh token lifecycle. -The Flow Service: +However, the hosting model does not require PureOpaque exclusively. -- Acts as the entry point for all flows -- Normalizes execution -- Delegates work to orchestrators +### Direct service access -πŸ‘‰ It does not implement business logic directly +When authentication infrastructure and application services are hosted in the same process, server-side code may be able to use registered services directly. -
+This can avoid unnecessary HTTP requests between components of the same application. -## 🧭 Orchestrator β€” Flow Coordinator -The Orchestrator manages the execution of a flow. +The distinction is between an application service call and a remote API request. -- Coordinates multiple steps -- Ensures correct execution order -- Delegates decisions to Authority +A direct service call must not bypass security checks merely because it avoids HTTP. -πŸ‘‰ Think of it as the flow engine +Authorization requirements must still be enforced at the appropriate trusted boundary. -
+### Long-lived interactive connections -## πŸ” Authority β€” Decision Layer -The Authority is the most critical component. +Blazor Server introduces an additional consideration: authentication state may change while an interactive circuit remains active. -- Validates authentication state -- Applies security rules -- Approves or rejects operations +A session can expire or be revoked after the circuit was established. -πŸ‘‰ No sensitive operation bypasses Authority +Applications must therefore account for authentication-state revalidation and authorization of sensitive operations. -
+An authenticated circuit must not be treated as permanently authorized solely because its initial connection succeeded. -## βš™οΈ Services & Stores -Once decisions are made: +## Blazor WebAssembly architecture -- Services handle domain logic -- Stores handle persistence -- Issuers generate tokens or session artifacts +Blazor WebAssembly executes application code in the browser. -πŸ‘‰ These layers do not make security decisions +Unlike Blazor Server, it cannot directly access private server-side authentication services. -
+Authentication and protected application operations therefore cross an HTTP boundary. -## πŸ” End-to-End Example (Login) -Login Request +```text +Browser + β”‚ + └── Blazor WebAssembly + β”‚ + β”œβ”€β”€ UltimateAuth Client + β”‚ β”‚ + β”‚ β–Ό + β”‚ UAuthHub + β”‚ + └── Application API Client + β”‚ + β–Ό + Resource API ``` - β†’ Endpoint Filter (Login Flow) - β†’ AuthFlowContext created - β†’ Flow Service - β†’ Orchestrator - β†’ Authority validates credentials - β†’ Session created (Store) - β†’ Tokens issued (Issuer) - β†’ Response generated + +Hybrid is a suitable authentication strategy for applications that need separate access and refresh tokens while retaining server-authoritative session validation. + +However, a browser application can also use other modes when the corresponding transport and security architecture supports them. + +For example, a backend-for-frontend architecture may use secure session cookies rather than exposing bearer tokens to browser application code. + +The runtime determines which integration techniques are available; the authentication mode determines the security and lifecycle model. + +## Native application architecture + +Native applications, including .NET MAUI applications, execute outside the browser environment. + +They generally communicate with authentication hosts and resource APIs through HTTP. + +```text +MAUI Application + β”‚ + β”œβ”€β”€ UltimateAuth Client + β”‚ β”‚ + β”‚ β–Ό + β”‚ UAuthHub + β”‚ + β”œβ”€β”€ Secure Credential Storage + β”‚ + └── Resource API Client + β”‚ + β–Ό + Resource API ``` -
-## 🧠 Why This Architecture Matters -βœ” Centralized Decision Making -- Authority is always in control -- No scattered validation logic +Native applications can use platform-specific secure storage for sensitive credentials. + +Their credential delivery and persistence requirements differ from browser applications. + +Hybrid can provide a suitable model for applications requiring refresh-token continuity and centralized session control. + +The choice remains configurable according to the supported authentication modes and application requirements. + +## Shared components across runtimes + +A unified authentication architecture should minimize unnecessary duplication between client environments. + +UltimateAuth separates shared authentication behavior from runtime-specific integration. + +For example, Blazor Server and Blazor WebAssembly applications may share: + +- Authentication-related UI components. +- Login and logout interaction patterns. +- Request and response contracts. +- Application-facing client abstractions. +- Authentication-state presentation logic. + +At the same time, they may require different implementations for: + +- Credential storage. +- HTTP communication. +- Browser interaction. +- Server-side service access. +- Authentication-state synchronization. + +The goal is not to force every runtime to execute identical code. + +The goal is to share behavior and contracts where appropriate while preserving each runtime's security and execution requirements. + +## HTTP boundaries and direct execution + +An important architectural distinction is whether an operation crosses an HTTP boundary. + +### Remote execution + +A remote client communicates with an authentication host or resource API through HTTP. + +The server must independently validate the request, credentials, and authorization requirements. + +Client-side checks are not sufficient. + +### In-process execution + +An application may host UltimateAuth services and protected application services within the same process. + +In that case, some operations may use direct service calls instead of HTTP. + +This can reduce serialization and network overhead. + +However, HTTP middleware and endpoint authorization attributes do not automatically execute during arbitrary direct service calls. + +The application must ensure that equivalent security requirements are enforced through the appropriate service or authorization boundary. + +Direct execution is an optimization and integration choice, not permission to bypass authentication or authorization. + +## Infrastructure and persistence + +UltimateAuth uses abstractions to separate authentication behavior from infrastructure implementations. + +Depending on the configured mode and services, the system may require storage for: + +- User and credential information. +- Authentication security state. +- Session lineage. +- Refresh-token lifecycle. +- Device-associated authentication state. + +These dependencies vary by authentication mode. + +For example, PureOpaque and Hybrid require session infrastructure for authentication. + +SemiHybrid requires session infrastructure for lifecycle management but not for normal JWT validation. + +PureJwt does not require server-side session infrastructure. Optional refresh-token functionality may introduce independent renewal-state requirements. + +The architecture must preserve these distinctions. + +Infrastructure abstractions allow the authentication services to remain independent of a particular persistence implementation, while the selected mode determines which capabilities are required. + +## Architectural boundaries + +UltimateAuth's runtime architecture depends on several boundaries remaining explicit. + +### Client and server + +Clients initiate operations and present credentials. + +Servers establish trusted authentication state and enforce security requirements. + +### Authentication and authorization + +Authentication determines identity. + +Authorization determines whether an operation is permitted. + +### Hosting and authentication mode + +UAuthHub and resource applications describe hosting responsibilities. + +PureOpaque, Hybrid, SemiHybrid, and PureJwt describe authentication strategies. + +### Shared contracts and runtime integration + +Shared contracts define consistent behavior. + +Runtime integrations adapt that behavior to browser, native, and server-side environments. + +### Orchestration and infrastructure + +Orchestration coordinates authentication operations. -βœ” Predictable Execution -- Every flow follows the same pipeline -- No hidden behavior +Infrastructure services provide persistence, token operations, and other required capabilities. -βœ” Extensibility -- Replace stores -- Extend flows -- Customize orchestration +These boundaries allow UltimateAuth to evolve without requiring every client integration or authentication mode to duplicate the complete authentication implementation. -βœ” Security by Design -- No bypass of Authority -- Context-driven validation -- Flow-aware execution +## Summary -## πŸ”— Relation to Other Concepts -This architecture connects all previous concepts: +UltimateAuth's runtime architecture separates authentication responsibilities from application hosting and client execution environments. -- Auth Model (Root / Chain / Session) β†’ validated in Authority -- Auth Flows β†’ executed by Orchestrator -- Auth Modes β†’ applied via EffectiveMode -- Client Profiles β†’ influence behavior at runtime +Its main architectural components are: -## 🧠 Mental Model +- **Core**, which defines shared contracts and authentication foundations. +- **Server**, which implements authentication operations and lifecycle management. +- **Client**, which provides application-facing authentication integration. +- **UAuthHub**, which hosts the authentication infrastructure. +- **Resource applications**, which authenticate callers and enforce authorization. -If you remember one thing: +These components can operate within an integrated application or across distributed services. -πŸ‘‰ Flow defines what happens -πŸ‘‰ Context defines how it happens -πŸ‘‰ Authority decides if it happens +Different client runtimes can share authentication contracts and behavior while using integration mechanisms appropriate to their environments. -## πŸ“Œ Key Takeaways -Authentication is executed as a pipeline -AuthFlowContext carries execution state -Orchestrator coordinates flows -Authority enforces security -Services and Stores execute operations +Authentication modes determine the required validation and state dependencies. -## ➑️ Next Step +**The architecture is unified because its security contracts and responsibilities remain consistentβ€”not because every runtime, mode, or deployment follows an identical execution path.** -Now that you understand the execution model: +## Next step -πŸ‘‰ Continue to Request Lifecycle +Continue to [Request Lifecycle](/docs/fundamentals/request-lifecycle) to understand how an incoming request moves through authentication validation, identity establishment, and authorization before reaching a protected operation. \ No newline at end of file diff --git a/docs/content/getting-started/ecosystem.md b/docs/content/getting-started/ecosystem.md index 953dfcad..73d266ab 100644 --- a/docs/content/getting-started/ecosystem.md +++ b/docs/content/getting-started/ecosystem.md @@ -836,6 +836,6 @@ Server +-- Persistence implementations ``` -Continue with the [Quick Start](./quickstart.md) to build your first UltimateAuth application. +Continue with the [Quick Start](/docs/getting-started/quickstart) to build your first UltimateAuth application. -For persistent storage, UAuthHub, standalone WebAssembly, Resource API, and other deployment models, continue with the [Real-World Setup](./real-world-setup.md). +For persistent storage, UAuthHub, standalone WebAssembly, Resource API, and other deployment models, continue with the [Real-World Setup](/docs/getting-started/real-world-setup). diff --git a/docs/content/getting-started/index.md b/docs/content/getting-started/index.md index cc8c3fde..cb1e86a5 100644 --- a/docs/content/getting-started/index.md +++ b/docs/content/getting-started/index.md @@ -4,169 +4,428 @@ order: 1 group: getting-started --- -# πŸš€ Getting Started +# Welcome to UltimateAuth -Welcome to **UltimateAuth**, a unified authentication and authorization framework for modern .NET applications. +**Modern authentication and authorization for .NET β€” built around a unified security lifecycle.** -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. +Authentication starts with a login. But it doesn't end there. -**One framework. Multiple authentication models. A consistent security lifecycle.** +As applications grow, they introduce new challenges: multiple clients, refresh tokens, device sessions, credential recovery, tenant isolation, permissions, and revocation. What begins as a simple login system can gradually become a collection of disconnected security mechanisms. -## What is UltimateAuth? +UltimateAuth takes a different approach. -UltimateAuth is a modular, flow-driven framework for building and managing authentication in .NET. It connects concepts that are often implemented separately: +It brings authentication, authorization, users, credentials, sessions, and client integration into **one coherent framework**, with a shared model for how security decisions are made and how authentication state changes over time. -- **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. +**One framework. Multiple client environments. A consistent security lifecycle.** -The goal is not to hide these concepts. It is to make them **work together predictably**. +[Explore the Source](https://github.com/CodeBeamOrg/UltimateAuth) -## Unified Does Not Mean One-Size-Fits-All +## Why UltimateAuth? -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. +Most applications don't struggle with generating an access token. -UltimateAuth provides a common foundation while allowing behavior to be shaped by the **effective authentication mode**, **client profile**, **tenant**, and **request context**. +They struggle with everything that comes afterward. -| 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 | +Consider an application that starts with a single web frontend. Eventually, it adds a mobile application, an administration panel, multiple organizations, and external APIs. -**Unified** is therefore about a shared security modelβ€”not forcing all applications to use identical cookies, tokens, or endpoints. +Suddenly, authentication raises questions that a basic login implementation doesn't answer: -## Authentication Is More Than Issuing a Token +- How should browser sessions and API tokens coexist? +- Can users see and revoke sessions associated with their devices? +- What happens to existing sessions after a password reset? +- How do we prevent refresh token replay? +- How do we enforce tenant boundaries consistently? +- How do we keep authorization decisions separate from authentication? +- Can multiple application types use the same backend security model? -In many applications, authentication is described as a short sequence: validate credentials, generate a token, return it to the client. +These concerns are related. Implementing each independently makes the system harder to maintain, audit, and extend. -That is only part of the story. +**UltimateAuth is designed to manage these concerns as parts of the same security lifecycle.** -A production authentication system must also answer questions such as: +## A Different Way to Think About Authentication -- 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? +Traditional authentication integration is often organized around credentials and their immediate results: -UltimateAuth treats authentication as a **controlled lifecycle**. Login, refresh, and logout are operations within that lifecycleβ€”not unrelated endpoints that happen to exchange tokens. +```text +Credentials + ↓ +Validate + ↓ +Issue Token + ↓ +Authenticated +``` -## Sessions Are First-Class +That sequence is necessary, but incomplete. -Where session-backed modes apply, UltimateAuth models authentication through a hierarchy: +UltimateAuth approaches authentication as a longer-running lifecycle: + +```text + UltimateAuth + β”‚ + β–Ό + Request Context + Client Β· Tenant Β· Device + β”‚ + β–Ό + Authentication + Validation Β· Decisions + β”‚ + β–Ό + Security State + User Β· Root Β· Chain Β· Session + β”‚ + β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” + β–Ό β–Ό β–Ό + Refresh Logout Revoke + β”‚ β”‚ β”‚ + β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β–Ό + Controlled State + Transitions +``` + +This model makes it possible to reason about authentication beyond a single request. + +Login establishes authentication. Refresh evaluates whether it may continue. Logout and revocation terminate authentication at the appropriate scope. Credential changes and security policies can affect existing sessions. + +The result is a framework where these operations share a consistent security vocabulary. + +## What Makes UltimateAuth Different? + +| Capability | UltimateAuth's approach | +|---|---| +| **Unified architecture** | Authentication, sessions, users, credentials, and authorization share a common foundation | +| **Client-aware behavior** | Authentication can adapt to the client profile and effective server configuration | +| **Session lifecycle** | Root, chain, and session concepts support different scopes of session management | +| **Flow-driven execution** | Authentication operations use explicit context, validation, decisions, and state transitions | +| **Credential lifecycle** | Credential changes, recovery, and session invalidation are related security operations | +| **Tenant awareness** | Tenant context participates in authentication and access decisions | +| **Modularity** | Core, Server, Client, and domain packages separate responsibilities | +| **Self-hosting** | Applications can operate their own authentication infrastructure | + + + +UltimateAuth does not claim that tokens, sessions, PKCE, or policy-based authorization are new concepts. + +Its value is in **bringing established security concepts together under a consistent architecture**, rather than requiring every application to connect them independently. + +## One Security Model Across Different Clients + +A browser application, a server-rendered application, and a native client have different security constraints. + +They should not necessarily use identical credential storage, transport, or authentication flows. + +UltimateAuth separates the common authentication model from client-specific integration. + +```text + Your .NET Applications + + Blazor Server Blazor WebAssembly + β”‚ β”‚ + β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ + UltimateAuth Client + β”‚ + β–Ό + UltimateAuth Server + β”‚ + β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” + β–Ό β–Ό β–Ό + Users Sessions Authorization + β”‚ β”‚ β”‚ + β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β–Ό + Shared Security + Lifecycle +``` + +The architecture also provides foundations for .NET MAUI, traditional ASP.NET Core applications, and API-oriented integrations. + +**Support depends on the client adapter and authentication modes available in the installed version.** A supported architectural profile should not be confused with a fully released platform adapter. + +The goal is to share authentication concepts without pretending that all clients have the same trust boundaries. + +## Sessions Are More Than Tokens + +One of UltimateAuth's central concepts is its stateful session hierarchy. ```text User - └── Root Global session-security boundary - └── Chain Device-associated lifecycle - └── Session Individual authentication instance + └── Root + β”œβ”€β”€ Chain β€” Browser A + β”‚ β”œβ”€β”€ Session + β”‚ └── Session + β”‚ + └── Chain β€” Device B + β”œβ”€β”€ Session + └── Session ``` -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. +Each level represents a different security scope. + +**Root** represents a broader authentication security boundary. + +**Chain** represents the continuity of authentication associated with a device or client context. + +**Session** represents an individual authentication instance within that chain. + +This distinction makes several operations easier to express: + +- End the current session. +- Revoke a particular device-associated chain. +- Revoke other active chains while preserving the current one. +- Revoke a broader authentication scope after a security-sensitive event. -**A token is not the entire authentication model.** It is a credential used within that model, subject to the applicable mode and validation rules. +A token is a credential used to access the system. It is not necessarily the complete record of the user's authentication lifecycle. -Not every authentication mode has identical persistence or session behavior. The session hierarchy describes the stateful lifecycle where that model is in use. +The hierarchy applies to session-backed authentication modes; different modes can have different persistence and validation characteristics. -## Flow-Driven by Design +[Explore Session Management](https://github.com/CodeBeamOrg/UltimateAuth) -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. +## Authentication Flows, Not Disconnected Endpoints + +UltimateAuth organizes security-sensitive operations around explicit flows. + +A flow can combine: + +1. Request and tenant context. +2. User, credential, and session resolution. +3. Input and security validation. +4. Authorization and authority decisions. +5. Controlled state transitions. +6. Client-appropriate results and responses. ```text Request - β†’ Tenant, session, and user resolution - β†’ AuthFlowContext - β†’ Flow-specific validation - β†’ Security decision and orchestration - β†’ State transition (when permitted) - β†’ Result and client-appropriate response + ↓ +AuthFlowContext + ↓ +Validation + ↓ +Security Decision + ↓ +Execution + ↓ +State Transition + ↓ +Response +``` + +This approach provides a consistent place for security rules to be evaluated. + +It also makes individual operations easier to test and inspect because the important decisions are part of an explicit execution model. + +**The purpose is not abstraction for its own sake. It is to make authentication behavior understandable and predictable.** + +## Security Continues After Login + +A secure authentication system must account for events that happen after a user signs in. + +| Event | UltimateAuth concept | +|---|---| +| A user signs in | Login flow | +| A session needs renewal | Refresh flow | +| A user signs out | Logout flow | +| A device should lose access | Chain revocation | +| A credential is changed | Credential lifecycle and session policy | +| An account is recovered | Recovery proof and session invalidation | +| A user requests a protected resource | Authorization decision | +| A request targets a tenant | Tenant-aware security context | + +This is why UltimateAuth treats session management, credential management, and authorization as connected parts of the system. + +For example, a password reset is not simply a database update. It can also affect the validity of existing authentication sessions according to the configured credential session policy. + +Similarly, logout and revocation are not interchangeable operations. They have different scopes and consequences. + +## Authentication and Authorization Together + +Knowing who a user is does not determine what that user is allowed to do. + +UltimateAuth separates authentication from authorization while keeping them within the same overall security architecture. + +Authentication establishes identity and applicable security context. + +Authorization evaluates whether an actor may perform an action on a resource. + +For example: + +```text +Actor: Authenticated user +Action: update +Resource: products +Result: Allowed or Denied ``` -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. +Roles and permissions can be managed through the authorization domain, while the server remains responsible for enforcing access decisions. + +This separation helps applications avoid spreading authorization logic across UI components, controllers, and unrelated service methods. + +Client-side authorization checks can improve the interface, but **server-side enforcement remains the security boundary**. + +## Tenant-Aware by Design + +Multi-tenant authentication involves more than adding a tenant identifier to a user record. + +A request must be interpreted within the correct tenant context, and security operations must respect that boundary. + +UltimateAuth provides tenant resolution and propagation mechanisms that participate in its request and flow infrastructure. + +This allows applications to build tenant-aware authentication and authorization behavior without treating tenancy as an unrelated afterthought. -For the implementation model, see [Auth Flows](../auth-flows/auth-flows.md). +Tenant context must still be configured correctly, and a client-supplied tenant identifier is never sufficient proof of authorization. -## Tenant-Aware from the Request Boundary +## Modular Without Becoming Fragmented -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 divided into packages with distinct responsibilities. -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. +| Package or domain | Responsibility | +|---|---| +| **Core** | Shared contracts, domain concepts, and authentication foundations | +| **Server** | Authentication execution, runtime configuration, and server integration | +| **Client** | Platform-independent authentication and domain APIs | +| **Client.Blazor** | Blazor-specific transport, storage, and authentication state integration | +| **Users** | User lifecycle, profiles, and identifiers | +| **Credentials** | Credential operations, changes, and recovery | +| **Authorization** | Roles, permissions, and access checks | +| **Policies** | Application access policy integration | + +This structure supports both a straightforward integration experience and deeper customization where the application requires it. + +Developers can begin with the public registration and client APIs without first learning every internal service. + +When necessary, they can explore the underlying flows, policies, stores, and extension points. + +## Designed for Practical .NET Development + +UltimateAuth is intended to fit naturally into .NET applications. + +A server application begins by registering UltimateAuth and configuring the security behavior it needs: + +```csharp +builder.Services.AddUltimateAuthServer(options => +{ + options.Login.MaxFailedAttempts = 5; + options.Session.Lifetime = TimeSpan.FromDays(7); + options.CredentialSessionPolicy.OnRecovery = CredentialSessionBehavior.RevokeAll; +}); +``` + +A Blazor application registers the client adapter: + +```csharp +builder.Services.AddUltimateAuthClientBlazor(options => +{ + options.AutoDetectClientProfile = true; + options.Endpoints.BasePath = "/auth"; +}); +``` -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.** +These examples illustrate the configuration entry points rather than a complete application setup. -## Built for Different Clients, Not Just Different Screens +The server and client have different responsibilities, and a complete deployment also needs the appropriate hosting, persistence, transport, and authentication integration. -UltimateAuth is designed for applications that may span several environments: +The important idea is that common behavior is configured through structured options rather than scattered application-specific 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. +[Start with QuickStart](/docs/getting-started/quickstart) -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. +## Who Is UltimateAuth For? -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 is particularly relevant if you are building: -## Security Is a Lifecycle, Too +- **A Blazor application** that needs authentication and authorization integrated with its hosting model. +- **A multi-client .NET system** where different frontends should share the same authentication backend. +- **An application with device-aware sessions** where users or administrators need control over active authentication. +- **A multi-tenant platform** with explicit tenant resolution and security boundaries. +- **A security-sensitive application** that requires controlled refresh, credential recovery, and revocation behavior. +- **A self-hosted system** where the team wants visibility into and control over its authentication infrastructure. -Security is not finished when login succeeds. UltimateAuth's architecture accounts for what happens *after* access is established: +**From a simple login page to a complex authentication platform, UltimateAuth is designed to make getting started easy without limiting what comes next.** -| 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 | +You shouldn't need to understand an entire identity framework just to add authentication to a small .NET application. And you shouldn't need to replace your authentication system when that application grows. -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 is built for both. -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). +### Simple Applications. Simple Beginnings. -## Modular and Extensible +For a small application, authentication should be straightforward. -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 aims to provide a lightweight integration experience with clear configuration, sensible defaults, and ready-to-use authentication flows. -This separation makes it possible to understand and evolve individual concerns without reducing authentication to a collection of disconnected custom handlers. +Instead of requiring developers to assemble numerous identity components before they can begin, UltimateAuth provides a cohesive foundation that can be configured through a small number of well-defined entry points. -**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. +**Start with what you need. You don't have to configure everything the framework supports.** -## When Should You Choose UltimateAuth? +### Complex Applications. Room to Grow. -UltimateAuth is especially relevant when: +When requirements become more demanding, the same framework provides a deeper security architecture. -- 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. +UltimateAuth is particularly useful for applications requiring multiple client types, device-aware session management, tenant isolation, advanced authorization, credential recovery, controlled refresh, and targeted revocation. -Also for a small application with minimal authentication needs, UltimateAuth brings the simplest possible solution. +Its modular architecture, explicit authentication flows, configurable policies, and extension points allow developers to introduce additional capabilities without abandoning the original authentication foundation. -## Open Source First +**The complexity of your application should not determine how difficult authentication is to begin with.** -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. +### One Framework for Both -The ambition is to build a framework that developers can adopt, inspect, contribute to, and trustβ€”not merely a service they must depend on. +Whether you're building a small personal project, a business application, a SaaS platform, or a larger distributed system, UltimateAuth follows the same principle: + +**Easy to start. Powerful when needed. Consistent as you grow.** + +You can begin with a straightforward authentication setup and progressively adopt the capabilities your application actually requires. + +UltimateAuth can also serve smaller applications, but its architecture becomes especially valuable when authentication requirements extend beyond a basic login form. + +If your application only needs a minimal authentication mechanism, evaluate whether UltimateAuth's broader lifecycle model provides enough value for your use case. + +## Current Scope and Maturity + +UltimateAuth is an evolving open-source framework. + +Its architecture covers several authentication models and client environments, but not every designed capability is necessarily available in the current release. + +In particular: + +- Authentication mode availability must be checked against the current server implementation. +- Client adapter availability varies by platform. +- Some advanced session and device policy options may be reserved for future enforcement. +- Production adoption requires appropriate configuration, persistence, deployment security, and testing. + +The documentation distinguishes supported behavior from planned or partially implemented capabilities. + +This matters because a trustworthy authentication framework should be clear not only about what it can do, but also about its current limits. + +## Open Source First. Self-Hosted by Design. + +UltimateAuth is developed as an open-source project with self-hosting at its foundation. + +The project's direction is to keep self-hosted authentication free, while a potential managed hosting service could provide an alternative for teams that prefer not to operate the infrastructure themselves. + +The intention is not to divide authentication capabilities into free and paid feature tiers. + +It is to make the framework accessible, inspectable, extensible, and useful to the .NET community. + +[View UltimateAuth on GitHub](https://github.com/CodeBeamOrg/UltimateAuth) + +## Choose Your Starting Point + +You do not need to understand the entire architecture before building your first application. + +| I want to… | Start here | +|---|---| +| Set up UltimateAuth for the first time | [QuickStart](/docs/getting-started/quickstart) | +| Understand how authentication works internally | [Auth Flows](/docs/auth-flows/overview) | +| Configure session, token, and security behavior | [Server Options](/docs/configuration/server-options) | +| Integrate authentication into a Blazor application | [Client Options](/docs/configuration/client-options) | +| Understand the source code and contribute | [GitHub Repository](https://github.com/CodeBeamOrg/UltimateAuth) | + +--- -## Where to Go Next +### Authentication is a lifecycle, not a login endpoint. -You do not need to understand every internal component before getting started. +UltimateAuth brings together the concepts needed to establish, maintain, evaluate, and terminate authentication within a consistent .NET architecture. -**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. +**Start simple. Understand the security model. Extend when you need to.** -**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. +[Continue to QuickStart](/docs/getting-started/quickstart) \ No newline at end of file diff --git a/docs/content/getting-started/quickstart.md b/docs/content/getting-started/quickstart.md index 1d5b1706..7eb68c3a 100644 --- a/docs/content/getting-started/quickstart.md +++ b/docs/content/getting-started/quickstart.md @@ -4,141 +4,338 @@ order: 3 group: getting-started --- -# ⚑ Quick Start +# ⚑ QuickStart -In this guide, you will set up UltimateAuth in a few minutes and perform your **first login**. +**Your first UltimateAuth application, in just a few steps.** ---- +This guide walks you through creating a Blazor Server application, adding UltimateAuth with in-memory storage, and signing in with a sample account. + +You'll start with a minimal setup. No database configuration, migrations, or custom authentication infrastructure required. + +## Prerequisites + +You'll need: -## 1. Create a Project +- .NET SDK (8 and higher, latest recommended) +- A code editor such as Visual Studio, VS Code, or Rider +- Basic familiarity with Blazor -Start by creating a new Blazor app: +This guide uses **Blazor Web App with Interactive Server rendering** and UltimateAuth's in-memory persistence bundle. + +## 1. Create a Blazor Application + +Open a terminal and create a new Blazor Interactive Server project: ```bash -dotnet new blazorserver -n UltimateAuthDemo +dotnet new blazor -n UltimateAuthDemo -int Server -ai cd UltimateAuthDemo ``` -## 2. Install Packages +This creates a Blazor Web App with Interactive Server components. -Install the required UltimateAuth packages: +## 2. Install UltimateAuth -```csharp +Install the Blazor client adapter and the in-memory bundle: + +```bash dotnet add package CodeBeam.UltimateAuth.Client.Blazor dotnet add package CodeBeam.UltimateAuth.InMemory.Bundle ``` -## 3. Configure Services +For this tutorial, also install the sample seed package: + +```bash +dotnet add package CodeBeam.UltimateAuth.Sample.Seed +``` + +The packages serve different purposes: + +| Package | Purpose | +|---|---| +| `Client.Blazor` | Blazor components, authentication state, and client integration | +| `InMemory.Bundle` | In-memory implementations for authentication domains | +| `Sample.Seed` | Development accounts and sample authorization data | + +The in-memory bundle includes the relevant persistence implementations, so you do not need to register each store individually. + +## 3. Configure UltimateAuth Services + +Open `Program.cs`. + +Add the following namespaces: + +```csharp +using CodeBeam.UltimateAuth.Client.Blazor; +using CodeBeam.UltimateAuth.Client.Blazor.Extensions; +using CodeBeam.UltimateAuth.InMemory; +using CodeBeam.UltimateAuth.Sample.Seed.Extensions; +using CodeBeam.UltimateAuth.Server.Extensions; +``` + +After creating the builder, register UltimateAuth: -Update your Program.cs: ```csharp builder.Services .AddUltimateAuthServer() .AddUltimateAuthInMemory(); -builder.Services - .AddUltimateAuthClientBlazor(); +builder.Services.AddUltimateAuthClientBlazor(); + +builder.Services.AddUltimateAuthSampleSeed(); ``` -## 4. Configure Middleware -In `Program.cs` +That's the core service configuration. + +`AddUltimateAuthServer()` registers the authentication server. + +`AddUltimateAuthInMemory()` provides in-memory stores. + +`AddUltimateAuthClientBlazor()` registers the Blazor client integration. + +`AddUltimateAuthSampleSeed()` prepares the development seed services. + +You don't need to call `AddUltimateAuth()` or `AddUltimateAuthClient()` separately. + +## 4. Configure the HTTP Pipeline + +In `Program.cs`, configure the application pipeline: + ```csharp +var app = builder.Build(); + +if (!app.Environment.IsDevelopment()) +{ + app.UseExceptionHandler("/Error", createScopeForErrors: true); + app.UseHsts(); +} + +app.UseHttpsRedirection(); +app.UseStaticFiles(); + app.UseUltimateAuthWithAspNetCore(); +app.UseAntiforgery(); app.MapUltimateAuthEndpoints(); -``` -## 5. Enable Blazor Integration -In `Program.cs` -```csharp app.MapRazorComponents() - .AddInteractiveServerRenderMode() // or webassembly (depends on your application type) + .AddInteractiveServerRenderMode() .AddUltimateAuthRoutes(UAuthAssemblies.BlazorClient()); + +if (app.Environment.IsDevelopment()) +{ + await app.SeedUltimateAuthAsync(); +} + +app.Run(); ``` -## 6. Add UAuth Script -Add this to `App.razor` or `index.html`: +Keep the `AddRazorComponents().AddInteractiveServerComponents()` registration generated by the Blazor project template. -```csharp +`UseUltimateAuthWithAspNetCore()` integrates UltimateAuth's middleware with ASP.NET Core authentication and authorization. + +`MapUltimateAuthEndpoints()` registers the authentication endpoints. + +`AddUltimateAuthRoutes()` includes UltimateAuth's Blazor client route assemblies in endpoint discovery. + +The development-only seed call creates the sample users before the application begins accepting requests. + +## 5. Add the UltimateAuth Script + +Open `Components/App.razor`. + +Keep the existing Blazor script and add UltimateAuth's JavaScript file: + +```html ``` -## 7. Configure Application Lifecycle -Replace `Routes.razor` with this code: -```csharp +Place these near the end of the document body. + +UltimateAuth's Blazor integration uses this script for browser-side functionality. + +## 6. Initialize the UltimateAuth Application + +Open `Components/Routes.razor`. + +Replace its contents with: + +```razor +@using CodeBeam.UltimateAuth.Client.Blazor + ``` -## 8. Recommended Setup (Optional) -Add these for better experience: +`UAuthApp` is the root integration component for UltimateAuth in Blazor. -For login page (Use this only once in your application) -```csharp -@attribute [UAuthLoginPage] +It initializes the client runtime, provides authentication state to child components, and coordinates the authenticated session lifecycle. + +With `UseBuiltInRouter="true"`, it also provides the application's router. + +You don't need to create a separate `Router` or `CascadingAuthenticationState` component for this setup. + +## 7. Add Usings + +Add this in `_Imports.razor` + +```razor +@using CodeBeam.UltimateAuth.Client +@using CodeBeam.UltimateAuth.Client.Blazor ``` -For protected pages -```csharp -@attribute [UAuthAuthorize] +## 8. Create Your Login Page + +Change `Components/Pages/Home.razor`: + +```razor +@page "/" +@page "/login" +@attribute [UAuthLoginPage] +@inherits UAuthPageBase + +
+

Sign in

+ +
+ + +
+ +
+ + +
+ + +
+
+ +@code { + private string _identifier = ""; + private string _password = ""; +} ``` -For any page that you use UltimateAuth features like AuthState etc. -```csharp +`UAuthLoginForm` handles the login submission through UltimateAuth's client flow. + +`[UAuthLoginPage]` identifies this component as the application's login page. + +Only designate one login page for this purpose. + +The form above is intentionally minimal. You can replace the inputs with your preferred UI components later. + +## 9. Add a Protected Page + +Create `Components/Pages/Dashboard.razor`: + +```razor +@page "/dashboard" +@attribute [UAuthAuthorize] @inherits UAuthPageBase +@inject IUAuthClient UAuthClient + +

Dashboard

+ +

You have reached an authenticated page.

+You have admin privileges. +

User: @AuthState?.Identity?.DisplayName

+ + + +@code { + private async Task Logout() + { + await UAuthClient.Flows.LogoutAsync(); + } +} ``` -## 9. Seed Data For QuickStart (Optional) -This code creates admin and user users with same password and admin role. +`[UAuthAuthorize]` declares an UltimateAuth authorization requirement for the page. -For in memory -```csharp -builder.Services.AddUltimateAuthSampleSeed(); +**Important:** Client-side route protection is not a substitute for server-side authorization. Protected APIs and resources must enforce authorization on the server. + +## 10. Run the Application + +Start the application: + +```bash +dotnet run ``` -For entity framework core, use scoped services: -```csharp -builder.Services.AddScopedUltimateAuthSampleSeed(); +Open the HTTPS address displayed in your terminal, then navigate to: + +```text +/login ``` -In pipeline configuration -```csharp -if (app.Environment.IsDevelopment()) -{ - await app.SeedUltimateAuthAsync(); -} +The sample seed package creates two development accounts: + +| Username | Password | Role | +|---|---|---| +| `admin` | `admin` | Administrator | +| `user` | `user` | Standard user | + +Sign in with: + +```text +Username: admin +Password: admin ``` -## 10. Perform Your First Login -Example using IUAuthClient: -```csharp -[Inject] IUAuthClient UAuthClient { get; set; } = null!; +After successful authentication, navigate to: -private async Task Login() -{ - await UAuthClient.Flows.LoginAsync(new LoginRequest - { - Identifier = "admin", - Secret = "admin" - }); -} +```text +/dashboard ``` -## πŸŽ‰ That’s It -You now have a working authentication system with: +You now have a Blazor application using UltimateAuth authentication. + +## πŸŽ‰ Your First UltimateAuth Application Is Running + +With this setup, you have: + +- An authentication server integrated into ASP.NET Core. +- In-memory user, credential, session, token, and authorization storage. +- A Blazor authentication client. +- A working login form. +- Authentication state initialization. +- Session lifecycle coordination. +- Sample users and authorization data. +- A page declaring an authentication requirement. + +You did not need to create your own password hashing, token issuance, session validation, or authentication middleware implementations. + +## What Happened When You Logged In? + +For this Blazor Server scenario, UltimateAuth uses the applicable session-oriented authentication model. -- Session-based authentication -- Automatic client detection -- Built-in login flow -- Secure session handling +The login flow validates the credentials and establishes authentication state on the server. -## What Just Happened? +Where a new session is created, it participates in UltimateAuth's root, chain, and session lifecycle. -When you logged in: +The Blazor client then coordinates authentication state for the application. -- A session (with root and chain) was created on the server, -- Your client received an authentication grant (cookie or token), -- UltimateAuth established your auth state automatically. +You can explore these operations in detail later through the Authentication Flows and Session Management documentation. -πŸ‘‰ You didn’t manage cookies, tokens, or redirects manually. +## Important: In-Memory Storage + +This QuickStart intentionally uses in-memory persistence. + +It is ideal for: + +- Learning UltimateAuth. +- Local development. +- Testing authentication flows. +- Building prototypes. + +**In-memory authentication data is not durable.** Users, credentials, sessions, and other stored state may be lost when the application restarts. + +The sample credentials are also for development only. Do not deploy the seeded accounts or their example passwords in a production application. + +For production use, configure durable persistence, appropriate secrets and signing keys, HTTPS, and the security settings required by your deployment. ## Next Steps -Discover the setup for real world applications with entity framework core. + +- **[Ecosystem](/docs/getting-started/ecosystem)** β€” Explore packages, integrations, and supported environments. +- **[Server Options](/docs/configuration/server-options)** β€” Configure sessions, tokens, login policies, and security behavior. +- **[Client Options](/docs/configuration/client-options)** β€” Customize Blazor integration and authentication flows. +- **[Authentication Flows](/docs/auth-flows/overview)** β€” Understand login, refresh, logout, and revocation. +- **[Entity Framework Core Setup](/docs/getting-started/real-world-setup#persistent-storage-with-entity-framework-core)** β€” Move from in-memory storage to persistent authentication data. \ No newline at end of file diff --git a/docs/content/getting-started/real-world-setup.md b/docs/content/getting-started/real-world-setup.md index 695b8e5a..4f98c524 100644 --- a/docs/content/getting-started/real-world-setup.md +++ b/docs/content/getting-started/real-world-setup.md @@ -4,214 +4,1079 @@ order: 4 group: getting-started --- -# πŸ— Real-World Setup +# πŸ—οΈ Real-World Setup -The Quick Start uses an in-memory setup for simplicity. -In real-world applications, you should replace it with a persistent configuration as shown below. +**One authentication framework. Different deployment models.** -In real applications, you will typically configure: +The [QuickStart](/docs/getting-started/quickstart) demonstrates UltimateAuth with Blazor Server and in-memory storage. -- A persistent database -- An appropriate client profile -- A suitable authentication mode +Real applications may require persistent data, a separate authentication server, browser-based clients, or independently deployed APIs. -This guide shows how to set up UltimateAuth for real-world scenarios. +UltimateAuth supports these scenarios through different service registrations and hosting integrations. You can choose the architecture that fits your application without configuring every available component. -## πŸ—„οΈ Using Entity Framework Core -For production, you should use a persistent store. (In-memory provider is volatile and automatically resets on each restart.) +This guide covers the setup patterns demonstrated by the UltimateAuth samples. -In this setup, you no longer need the `CodeBeam.UltimateAuth.InMemory.Bundle` package. +## 1. Choose Your Architecture -### Install Packages +Start by identifying where authentication is hosted and how your application communicates with it. + +| Application architecture | Authentication host | Application registration | +|---|---|---| +| Blazor Server, single project | Same application | Server + persistence + Blazor client | +| Blazor Server with EF Core | Same application | Server + EF Core + Blazor client | +| Dedicated UAuthHub | Separate authentication application | Server + persistence + Hub | +| Standalone Blazor WASM | Remote UAuthHub | Blazor client | +| Blazor Web App, Interactive WASM | Remote UAuthHub | Host integration + WASM client | +| Separate Resource API | Remote UAuthHub | Resource API integration | + +These models can be combined. + +For example, an application might use an EF Core-backed UAuthHub, a standalone Blazor WASM frontend, and a separate Resource API. + +The frontend handles user interaction, UAuthHub manages authentication, and the Resource API enforces access to protected resources. + +## 2. Persistent Storage with Entity Framework Core + +For applications that need authentication state to survive restarts, use a persistent storage provider. + +UltimateAuth provides an Entity Framework Core bundle that registers its reference domain implementations and EF Core-backed stores. + +### Install packages ```bash dotnet add package CodeBeam.UltimateAuth.EntityFrameworkCore.Bundle +dotnet add package Microsoft.EntityFrameworkCore.Sqlite +dotnet add package Microsoft.EntityFrameworkCore.Design ``` -### Configure Services +The SQLite provider is used here for simplicity. You can use another compatible EF Core database provider. + +When using the EF Core bundle, you do not need the in-memory bundle. + +### Register the services + +In `Program.cs`: + ```csharp -builder.Services - .AddUltimateAuthServer() +using CodeBeam.UltimateAuth.EntityFrameworkCore; +using CodeBeam.UltimateAuth.Server.Extensions; +using Microsoft.EntityFrameworkCore; + +builder.Services.AddUltimateAuthServer() .AddUltimateAuthEntityFrameworkCore(db => { - db.UseSqlite("Data Source=uauth.db"); - // or UseSqlServer(...) / UseNpgsql(...) / UseMySql(...) etc. + db.UseSqlite("Data Source=uauth.db", sqlite => sqlite.MigrationsAssembly(typeof(Program).Assembly.GetName().Name!)); }); - -builder.Services - .AddUltimateAuthClientBlazor(); ``` -### Database Migrations +`AddUltimateAuthEntityFrameworkCore()` registers the EF Core stores and reference domain implementations. -UltimateAuth integrates with Entity Framework Core, but database migrations belong to your application. +The bundle provides a unified `UAuthDbContext` for the supported authentication domains. -UltimateAuth does not automatically create or apply migrations on your behalf. This keeps your database schema lifecycle under your control and allows migrations to follow the same deployment and review process as the rest of your application. +You do not need to register each user, session, credential, token, or authorization store individually. -After configuring the Entity Framework Core provider, create the initial migration and update the database using either the .NET CLI or Visual Studio Package Manager Console. +### Configure the database connection -#### Option A β€” .NET CLI +For a deployed application, prefer configuration over hardcoded connection strings. -If you use the .NET CLI: +`appsettings.json`: -```bash -dotnet ef migrations add InitUAuth --context UAuthDbContext -dotnet ef database update --context UAuthDbContext +```json +{ + "ConnectionStrings": { + "UltimateAuth": "Data Source=uauth.db" + } +} ``` -If the dotnet ef command is not available, install the EF Core CLI tool: +`Program.cs`: + +```csharp +var connectionString = builder.Configuration.GetConnectionString("UltimateAuth") + ?? throw new InvalidOperationException("UltimateAuth connection string is missing."); + +builder.Services.AddUltimateAuthServer() + .AddUltimateAuthEntityFrameworkCore(db => + { + db.UseSqlite(connectionString); + }); +``` + +For production databases, use your deployment environment's secret management facilities rather than storing credentials in source control. + +### Database migrations + +UltimateAuth does not automatically create or apply migrations when the EF Core bundle is registered. + +Your application owns the database migration lifecycle. + +Install the EF Core CLI if necessary: ```bash dotnet tool install --global dotnet-ef ``` -Your project also needs the Entity Framework Core design package: +Create the initial migration: ```bash -dotnet add package Microsoft.EntityFrameworkCore.Design +dotnet ef migrations add InitUAuth --context UAuthDbContext ``` -#### Option B β€” Visual Studio Package Manager Console - -If you use Visual Studio, you can perform the same operation from Tools β†’ NuGet Package Manager β†’ Package Manager Console: +Apply it: ```bash +dotnet ef database update --context UAuthDbContext +``` + +For Visual Studio Package Manager Console: + +```powershell Add-Migration InitUAuth -Context UAuthDbContext Update-Database -Context UAuthDbContext ``` -For Package Manager Console tooling, make sure the required EF Core tooling package is available: +Install `Microsoft.EntityFrameworkCore.Tools` if required. + +### Applying migrations programmatically + +The EF Core samples demonstrate initialization through a scoped `UAuthDbContext`. + +For a controlled development environment: + +```csharp +using CodeBeam.UltimateAuth.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; + +using (var scope = app.Services.CreateScope()) +{ + var db = scope.ServiceProvider.GetRequiredService(); + await db.Database.MigrateAsync(); +} +``` + +This applies pending migrations. It does not generate migration files. + +**Production guidance:** Prefer applying reviewed migrations as a separate deployment step, particularly when multiple application instances share a database. + +Do not copy the sample initializer's `reset: true` behavior into production. The samples use database reset functionality for repeatable demonstrations, not as a safe deployment strategy. + +### Optional: sample accounts with EF Core + +The sample seed package can also work with EF Core persistence. ```bash -Install-Package Microsoft.EntityFrameworkCore.Tools +dotnet add package CodeBeam.UltimateAuth.Sample.Seed +``` + +Register the scoped seed services: + +```csharp +builder.Services.AddScopedUltimateAuthSampleSeed(); +``` + +After preparing the database: + +```csharp +if (app.Environment.IsDevelopment()) +{ + await app.SeedUltimateAuthAsync(); +} ``` -### Who Owns the Migrations? +The difference matters: + +- `AddUltimateAuthSampleSeed()` is used by the in-memory samples. +- `AddScopedUltimateAuthSampleSeed()` is used by the EF Core samples. + +Do not deploy the demonstration accounts and passwords to production. + +## 3. Blazor Server β€” Integrated Authentication -Your application does. +**Best for:** A Blazor Server application that wants to host its own authentication system. -This is intentional. UltimateAuth provides the authentication and identity model through its Entity Framework Core integration, while your application remains responsible for managing the resulting database schema. +This is the simplest deployment model for a server-side Blazor application. -This means you can: +The application hosts UltimateAuth Server and uses the Blazor client adapter in the same project. -- review migrations before applying them, -- include UltimateAuth schema changes in your normal deployment process, -- control when database changes are applied, -- maintain migration history alongside your application, -- use the database provider and deployment strategy appropriate for your environment. +A separate UAuthHub is not required. -When upgrading UltimateAuth, review the release notes for persistence-related schema changes and create a new migration when required. +### Packages -> **Tip:** Treat UltimateAuth model changes like any other Entity Framework Core model change: upgrade the package, create a migration, review the generated migration, and apply it through your normal deployment process. +```bash +dotnet add package CodeBeam.UltimateAuth.Client.Blazor +dotnet add package CodeBeam.UltimateAuth.EntityFrameworkCore.Bundle +dotnet add package Microsoft.EntityFrameworkCore.Sqlite +``` + +### Services -## Configure Services With Options -UltimateAuth provides rich options for server and client service registration. ```csharp -builder.Services.AddUltimateAuthServer(o => { - o.Diagnostics.EnableRefreshDetails = true; - o.Login.MaxFailedAttempts = 4; - o.Identifiers.AllowMultipleUsernames = true; -}); +using CodeBeam.UltimateAuth.Client.Blazor.Extensions; +using CodeBeam.UltimateAuth.EntityFrameworkCore; +using CodeBeam.UltimateAuth.Server.Extensions; +using Microsoft.EntityFrameworkCore; + +builder.Services + .AddRazorComponents() + .AddInteractiveServerComponents(); + +builder.Services.AddUltimateAuthServer() + .AddUltimateAuthEntityFrameworkCore(db => + { + db.UseSqlite("Data Source=uauth.db"); + }); + +builder.Services.AddUltimateAuthClientBlazor(); ``` -## Blazor Standalone WASM Setup -Blazor WASM applications run entirely on the client and cannot securely handle credentials. -For this reason, UltimateAuth uses a dedicated Auth server called **UAuthHub**. +### Pipeline -WASM `Program.cs`: ```csharp -builder.Services.AddUltimateAuthClientBlazor(o => +app.UseHttpsRedirection(); +app.UseStaticFiles(); + +app.UseUltimateAuthWithAspNetCore(); +app.UseAntiforgery(); + +app.MapUltimateAuthEndpoints(); + +app.MapRazorComponents() + .AddInteractiveServerRenderMode() + .AddUltimateAuthRoutes(UAuthAssemblies.BlazorClient()); +``` + +Also configure the `UAuthApp` root component, login page, and UltimateAuth JavaScript file as demonstrated in QuickStart. + +### Authentication behavior + +The default client profile resolution selects `PureOpaque` for Blazor Server. + +This uses session-oriented authentication rather than requiring the application to manage access and refresh tokens manually. + +You can configure session lifetimes, login policies, credential policies, and other behavior through `AddUltimateAuthServer()`. + +For example: + +```csharp +builder.Services.AddUltimateAuthServer(options => { - o.Endpoints.BasePath = "https://localhost:6110/auth"; // UAuthHub URL - o.Pkce.ReturnUrl = "https://localhost:6130/home"; // Your (WASM) application domain + return path + options.Login.MaxFailedAttempts = 5; + options.Login.LockoutDuration = TimeSpan.FromMinutes(15); + options.Session.Lifetime = TimeSpan.FromDays(7); + options.Identifiers.Behavior.AllowMultipleUsernames = true; }); ``` -UAuthHub `Program.cs`: +The effective authentication mode is resolved at runtime. The application does not need to select a single mode globally. + +## 4. UAuthHub β€” Dedicated Authentication Server + +**Best for:** Applications where multiple clients share one authentication authority. + +UAuthHub hosts UltimateAuth's authentication services separately from your frontend applications. + +It is particularly relevant for standalone WASM applications, Interactive WASM applications, and systems with separate Resource APIs. + +A typical architecture looks like this: + +```text + Standalone WASM Interactive WASM + β”‚ β”‚ + β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ + β–Ό + UAuthHub + Authentication + Sessions & Users + β”‚ + β–Ό + Persistence + + Resource API ───► UAuthHub + (validation) +``` + +### Create the Hub application + +A UAuthHub can be hosted in an ASP.NET Core Blazor Server application. + +The current samples use Blazor Server to provide the Hub's login UI. + +### In-memory UAuthHub + +For development: + +```bash +dotnet add package CodeBeam.UltimateAuth.Client.Blazor +dotnet add package CodeBeam.UltimateAuth.InMemory.Bundle +``` + +Register: + ```csharp +using CodeBeam.UltimateAuth.Client.Blazor.Extensions; +using CodeBeam.UltimateAuth.InMemory; +using CodeBeam.UltimateAuth.Server.Extensions; + builder.Services.AddUltimateAuthServer() .AddUltimateAuthInMemory() - .AddUAuthHub(o => o.AllowedClientOrigins.Add("https://localhost:6130")); // WASM application's URL + .AddUAuthHub(options => + { + options.AllowedClientOrigins.Add("https://localhost:6130"); + }); + +builder.Services.AddUltimateAuthClientBlazor(); ``` -UAuthHub Pipeline Configuration +The Hub's Blazor client registration is needed when the Hub itself hosts UltimateAuth-powered Blazor pages and login UI. + +### EF Core UAuthHub + +For persistent storage: + ```csharp +using CodeBeam.UltimateAuth.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; + +builder.Services.AddUltimateAuthServer() + .AddUltimateAuthEntityFrameworkCore(db => + { + db.UseSqlite("Data Source=uauthhub.db"); + }) + .AddUAuthHub(options => + { + options.AllowedClientOrigins.Add("https://localhost:6132"); + }); + +builder.Services.AddUltimateAuthClientBlazor(); +``` + +The `AllowedClientOrigins` collection identifies browser origins permitted to interact with the Hub. + +Use exact origins, including scheme and port. Do not include paths such as `/home`. + +### Hub pipeline + +```csharp +app.UseHttpsRedirection(); + +app.UseUltimateAuthWithAspNetCore(); +app.UseAntiforgery(); + app.MapUltimateAuthEndpoints(); app.MapUAuthHub(); + +app.MapStaticAssets(); + +app.MapRazorComponents() + .AddInteractiveServerRenderMode() + .AddUltimateAuthRoutes(UAuthAssemblies.BlazorClient()); +``` + +`MapUltimateAuthEndpoints()` maps the authentication endpoints. + +`MapUAuthHub()` maps the Hub-specific entry endpoint. + +Both registrations are used by the current UAuthHub samples. + +The Hub also needs its login page and Blazor application lifecycle components. + +### Choosing origins + +For example: + +| Application | URL | +|---|---| +| UAuthHub | `https://localhost:6112` | +| Standalone WASM | `https://localhost:6130` | +| Interactive WASM | `https://localhost:6132` | + +Configure the Hub's allowed origins for the actual frontend applications it serves. + +These localhost addresses are illustrative and must match your deployment. + +## 5. Blazor Standalone WebAssembly + +**Best for:** A browser-hosted application with a separate UAuthHub. + +Standalone Blazor WASM runs in the browser. It does not host UltimateAuth Server or database persistence. + +Authentication is handled by the remote UAuthHub. + +### Client packages + +```bash +dotnet add package CodeBeam.UltimateAuth.Client.Blazor +``` + +### Configure services + +In the standalone WASM application's `Program.cs`: + +```csharp +using CodeBeam.UltimateAuth.Client.Blazor.Extensions; +using CodeBeam.UltimateAuth.Core.Domain; + +var builder = WebAssemblyHostBuilder.CreateDefault(args); + +builder.RootComponents.Add("#app"); +builder.RootComponents.Add("head::after"); + +builder.Services.AddScoped(sp => new HttpClient +{ + BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) +}); + +builder.Services.AddUltimateAuthClientBlazor(options => +{ + options.Endpoints.BasePath = "https://localhost:6110/auth"; + options.Pkce.ReturnUrl = "https://localhost:6130/home"; + options.Login.AllowCredentialPost = true; + options.Reauth.Behavior = ReauthBehavior.RaiseEvent; +}); + +await builder.Build().RunAsync(); ``` -## Blazor Web App Setup -A blazor web app contains two projects that includes host and client. You need to arrange them both. +The current standalone WASM sample explicitly configures `Login.AllowCredentialPost` and the PKCE return URL. + +These are client-side settings. The UAuthHub must be configured independently. + +### What the URLs mean + +`Endpoints.BasePath` points to the remote authentication endpoints. + +`Pkce.ReturnUrl` identifies the frontend destination used by the relevant PKCE flow. + +The Hub must recognize the frontend's origin, and any return URL used in an authentication flow must be validated against the application's security policy. + +### Root component + +A standalone WASM application should initialize UltimateAuth through `UAuthApp`. + +For example, its root application or routing component can contain: + +```razor +@using CodeBeam.UltimateAuth.Client.Blazor + + +``` + +Include the UltimateAuth JavaScript asset in the application's HTML host page. + +### Authentication modes + +The default `BlazorWasm` profile resolves to `Hybrid` in the current mode resolver. + +Do not manually force `PureOpaque` for a standalone browser client simply because a Blazor Server application uses it. + +The transport and authentication lifecycle must match the client environment. + +## 6. Blazor Web App β€” Interactive WebAssembly + +**Best for:** A Blazor Web App with an ASP.NET Core host and a separate `.Client` project. + +This scenario differs from standalone WASM because components may participate in both server-side rendering and browser-side interactivity. + +The current UltimateAuth sample registers the Blazor client integration in **both projects**. + +It also adds a separate ASP.NET Core compatibility registration in the host. + +### Project structure + +```text +MyApplication/ +β”œβ”€β”€ MyApplication/ +β”‚ β”œβ”€β”€ Program.cs +β”‚ └── Components/ +β”‚ └── App.razor +β”‚ +└── MyApplication.Client/ + β”œβ”€β”€ Program.cs + └── Pages/ +``` + +The host serves the application. + +The `.Client` project contains browser-executed components and services. + +A remote UAuthHub manages authentication. + +### 6.1 Host project packages + +Install the Blazor client integration and ASP.NET Core compatibility package in the host: + +```bash +dotnet add package CodeBeam.UltimateAuth.Client.Blazor +dotnet add package CodeBeam.UltimateAuth.Client.AspNetCore +``` + +### 6.2 Host project services + +In the host `Program.cs`: -In the host project: ```csharp -builder.Services.AddUltimateAuthClientBlazor(o => +using CodeBeam.UltimateAuth.Client.AspNetCore; +using CodeBeam.UltimateAuth.Client.Blazor.Extensions; +using CodeBeam.UltimateAuth.Core.Domain; + +builder.Services + .AddRazorComponents() + .AddInteractiveWebAssemblyComponents(); + +builder.Services.AddUltimateAuthClientBlazor(options => { - o.Endpoints.BasePath = "https://localhost:6112/auth"; // UAuthHub URL - o.Pkce.ReturnUrl = "https://localhost:6132/home"; // Current application domain + path + options.Endpoints.BasePath = "https://localhost:6112/auth"; + options.Pkce.ReturnUrl = "https://localhost:6132/home"; + options.Login.AllowCredentialPost = true; + options.Reauth.Behavior = ReauthBehavior.RaiseEvent; }); -// In pipeline configuration +builder.Services.AddUltimateAuthAspNetCoreCompatibility(); +``` + +`AddUltimateAuthAspNetCoreCompatibility()` registers the compatibility authentication scheme and ASP.NET Core authorization services used by this hosting pattern. + +It does **not** turn the host into an UltimateAuth authentication server. + +It is also not equivalent to `AddUltimateAuthResourceApi()`. + +### 6.3 Host project pipeline + +```csharp +app.UseHttpsRedirection(); + +app.UseAntiforgery(); + +app.MapStaticAssets(); + app.MapRazorComponents() .AddInteractiveWebAssemblyRenderMode() - .AddAdditionalAssemblies(UAuthAssemblies.BlazorClient().First()); + .AddAdditionalAssemblies(typeof(MyApplication.Client._Imports).Assembly, UAuthAssemblies.BlazorClient().First()); +``` + +Replace `MyApplication.Client._Imports` with the corresponding type from your client project. + +The additional assemblies allow the host to discover routable components from the WASM client project and UltimateAuth's Blazor client assembly. + +The sample does not register `AddUltimateAuthServer()` in this host because authentication is delegated to a separate UAuthHub. + +### 6.4 Client project packages + +Install the Blazor client adapter in the `.Client` project: + +```bash +dotnet add package CodeBeam.UltimateAuth.Client.Blazor +``` + +### 6.5 Client project services + +In `MyApplication.Client/Program.cs`: + +```csharp +using CodeBeam.UltimateAuth.Client.Blazor.Extensions; +using CodeBeam.UltimateAuth.Core.Domain; + +var builder = WebAssemblyHostBuilder.CreateDefault(args); + +builder.Services.AddScoped(sp => new HttpClient +{ + BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) +}); + +builder.Services.AddUltimateAuthClientBlazor(options => +{ + options.Endpoints.BasePath = "https://localhost:6112/auth"; + options.Pkce.ReturnUrl = "https://localhost:6132/home"; + options.Login.AllowCredentialPost = true; + options.Reauth.Behavior = ReauthBehavior.RaiseEvent; +}); + +await builder.Build().RunAsync(); +``` + +The host and client configurations should agree on the remote UAuthHub URL and authentication flow settings. + +### Why both registrations are necessary + +The host and browser client have separate dependency injection containers. + +Registering `AddUltimateAuthClientBlazor()` in the host does not automatically register those services inside the WebAssembly runtime. + +Likewise, registering the client adapter only in the `.Client` project does not supply the services required during server-side integration. + +The additional ASP.NET Core compatibility registration addresses the host's ASP.NET Core authentication and authorization integration requirements. + +**Important:** This compatibility layer is not a replacement for server-side authorization of your protected business APIs. + +## 7. Resource API β€” Separate Protected Backend + +**Best for:** An API that needs UltimateAuth authentication validation but does not host the authentication system itself. + +A Resource API can be an ASP.NET Core Web API, MVC application, or another supported server-side application. + +It does not need to be a Blazor application. + +### Architecture + +```text + Blazor Client + β”‚ + β”Œβ”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β” + β–Ό β–Ό + UAuthHub Resource API + β–² β”‚ + β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + Authentication + validation +``` + +The Resource API uses the configured UAuthHub for remote authentication and session validation behavior. + +### Install the server integration package + +```bash +dotnet add package CodeBeam.UltimateAuth.Server ``` -In the client project: +### Configure services + +In the Resource API's `Program.cs`: + ```csharp -builder.Services.AddUltimateAuthClientBlazor(o => +using CodeBeam.UltimateAuth.Server.Extensions; + +var builder = WebApplication.CreateBuilder(args); + +builder.Services.AddControllers(); + +builder.Services.AddUltimateAuthResourceApi(options => { - o.Endpoints.BasePath = "https://localhost:6112/auth"; // UAuthHub URL - o.Pkce.ReturnUrl = "https://localhost:6132/home"; // Current application domain + path + options.UAuthHubBaseUrl = "https://localhost:6112"; + options.AllowedClientOrigins.Add("https://localhost:6132"); + options.CorsPolicyName = "resourceApi"; }); +``` + +`UAuthHubBaseUrl` is the base address of the authentication server. + +Notice that this example uses the Hub origin without the `/auth` suffix, matching the current Resource API samples. + +`AllowedClientOrigins` controls the browser origins permitted by the Resource API's CORS configuration. + +### Configure the pipeline + +```csharp +var app = builder.Build(); + +app.UseHttpsRedirection(); + +app.UseUltimateAuthResourceApiWithAspNetCore(); + +app.MapControllers(); + +app.Run(); +``` + +`UseUltimateAuthResourceApiWithAspNetCore()` installs the Resource API integration middleware together with ASP.NET Core authentication and authorization middleware. + +You do not need to register a local UltimateAuth Server or its persistence bundle in the Resource API. + +### Protecting endpoints + +Use ASP.NET Core authorization attributes for endpoints that require authentication: + +```csharp +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +[ApiController] +[Route("api/products")] +public class ProductsController : ControllerBase +{ + [HttpGet] + [Authorize] + public IActionResult GetProducts() + { + return Ok(new[] + { + new { Id = 1, Name = "Example product" } + }); + } +} +``` + +The authorization attribute is enforced by ASP.NET Core on the server. + +For permission-specific rules, configure and apply the corresponding authorization policies. + +Do not rely on Blazor UI authorization checks to protect Resource API endpoints. + +### CORS configuration + +The current Resource API integration registers CORS from the configured allowed origins. + +For example: + +```csharp +options.AllowedClientOrigins.Add("https://localhost:6132"); +``` + +Only add origins that should be allowed to call your API from a browser. + +CORS is a browser access control mechanism, not an authentication or authorization mechanism. + +A request must still pass the applicable authentication and authorization checks. + +### Resource API vs UAuthHub + +These are different responsibilities. + +| UAuthHub | Resource API | +|---|---| +| Hosts authentication flows | Hosts application resources | +| Manages authentication sessions | Validates remote authentication context | +| Uses authentication persistence | Does not require local authentication stores | +| Provides login and related endpoints | Provides application endpoints | +| Serves authentication clients | Serves authorized API consumers | + +Use a Resource API when you want independent deployment of business endpoints without duplicating the authentication server. + +## 8. Resource API with an EF Core-Backed UAuthHub + +The Resource API does not need to use EF Core just because UAuthHub does. + +For example: + +```text +Interactive WASM + β”‚ + β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Ί Resource API + β”‚ + β–Ό + UAuthHub + β”‚ + β–Ό + EF Core Database +``` -builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); +The Hub stores users, credentials, sessions, and related security state in its database. -// Optional if you use external API calls in your client project. -builder.Services.AddHttpClient("resourceApi", client => +The Resource API communicates with the Hub through the configured remote validation integration. + +Its registration remains: + +```csharp +builder.Services.AddUltimateAuthResourceApi(options => { - client.BaseAddress = new Uri("https://localhost:6122"); + options.UAuthHubBaseUrl = "https://localhost:6112"; + options.AllowedClientOrigins.Add("https://localhost:6132"); + options.CorsPolicyName = "resourceApi"; }); ``` -> If you want to use embedded UAuthHub in host project, you can register server services as shown in quickstart. +This separation allows the API and authentication infrastructure to be deployed, scaled, and maintained independently. + +## 9. Calling a Resource API from Blazor -> ℹ️ UltimateAuth automatically selects the appropriate authentication mode (PureOpaque, Hybrid, etc.) based on the client type. +The current samples demonstrate calling a separate Resource API from Blazor applications. -## ResourceApi Setup -You may want to secure your custom API with UltimateAuth. UltimateAuth provides a lightweight option for this case. (ResourceApi doesn't have to be a blazor application, it can be any server-side project like MVC.) +The client integration and the business API's HTTP client are separate concerns. + +### Interactive WASM host + +In the ASP.NET Core host: -ResourceApi's `Program.cs` ```csharp -builder.Services.AddUltimateAuthResourceApi(o => +builder.Services.AddHttpClient( + client => { - o.UAuthHubBaseUrl = "https://localhost:6110"; - o.AllowedClientOrigins.Add("https://localhost:6130"); + client.BaseAddress = new Uri("https://localhost:6122"); }); ``` -Configure pipeline: +### Interactive WASM client + +Register the same application API service in the `.Client` project: + ```csharp -app.UseUltimateAuthResourceApiWithAspNetCore(); +builder.Services.AddHttpClient( + client => + { + client.BaseAddress = new Uri("https://localhost:6122"); + }); +``` + +The host and client each require the service when application components or services execute in their respective environments. + +`SampleProductApiService` is an example application service, not a required UltimateAuth framework type. + +### Standalone WASM + +A standalone WASM application can register an HTTP client for its Resource API: + +```csharp +builder.Services.AddScoped(sp => new HttpClient +{ + BaseAddress = new Uri("https://localhost:6120") +}); +``` + +If your application has multiple HTTP clients, prefer explicitly named or typed clients to avoid ambiguity. + +The current samples also demonstrate browser request credentials handling for cookie-based requests. + +For cross-origin cookie authentication, the browser request, API CORS policy, cookie attributes, and Hub configuration must all be compatible. + +Do not assume that registering an `HttpClient` automatically forwards authentication credentials to every remote API. + +## 10. Authentication Modes and Client Profiles + +UltimateAuth separates **client profile detection** from **authentication mode enforcement**. + +The default resolver maps client profiles to authentication modes. + +| Client profile | Default resolved mode | +|---|---| +| Blazor Server | `PureOpaque` | +| Blazor WASM | `Hybrid` | +| Web Server | `Hybrid` | +| MAUI | `Hybrid` | +| API | `PureJwt` | + +This mapping describes the resolver's behavior, not a guarantee that every mode is currently fully supported by the server. + +In particular, `PureJwt` and `SemiHybrid` have implementation limitations in the current release. + +### Restrict allowed modes + +On the authentication server: + +```csharp +builder.Services.AddUltimateAuthServer(options => +{ + options.AllowedModes = + [ + UAuthMode.PureOpaque, + UAuthMode.Hybrid + ]; +}); +``` + +`AllowedModes` is a guardrail. + +It does not select a mode for every request. + +The effective mode is resolved from the client profile and then checked against the server's allowed-mode policy. + +For most applications, use the default mode resolution unless you have a specific integration requirement. + +## 11. Server Options for Real Applications + +Production configuration should reflect your application's security requirements. + +For example: + +```csharp +builder.Services.AddUltimateAuthServer(options => +{ + options.Login.MaxFailedAttempts = 5; + options.Login.LockoutDuration = TimeSpan.FromMinutes(15); + options.Identifiers.Behavior.AllowMultipleUsernames = true; + options.UserProfile.EnableMultiProfile = false; +}); +``` + +Server options also compose the Core-defined login, session, token, PKCE, and multi-tenant options. + +Configure those through `AddUltimateAuthServer()`. + +Do not independently configure Core options and assume the server will automatically use them. + +### Configuration files + +UltimateAuth also supports configuration through `appsettings.json`. + +```json +{ + "UltimateAuth": { + "Server": { + "Login": { + "MaxFailedAttempts": 5, + "LockoutDuration": "00:15:00" + } + } + } +} ``` -Notes: -- ResourceApi should connect with an UAuthHub, not a pure-server. Make sure `.AddUAuthHub()` after calling `builder.Services.AddUltimateAuthServer()`. -- UltimateAuth automatically configures CORS based on the provided origins. +The server options registration binds the `UltimateAuth:Server` configuration section. + +Remember that effective mode defaults and mode-specific overrides are applied after the base options have been configured. + +For detailed configuration behavior, see Server Options and Advanced Configuration. + +## 12. Development and Production Differences + +The samples intentionally prioritize visibility and repeatability. + +A production application needs additional safeguards. + +| Area | Sample or development | Production | +|---|---|---| +| Persistence | InMemory or local SQLite | Durable database | +| Seed accounts | Example users and passwords | Real provisioning | +| Database initialization | May reset sample data | Reviewed migrations | +| HTTPS | Local development certificate | Proper TLS termination | +| CORS | Localhost origins | Exact trusted origins | +| Signing keys | Development defaults | Managed, persistent keys | +| Diagnostics | Detailed debugging | Restrict sensitive diagnostics | +| Credential recovery | Development notifier | Real secure notification channel | +| Identifier verification | Development bypass | Proof-based verification | +| Secrets | Local settings | Secure configuration provider | + +The sample projects include development-only implementations for credential reset notifications and identifier verification. + +Those implementations demonstrate extension points and should not be treated as production verification systems. + +### Credential recovery + +If your application exposes credential recovery, configure a real implementation of the credential reset notifier. + +Recovery secrets must be delivered through an appropriate channel and must not be exposed directly through anonymous API responses. + +### Identifier verification + +If your application supports verified email addresses, phone numbers, or other identifiers, use an implementation that verifies ownership through suitable proof. + +Do not use a development bypass verifier in production. + +### Signing keys + +Configure persistent signing keys for authentication modes that issue JWTs. + +Development signing key providers are not a substitute for production key management. + +## 13. Deployment Checklist + +Before deploying, verify the following: + +- [ ] The selected architecture matches the actual client hosting model. +- [ ] The authentication server uses appropriate persistent storage. +- [ ] Database migrations have been reviewed and applied. +- [ ] Sample users and development-only verification services are disabled. +- [ ] Production signing keys and secrets are configured. +- [ ] HTTPS is enforced at the appropriate boundary. +- [ ] UAuthHub and Resource API origins are explicitly configured. +- [ ] Authentication endpoints and application endpoints are protected appropriately. +- [ ] Client and server endpoint URLs match the deployment. +- [ ] Session expiration, refresh, logout, and revocation have been tested. +- [ ] Protected resources enforce server-side authorization. +- [ ] Authentication state and persistence survive the expected application lifecycle. + +## 14. Choosing Your Architecture + +**Start with one server and one client. Connect more clients whenever you need them.** + +UltimateAuth is built around two fundamental responsibilities: + +- **UltimateAuth Server** β€” Manages authentication, users, credentials, sessions, authorization, and the security lifecycle. +- **UltimateAuth Client** β€” Integrates an application with the authentication system using the appropriate client profile and authentication flows. + +Every application needs access to an authentication server, but that server does not have to be a separate application. It can be hosted within your existing ASP.NET Core application or deployed independently as UAuthHub. + +### Single Application β€” The Simplest Setup + +For a traditional ASP.NET Core or Blazor Server application, UltimateAuth Server and Client can run together. + +```text +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ Your Application β”‚ +β”‚ β”‚ +β”‚ UltimateAuth Client β”‚ +β”‚ β”‚ β”‚ +β”‚ UltimateAuth Server β”‚ +β”‚ β”‚ β”‚ +β”‚ Database β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +``` + +This approach is ideal when you want authentication without introducing another service or deployment. + +### Multiple Applications β€” One Authentication Server + +As your system grows, you can connect multiple client applications to the same UltimateAuth Server. + +```text + Blazor Server Blazor WASM + β”‚ β”‚ + β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ + β”‚ β”‚ β”‚ + Razor Pages MAUI Other .NET Clients + β”‚ β”‚ β”‚ + β””β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ + β–Ό + UltimateAuth Server + UAuthHub + β”‚ + β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” + β–Ό β–Ό β–Ό + Users Sessions Authorization + β”‚ + β–Ό + Persistence +``` + +Each client can have its own application structure, hosting model, and authentication requirements while sharing the same authentication authority. + +For example, a business platform might use Blazor Server for administration, Blazor WASM for its customer portal, MAUI for mobile access, and Razor Pages for an internal application. + +**Adding another client does not require replacing the existing clients or creating another UltimateAuth Server.** + +The same users, credentials, security policies, and session infrastructure can serve multiple applications, subject to the authentication modes and client integrations supported by the installed version. + +### Distributed Applications β€” Separate Resource APIs + +When your architecture includes independently deployed backend services, Resource APIs can integrate with the same authentication authority. + +```text + Multiple Client Applications + β”‚ + β”Œβ”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β” + β–Ό β–Ό + UAuthHub Resource APIs + β–² β”‚ + β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + Authentication + Validation +``` + +Resource APIs protect application resources without becoming additional authentication servers. + +This lets you separate business services from authentication infrastructure while maintaining a shared security model. + +### One Foundation, Different Scales -Use ResourceApi when: +| Your scenario | Recommended approach | +|---|---| +| A simple Blazor Server application | Integrated Server + Client | +| A traditional ASP.NET Core application | Server hosted within the application | +| A standalone Blazor WASM application | Client + remote UAuthHub | +| An Interactive WASM application | Host + WASM Client + remote UAuthHub | +| Multiple web, desktop, or mobile applications | Multiple Clients + shared UAuthHub | +| Multiple backend services | Shared UAuthHub + Resource APIs | -- You have a separate backend API -- You want to validate sessions or tokens externally -- Your API is not hosting UltimateAuth directly +**UltimateAuth does not require a complex architecture to get started, and it does not restrict you to a single client as your application grows.** -## 🧠 How to Think About Setup +Start with the simplest deployment that meets your needs. Add new client applications, persistent storage, or separate Resource APIs as your requirements evolve. -In UltimateAuth: +The authentication foundation remains consistent. -- The **Server** manages authentication flows and sessions -- The **Client** interacts through flows (not tokens directly) -- The **Storage layer** (InMemory / EF Core) defines persistence -- The **Application type** determines runtime behavior +## Next Steps -πŸ‘‰ You configure the system once, and UltimateAuth adapts automatically. +- [QuickStart](/docs/getting-started/quickstart) β€” Build the minimal in-memory application. +- [Ecosystem](/docs/getting-started/ecosystem) β€” Explore packages and supported integrations. +- [Server Options](/docs/configuration/server-options) β€” Configure server authentication and security behavior. +- [Client Options](/docs/configuration/client-options) β€” Configure client profiles, endpoints, refresh, and reauthentication. +- [Advanced Configuration](/docs/configuration/advanced-configuration) β€” Explore effective options, dependency injection, and extension points. \ No newline at end of file diff --git a/docs/content/plugin-domains/authorization-domain.md b/docs/content/plugin-domains/authorization-domain.md deleted file mode 100644 index 1b9e0c37..00000000 --- a/docs/content/plugin-domains/authorization-domain.md +++ /dev/null @@ -1,188 +0,0 @@ ---- -title: Authorization -order: 4 -group: plugin-domains ---- - - -# πŸ” Authorization & Policies - -UltimateAuth provides a flexible and extensible authorization system based on: - -- Roles -- Permissions -- Policies -- Access orchestration - -## 🧩 Core Concepts - -### πŸ”‘ Permissions - -In UltimateAuth, permissions are not just arbitrary strings. - -They follow a **structured action model**. - -#### 🧩 Permission Structure - -Permissions are built using a consistent format: - -``` -resource.operation.scope -``` -or -``` -resource.subresource.operation.scope -``` - -#### βœ… Examples - -- `users.create.admin` -- `users.profile.update.self` -- `sessions.revokechain.admin` -- `credentials.change.self` - -πŸ‘‰ This structure is not accidental β€” -it is **designed for consistency, readability, and policy evaluation**. - ---- - -### βš™οΈ Built-in Action Catalog - -UltimateAuth provides a predefined action catalog. - -Examples: - -- `flows.logout.self` -- `sessions.listchains.admin` -- `users.delete.self` -- `credentials.revoke.admin` -- `authorization.roles.assign.admin` - -πŸ‘‰ This ensures: - -- No magic strings -- Discoverable permissions -- Consistent naming across the system - -
- -### 🧠 Scope Semantics - -The last part of the permission defines **scope**: - -| Scope | Meaning | -|------------|----------------------------------| -| `self` | User acts on own resources | -| `admin` | User acts on other users | -| `anonymous`| No authentication required | - -
- -### 🌲 Wildcards & Grouping - -Permissions support hierarchical matching: - -- `users.*` β†’ all user actions -- `users.profile.*` β†’ all profile operations -- `*` β†’ full access - -### ⚑ Normalization - -Permissions are automatically normalized: - -- Full coverage β†’ replaced with `*` -- Full group β†’ replaced with `prefix.*` - -
- -### Role - -A role is a collection of permissions. - -- Roles are tenant-scoped -- Roles can be dynamically updated -- Permissions are normalized internally - -### UserRole - -Users are assigned roles: - -- Many-to-many relationship -- Assignment is timestamped -- Role resolution is runtime-based - -
- -## πŸ”„ Permission Resolution - -Permissions are evaluated using: - -- Exact match -- Prefix match -- Wildcard match - -CompiledPermissionSet optimizes runtime checks. - -
- -## 🧠 Claims Integration - -Authorization integrates with authentication via claims: - -- Roles β†’ `ClaimTypes.Role` -- Permissions β†’ `permission` claim -- Tenant β†’ `tenant` - -This allows: - -- Token-based authorization -- Stateless permission checks (for JWT modes) - -
- -## βš™οΈ Authorization Flow - -Authorization is executed through: - -πŸ‘‰ AccessOrchestrator - -Steps: - -1. Build AccessContext -2. Execute policies -3. Allow or deny operation - -
- -## πŸ›‘ Policies - -Policies are the core of authorization logic. - -Default policies include: - -- RequireAuthenticated -- DenyCrossTenant -- RequireActiveUser -- RequireSelf -- RequireSystem -- MustHavePermission - -
- -## πŸ”Œ Plugin Integration - -Authorization is a plugin domain. - -It: - -- Does NOT depend on other domains -- Uses contracts only -- Integrates via policies and claims - -## 🎯 Key Takeaways - -- Authorization is policy-driven -- Roles are permission containers -- Permissions support wildcard & prefix -- Policies enforce rules -- Fully extensible and replaceable diff --git a/docs/content/plugin-domains/authorization.md b/docs/content/plugin-domains/authorization.md new file mode 100644 index 00000000..d9754b53 --- /dev/null +++ b/docs/content/plugin-domains/authorization.md @@ -0,0 +1,535 @@ +--- +title: Authorization +order: 4 +group: plugin-domains +--- + +# Authorization & Policies + +Authentication establishes an identity. Authorization determines what that identity is allowed to do. + +UltimateAuth treats authorization as a separate responsibility, built around **structured actions, permissions, roles, and policies**. + +Rather than relying exclusively on role names or scattered permission checks, UltimateAuth provides a consistent model for describing protected operations and evaluating access within their security context. + +The Authorization Plugin Domain extends this model with permission management, role assignments, and supporting infrastructure. + +## Understanding Authorization + +A successful authentication does not automatically grant access to protected operations. + +An authenticated identity may be permitted to read its own profile while being prohibited from modifying another user's account. An administrator may have elevated permissions within one tenant without having authority over another tenant. + +These decisions require more than knowing whether the caller is authenticated. + +UltimateAuth separates authorization into several responsibilities: + +| Concept | Responsibility | +|---|---| +| **Action** | Identifies the protected operation | +| **Permission** | Represents authority to perform an action | +| **Role** | Groups permissions for assignment | +| **Policy** | Evaluates the conditions required to allow access | +| **AccessContext** | Carries the identity, target, tenant, and operation context | +| **AccessOrchestrator** | Coordinates policy evaluation and the access decision | + +These concepts work together but are not interchangeable. + +A permission can indicate that an identity is authorized to perform a class of operations. A policy may still deny a particular request because its target, tenant, or security context is invalid. + +**Authorization is therefore a decision process, not simply a permission lookup.** + +## Structured Actions + +UltimateAuth identifies protected operations through a structured action model. + +Actions use a hierarchical naming convention: + +```text +resource.operation.scope +``` + +For operations involving a more specific resource: + +```text +resource.subresource.operation.scope +``` + +Examples include: + +```text +users.create.admin +users.delete.self +users.profile.update.self +credentials.change.self +sessions.revokechain.admin +``` + +The structure makes the intended operation and its access boundary explicit. + +| Segment | Example | Meaning | +|---|---|---| +| Resource | `users` | The primary protected domain | +| Subresource | `profile` | An optional, more specific resource | +| Operation | `update` | The requested behavior | +| Scope | `self` | The intended access boundary | + +This convention supports consistent naming, grouping, permission matching, and policy evaluation. + +It also helps developers understand an operation without needing to inspect its endpoint implementation. + +### UAuthActions + +`UAuthActions` is UltimateAuth's built-in action catalog. + +It provides predefined identifiers for framework operations so that services, endpoints, and authorization logic can refer to the same action consistently. + +Examples of built-in actions include: + +```text +flows.logout.self +sessions.listchains.admin +users.delete.self +credentials.revoke.admin +authorization.roles.assign.admin +``` + +Using a shared action catalog avoids duplicating string literals throughout the framework and reduces the risk of inconsistent permission names. + +Actions are identifiers, not authorization decisions. + +Referencing an action does not automatically authorize a request. The action must be evaluated against the applicable authorization rules. + +Applications can also define actions for their own protected resources using the same naming convention. + +## Understanding Scope + +The final segment of an action describes its intended security scope. + +UltimateAuth distinguishes three principal scopes: + +| Scope | Meaning | +|---|---| +| `self` | An operation targeting the caller's own identity or resources | +| `admin` | An operation performed with elevated authority over another target | +| `anonymous` | An operation that can be initiated without an authenticated identity | + +The scope is part of the action's identity. + +### Self and Admin Are Different Permissions + +Consider: + +```text +users.delete.self +users.delete.admin +``` + +The first represents the authority to delete one's own account. + +The second represents the authority to perform an administrative deletion. + +Granting `users.delete.self` must not implicitly grant `users.delete.admin`. + +Likewise, an authenticated user must not gain administrative access simply by supplying another user's identifier to a self-service operation. + +The application must establish the actual relationship between the actor and the target. + +**The requested scope is not proof that the caller satisfies that scope.** + +A self operation requires a valid self relationship. An administrative operation requires the corresponding authority and must satisfy the applicable target and tenant policies. + +### Anonymous Does Not Mean Unrestricted + +An anonymous action is designed to be initiated without an authenticated session. + +Examples include certain login and credential recovery operations. + +However, anonymous availability does not remove the need for validation, proof requirements, abuse protection, or operation-specific security policies. + +An anonymous credential reset request, for example, must not be allowed to replace a password without completing the required recovery proof. + +## Permissions + +A permission represents authority associated with a protected action. + +In UltimateAuth, permission identifiers follow the same structured action convention. + +For example: + +```text +users.profile.update.self +``` + +A user granted this permission may be eligible to update their own profile, provided the other required policies succeed. + +Permissions do not replace those policies. + +A permission match answers whether the relevant authority is present. It does not independently establish that the target belongs to the caller, that the tenant boundary is valid, or that the account satisfies the required security conditions. + +### Hierarchical Permissions + +UltimateAuth supports hierarchical permission matching. + +Examples: + +| Permission | Intended coverage | +|---|---| +| `users.profile.update.self` | One specific action | +| `users.profile.*` | Actions under the profile group | +| `users.*` | Actions under the users resource | +| `*` | All actions covered by the permission matcher | + +This allows permissions to be granted at different levels of granularity. + +An application can assign a narrowly scoped permission to ordinary users while granting broader permission groups to trusted administrative roles. + +### Permission Normalization + +UltimateAuth provides permission normalization to reduce redundant representations. + +When a permission set fully covers a known group of actions, the corresponding permissions can be represented by a wildcard for that group. + +For example, complete coverage of a defined group may be normalized to: + +```text +users.profile.* +``` + +Normalization is intended to preserve the effective permission coverage, not expand it. + +A wildcard must not be introduced merely because several permissions share a prefix. The relevant group must actually be fully covered according to the known permission catalog. + +### Compiled Permission Sets + +Permission resolution may involve exact matches, hierarchical groups, and wildcards. + +`CompiledPermissionSet` provides an optimized representation for evaluating these permissions at runtime. + +This allows the authorization system to avoid repeatedly performing the same permission-processing work during access checks. + +The optimization does not change the authorization model: permission matching remains one part of the overall access decision. + +## Roles and Role Assignments + +Roles provide a convenient way to manage collections of permissions. + +Instead of assigning every permission directly to every user, an application can define roles corresponding to its operational responsibilities. + +For example: + +```text +SupportAgent +AccountManager +TenantAdministrator +``` + +Each role can contain the permissions required for that responsibility. + +A role is not inherently a security bypass or a substitute for policy evaluation. + +### Role Management + +The reference authorization model supports role management and user-role assignments. + +Its responsibilities include: + +- Defining roles and their permissions. +- Assigning roles to users. +- Managing role membership. +- Resolving effective permissions. +- Maintaining the relevant tenant boundaries. + +Roles and assignments are designed to support tenant-aware authorization. + +An administrative role in one tenant must not automatically imply authority in another tenant. + +### Roles Versus Permissions + +Roles describe how authority is organized and assigned. + +Permissions describe which operations that authority covers. + +Policies determine whether the current operation is permitted under the relevant conditions. + +Keeping these concepts separate allows applications to change role structures without redesigning their protected operations. + +## Authorization Policies + +Policies are the enforcement layer of UltimateAuth's authorization model. + +A policy evaluates a security requirement against the current access context. + +UltimateAuth includes policy concepts such as: + +| Policy | Responsibility | +|---|---| +| `RequireAuthenticated` | Requires an authenticated actor | +| `RequireActiveUser` | Requires the relevant active-user condition | +| `RequireSelf` | Enforces the self-access relationship | +| `RequireSystem` | Restricts operations to an appropriate system context | +| `DenyCrossTenant` | Enforces tenant isolation | +| `MustHavePermission` | Requires the applicable permission | + +These policies address different questions. + +For example, `MustHavePermission` may confirm that the actor has `users.delete.admin`, while `DenyCrossTenant` may independently prevent the operation from targeting a user in an unauthorized tenant. + +A successful permission match must not bypass another mandatory security policy. + +### Policies Are Composable + +A protected operation may require multiple conditions. + +An administrative account operation might require: + +1. An authenticated actor. +2. A valid and active identity. +3. Permission for the requested administrative action. +4. A valid target identity. +5. Compliance with tenant isolation rules. + +Authorization succeeds only when the required conditions are satisfied. + +This approach allows UltimateAuth to separate reusable security requirements from the business operation being protected. + +## AccessContext and AccessOrchestrator + +Authorization needs a trustworthy representation of the operation being evaluated. + +`AccessContext` carries the relevant information for the decision, including the actor, target, tenant context, and requested operation. + +The `AccessOrchestrator` coordinates the applicable policy evaluations and produces the authorization decision. + +Conceptually, the process is: + +```text +Authenticated identity + | + v +Requested action + | + v +Build AccessContext + | + v +Evaluate required policies + | + +---- Denied ---> Reject operation + | + v + Allowed + | + v +Execute protected operation +``` + +The context must be constructed from trusted authentication state and validated operation data. + +A caller-provided user ID, tenant ID, or scope string must not automatically be accepted as authoritative. + +This is particularly important for administrative operations and multi-tenant applications. + +### Authorization and Authentication Are Separate + +`AuthFlowContext` and `AccessContext` serve different purposes. + +`AuthFlowContext` belongs to authentication flow execution. + +`AccessContext` belongs to authorization evaluation. + +A successfully completed authentication flow may establish the identity required for an access context, but it does not replace authorization evaluation. + +See [Flow-Based Authentication](/docs/fundamentals/flow-based-authentication) and [Request Lifecycle](/docs/fundamentals/request-lifecycle). + +## Claims and Permission Resolution + +UltimateAuth can integrate authorization information with authentication claims. + +Relevant claims may include: + +```text +ClaimTypes.Role +permission +tenant +``` + +Claims allow authorization information to participate in token-based and ASP.NET Core authorization scenarios. + +However, claims are representations of authorization information, not necessarily its authoritative source. + +### Session-Based Authorization + +In session-authoritative authentication modes, authorization can rely on server-side state and the relevant runtime resolution mechanisms. + +Changes to roles, permissions, or account security state can be reflected according to the session validation and authorization configuration. + +### JWT-Based Authorization + +In JWT-based modes, roles or permissions may be included in issued tokens. + +A protected resource can evaluate those claims without contacting the authentication server on every request. + +This introduces an important tradeoff. + +**A permission removed after token issuance may remain represented in an already-issued JWT until that token expires.** + +Applications requiring immediate permission revocation must account for this behavior through their token lifetimes, refresh strategy, or additional server-side validation. + +The authorization model remains consistent, but the freshness of authorization information depends on how it is resolved. + +## Protecting Application Endpoints + +UltimateAuth's authorization architecture is not limited to its built-in identity management endpoints. + +Applications can protect their own business resources using ASP.NET Core authorization and, where integrated, UltimateAuth's action and policy model. + +### Requiring Authentication + +For a controller-based resource API, standard ASP.NET Core authorization attributes can require an authenticated caller. + +```csharp +[ApiController] +[Route("api/products")] +[Authorize] +public sealed class ProductsController : ControllerBase +{ + [HttpGet] + public IActionResult GetProducts() + { + return Ok(); + } +} +``` + +When the application is configured with the appropriate UltimateAuth authentication integration, `[Authorize]` requires a valid authenticated identity. + +This is the simplest protection boundary. + +It does not, by itself, require a particular UltimateAuth action permission. + +### Requiring a Named Policy + +ASP.NET Core also supports policy-based endpoint authorization. + +For example: + +```csharp +[Authorize(Policy = "Products.Read")] +[HttpGet] +public IActionResult GetProducts() +{ + return Ok(); +} +``` + +The named policy must be registered in the application's authorization configuration. + +A standard ASP.NET Core policy can require a particular permission claim: + +```csharp +builder.Services.AddAuthorization(options => +{ + options.AddPolicy("Products.Read", policy => + { + policy.RequireAuthenticatedUser(); + policy.RequireClaim( + "permission", + "products.read.self"); + }); +}); +``` + +This example uses ASP.NET Core's built-in claim authorization. + +It checks for the specified claim in the authenticated principal. It does not automatically execute UltimateAuth's `AccessOrchestrator` or its domain policies. + +It is suitable when the application's security model permits authorization based on the available claims. + +### Using UltimateAuth Actions for Application Resources + +Applications can define their own action identifiers using the same convention as UltimateAuth's built-in catalog. + +For example: + +```csharp +public static class ProductActions +{ + public const string ReadSelf = "products.read.self"; + public const string CreateAdmin = "products.create.admin"; + public const string DeleteAdmin = "products.delete.admin"; +} +``` + +These constants establish consistent names for application permissions. + +They can be used by permission management, policy registration, and endpoint authorization integrations. + +Defining the constants alone does not register them with the authorization system or enforce them at an endpoint. + +For operations requiring actor-target validation, tenant isolation, or other UltimateAuth domain policies, the application must use the appropriate UltimateAuth authorization integration rather than relying solely on a permission claim. + +The exact registration and invocation APIs depend on the configured authorization packages and hosting model. + +## Authorization in Different Hosting Models + +UltimateAuth supports different hosting architectures, and the enforcement boundary matters. + +### Resource APIs + +For HTTP resource APIs, authentication and authorization are normally enforced through the ASP.NET Core request pipeline. + +Controllers and endpoints can use standard authorization attributes and policies. + +The UltimateAuth resource API integration establishes the authentication context required for protected requests. + +### Integrated Server Applications + +In an integrated server application, business operations may execute directly through application services without making an HTTP request. + +Such calls do not automatically pass through ASP.NET Core endpoint authorization attributes. + +If an operation bypasses the HTTP endpoint, authorization must be enforced at an appropriate service or application boundary. + +This is particularly relevant for Blazor Server applications that invoke services directly. + +**Protecting an HTTP controller does not automatically protect every direct call to the underlying service.** + +Authorization must remain effective at the actual execution boundary. + +## Extending Authorization + +Applications may need policies beyond the framework's built-in requirements. + +Examples include: + +- Resource ownership rules. +- Organization-specific access restrictions. +- Context-dependent administrative authority. +- Business workflow conditions. +- Additional tenant or resource boundaries. + +UltimateAuth's policy-driven architecture is intended to support such requirements without embedding them directly into the authentication engine. + +The reference Authorization domain provides a foundation for roles, permissions, and their management. + +Applications can build on that foundation while keeping application-specific authorization rules close to the resources and operations they protect. + +Detailed customization, custom policy implementation, and advanced permission registration are covered separately. + +## Architectural Perspective + +UltimateAuth's authorization model is built around a distinction: + +**Actions describe operations. Permissions represent authority. Policies enforce security requirements.** + +Roles make permissions manageable, while `AccessContext` and `AccessOrchestrator` provide the context and coordination needed for authorization decisions. + +This allows applications to use consistent authorization semantics across authentication modes, hosting models, and business domains without reducing security to a collection of role checks. + +## Next Steps + +- [Users](/docs/plugin-domains/users) β€” Identity ownership, account lifecycle, and user management. +- [Credentials](/docs/plugin-domains/credentials) β€” Authentication evidence and credential security. +- [Plugin Domains](/docs/plugin-domains) β€” The broader Plugin Domain architecture. +- [Request Lifecycle](/docs/fundamentals/request-lifecycle) β€” How authentication and authorization participate in protected requests. \ No newline at end of file diff --git a/docs/content/plugin-domains/credential-domain.md b/docs/content/plugin-domains/credential-domain.md deleted file mode 100644 index 1f9f5813..00000000 --- a/docs/content/plugin-domains/credential-domain.md +++ /dev/null @@ -1,122 +0,0 @@ ---- -title: Credentials -order: 3 -group: plugin-domains ---- - - -# πŸ” Credentials Domain - -Credentials in UltimateAuth define how a user proves their identity. - -## 🧠 Core Concept - -Authentication is not tied to users directly. - -πŸ‘‰ It is performed through credentials. - -
- -## πŸ”‘ What is a Credential? - -A credential represents a secret or factor used for authentication. - -Examples: - -- Password -- OTP (future) -- External providers (future) - -
- -## πŸ”’ Password Credential - -The default credential type is password. - -A password credential contains: - -- Hashed secret (never raw password) -- Security state (active, revoked, expired) -- Metadata (last used, source, etc.) - -πŸ‘‰ Credentials are always stored securely and validated through hashing. - -
- -## βš™οΈ Credential Validation - -Credential validation is handled by a validator: - -- Verifies secret using hashing -- Checks credential usability (revoked, expired, etc.) - -πŸ‘‰ Validation is isolated from business logic. - -
- -## πŸ”— Integration with Users - -Credentials are NOT created directly inside user logic. - -Instead: - -πŸ‘‰ They are integrated via lifecycle hooks - -Example: - -- When a user is created β†’ password credential may be created -- When a user is deleted β†’ credentials are removed - -πŸ‘‰ This keeps domains decoupled. - -
- -## πŸ”„ Credential Lifecycle - -Credentials support: - -- Creation -- Secret change -- Revocation -- Expiration -- Deletion - -
- -## πŸ” Security Behavior - -Credential changes trigger security actions: - -- Changing password revokes sessions -- Reset flows require verification tokens -- Invalid attempts are tracked - -πŸ‘‰ Credentials are tightly coupled with security. - -
- -## πŸ”‘ Reset Flow - -Password reset is a multi-step process: - -1. Begin reset (generate token or code) -2. Validate token -3. Apply new secret - -πŸ‘‰ Reset flow is protected against enumeration and abuse. - -
- -## 🧠 Mental Model - -Users define identity. - -Credentials define authentication. - -## 🎯 Summary - -- Credentials handle authentication secrets -- Password is default but extensible -- Integrated via lifecycle hooks -- Strong security guarantees -- Fully extensible for new credential types diff --git a/docs/content/plugin-domains/credentials.md b/docs/content/plugin-domains/credentials.md new file mode 100644 index 00000000..c9ad7f59 --- /dev/null +++ b/docs/content/plugin-domains/credentials.md @@ -0,0 +1,307 @@ +--- +title: Credentials +order: 3 +group: plugin-domains +--- + +# Credentials Domain + +Authentication establishes whether an entity can be trusted to act as a particular identity. A credential provides the evidence used to establish that trust. + +UltimateAuth treats credentials as a distinct domain rather than embedding authentication secrets directly into user records or authentication flows. + +The **Credentials Plugin Domain** manages the lifecycle, verification, and security of authentication credentials while allowing the authentication engine to remain independent of a particular credential type. + +The current reference implementation supports password credentials. Its architecture establishes the separation of responsibilities needed to accommodate additional credential mechanisms without redefining the underlying authentication model. + +## Understanding Credentials + +A credential represents authentication material or a mechanism through which an identity can demonstrate possession of an accepted authentication factor. + +Credentials may take different forms: + +| Credential mechanism | Authentication evidence | +|---|---| +| **Password** | Knowledge of a secret verified against a protected representation | +| **One-time code** | Possession of a time-limited or single-use authentication value | +| **Cryptographic credential** | Proof of control over a private key or authenticator | +| **External identity provider** | A trusted authentication assertion established through an external protocol | + +These mechanisms do not necessarily share the same storage, verification, or lifecycle requirements. + +For example, a password requires secure secret verification, while a cryptographic credential may require signature validation and public-key metadata. An external provider may require validating protocol assertions rather than storing a reusable secret. + +UltimateAuth's credential architecture is intended to accommodate these differences through explicit credential-specific behavior. + +**Current implementation:** Password credentials are supported. The other mechanisms illustrate the broader credential model and its intended extension boundaries; they are not presented as implemented credential types. + +## Credentials and User Identity + +A user identity and a credential are separate concepts. + +The Users domain manages identity ownership, identifiers, and account lifecycle. The Credentials domain manages the authentication material associated with an identity. + +This separation means: + +- A user can exist independently of a particular credential. +- Credential state can change without redefining the user's identity. +- An identity can conceptually be associated with multiple authentication mechanisms. +- Credential verification does not replace user lifecycle or account eligibility checks. + +A username or email address is generally an **identifier**, not a credential. + +An identifier helps resolve which identity is involved in an authentication attempt. A credential provides evidence that the actor is entitled to authenticate as that identity. + +For a password-based login, these responsibilities work together: + +1. The submitted identifier is resolved to an identity. +2. The applicable password credential is located. +3. The submitted secret is verified. +4. Authentication policies and relevant security conditions are evaluated. +5. The authentication engine establishes authentication state if the flow succeeds. + +The credential domain participates in establishing identity proof, but it does not independently determine the entire authentication outcome. + +## Responsibilities Across the Framework + +Credential support spans several architectural layers, each with a different responsibility. + +| Layer | Responsibility | +|---|---| +| **Core** | Foundational authentication contracts and security concepts | +| **Server** | Authentication flow execution, orchestration, and authentication-state management | +| **Credentials Bridge** | Contracts connecting credential capabilities to the authentication server | +| **Credentials Contracts** | Shared credential operation models and results | +| **Credentials Reference Implementation** | Credential management, validation, recovery, and lifecycle behavior | +| **Persistence** | Storage of credential records and related security state | + +The distinction is between **using credentials to authenticate** and **managing credentials as domain resources**. + +The authentication engine coordinates authentication flows. The Credentials domain owns the rules governing credential records, their usability, and their lifecycle. + +An application may integrate a custom credential system through the relevant authentication contracts without necessarily adopting the reference credential management implementation. + +## Password Credentials + +Password is the first supported credential mechanism in UltimateAuth. + +A password credential represents a password-based authentication factor associated with an identity. + +The reference implementation separates the submitted secret from its protected representation and the credential's operational state. + +### Secret Protection + +Passwords must not be stored as plaintext or returned through credential management responses. + +The password verification process operates against a protected password representation using the configured password hashing infrastructure. + +Credential management APIs should expose only the information required for their intended operations, not the underlying secret. + +This separation also allows password verification and password management to evolve without changing the user identity model. + +### Credential Security State + +A credential is not necessarily usable merely because a matching secret has been supplied. + +Credential validation must also account for relevant lifecycle and security conditions, such as: + +- Whether the credential is active or revoked. +- Whether an applicable expiration condition has been reached. +- Whether the credential belongs to the intended identity. +- Whether the requested operation is permitted. +- Whether the credential is eligible for the authentication mechanism being executed. + +Secret verification and credential usability are related but distinct decisions. + +A correctly supplied password must not make a revoked or otherwise unusable credential valid. + +## Credential Validation + +UltimateAuth separates credential-specific verification from the broader authentication flow. + +For password authentication, credential validation includes evaluating the supplied secret against the stored protected representation and enforcing the relevant credential conditions. + +The authentication server then uses the result within its authentication flow. + +This boundary matters because credential verification is not equivalent to session creation or token issuance. + +A successful credential check establishes evidence for authentication. The authentication engine remains responsible for determining whether the flow can complete and what authentication state should be created. + +The same architectural distinction applies to other credential mechanisms. + +A future cryptographic credential validator, for example, would verify a different kind of proof, but it would still participate in a broader authentication decision rather than independently issuing authentication state. + +## Credential Lifecycle + +Credentials are managed resources with their own lifecycle. + +The reference domain provides operations around credential creation, secret changes, revocation, and recovery. + +| Operation | Purpose | +|---|---| +| **Add** | Register a credential for an identity | +| **Change secret** | Replace authentication material through an authorized operation | +| **Revoke** | Make a credential unusable | +| **Begin reset** | Initiate a controlled credential recovery process | +| **Complete reset** | Replace a secret after satisfying recovery requirements | + +These operations have different security requirements. + +Changing a password while authenticated is not equivalent to recovering access without the existing password. Likewise, revoking a credential is not the same as invalidating every authentication session previously established using that credential. + +UltimateAuth treats these distinctions explicitly rather than assuming that every credential mutation has the same security effect. + +## Credential Ownership and Operation Scope + +Credential management operations must be evaluated in the context of the actor, the target identity, and the requested operation. + +UltimateAuth distinguishes between self-service, administrative, and anonymous recovery scenarios. + +### Self-Service Operations + +An authenticated user may manage credentials associated with their own identity, subject to the applicable permissions and validation rules. + +Changing a secret may require proving knowledge of the existing secret or satisfying another supported security requirement. + +### Administrative Operations + +Administrative credential management is performed against an explicitly identified target. + +Administrative authority does not eliminate the requirement to preserve target identity consistency. + +For example, a credential recovery operation must not modify a different user's credential merely because a supplied identifier resolves to another account. + +### Anonymous Recovery + +Anonymous recovery allows an identity to regain access without an existing authenticated session. + +Because the actor is not authenticated, the recovery process must establish sufficient proof before allowing credential modification. + +These operation scopes are security boundaries, not merely different API routes. + +## Credential Recovery + +Credential recovery is one of the most security-sensitive responsibilities of the Credentials domain. + +The password reset process is designed as a controlled, multi-stage operation rather than an unrestricted secret update. + +### Beginning Recovery + +A recovery request identifies the intended account and initiates a proof-based process. + +Depending on the supported recovery configuration, proof may take the form of a token or code delivered through an appropriate notification mechanism. + +The proof must be bound to the intended recovery context and subject to server-controlled validity rules. + +Recovery material must not be exposed to an anonymous caller simply because that caller initiated the request. + +### Completing Recovery + +Completing recovery requires validating the supplied proof and its associated security conditions before applying a new secret. + +The reference recovery model accounts for: + +- Proof validity and expiration. +- Single-use recovery material. +- Consistency between the recovery target and intended identity. +- Rejection of invalid or previously consumed proof. +- Protection against unauthorized credential replacement. +- Security-state updates following successful recovery. + +A completed recovery must not leave the same proof available for another successful reset. + +### Enumeration Resistance + +Anonymous recovery endpoints must avoid unnecessarily revealing whether an identifier belongs to an existing account. + +UltimateAuth separates externally observable recovery responses from the internal decisions required to locate and manage the account. + +This reduces account enumeration opportunities while preserving the ability to enforce recovery rules. + +Recovery behavior should also be combined with appropriate abuse protection and operational monitoring. + +## Credentials and Authentication Sessions + +Changing or recovering a credential may require action against authentication sessions that were established before the change. + +However, **credential lifecycle and session lifecycle are separate responsibilities**. + +UltimateAuth supports security policies that determine how credential changes and resets affect existing sessions. + +The available policy model includes: + +| Policy | Intended behavior | +|---|---| +| `KeepAll` | Preserve existing sessions | +| `KeepCurrent` | Preserve the current session while revoking other applicable sessions | +| `RevokeAll` | Revoke all applicable sessions | + +The appropriate policy depends on the operation and the application's security requirements. + +A password reset performed through anonymous recovery may warrant different treatment from an authenticated password change. + +Enforcement also depends on the authentication mode. + +In session-authoritative modes, revocation can be reflected through server-side session validation. In JWT-based modes, previously issued access tokens may remain usable until expiration unless additional validation mechanisms are involved. + +Credential changes therefore initiate the applicable security response; they do not imply identical immediate token invalidation across every mode. + +See [Authentication Modes](/docs/fundamentals/auth-modes) for the underlying differences. + +## Integration with the Users Domain + +The Credentials domain does not need to own user records to manage authentication material associated with an identity. + +User creation, deletion, or other lifecycle changes may require corresponding credential operations. + +UltimateAuth supports coordination through integration contracts rather than requiring the Users and Credentials implementations to depend directly on one another. + +For example, an application may coordinate initial credential enrollment with user creation or perform credential cleanup as part of account removal. + +These behaviors belong to explicit integration workflows. + +Creating a user does not inherently require creating a password credential, and deleting a user should not rely on an undocumented side effect to maintain credential consistency. + +This distinction keeps identity lifecycle and credential lifecycle independently understandable. + +## Extending Credential Capabilities + +Credential mechanisms differ substantially in how they establish trust. + +UltimateAuth's architecture is intended to support additional mechanisms by separating credential-specific verification and management from authentication flow orchestration. + +Such extensions may require their own: + +- Credential representation and metadata. +- Enrollment and verification procedures. +- Security and expiration rules. +- Recovery or revocation behavior. +- Persistence and integration contracts. + +Not every credential mechanism needs to implement password-style secret changes or recovery. + +The goal is to preserve consistent authentication boundaries while allowing each credential mechanism to enforce its own security requirements. + +For most applications using password authentication, the reference implementation provides the recommended starting point. + +Applications with specialized requirements can customize supported services and integrations or provide alternative implementations through the relevant contracts. + +Detailed extension and replacement strategies belong in the dedicated customization documentation. + +## Architectural Perspective + +The Credentials domain establishes an important distinction within UltimateAuth: + +**A user identifies the account. A credential provides authentication evidence. The authentication engine decides how that evidence establishes authentication state.** + +Keeping these responsibilities separate allows the framework to support identity management without tying authentication to one user entity, one secret format, or one credential mechanism. + +The current password implementation provides the operational foundation. The broader architecture allows credential capabilities to evolve without turning the authentication engine into a monolithic credential management system. + +## Next Steps + +- [Users](/docs/plugin-domains/users) β€” Understand identity ownership, identifiers, and account lifecycle. +- [Authorization](/docs/plugin-domains/authorization) β€” Explore how authenticated identities are evaluated against access policies. +- [Plugin Domains](/docs/plugin-domains) β€” Review the architecture and responsibilities of Plugin Domains. + +For the authentication flow that consumes credential verification results, see [Flow-Based Authentication](/docs/fundamentals/flow-based-authentication). \ No newline at end of file diff --git a/docs/content/plugin-domains/index.md b/docs/content/plugin-domains/index.md deleted file mode 100644 index 14950f03..00000000 --- a/docs/content/plugin-domains/index.md +++ /dev/null @@ -1,153 +0,0 @@ ---- -title: Plugin Domains -order: 1 -group: plugin-domains ---- - - -# 🧩 Plugin Domains - -Authentication alone is not enough. - -Real-world systems also require: - -- User management -- Credential handling -- Authorization rules - -πŸ‘‰ UltimateAuth provides these as **plugin domains** - -## 🧠 What Is a Plugin Domain? - -A plugin domain is a **modular business layer** built on top of UltimateAuth. - -πŸ‘‰ Core handles authentication -πŸ‘‰ Plugin domains handle identity and access logic - -
- -## πŸ— Architecture - -Each plugin domain is composed of multiple layers: - -### πŸ”Ή Bridge Package - -Defines the server needed bridge interfaces: - -πŸ‘‰ This package provides required minimal info for server package. - -### πŸ”Ή Contracts Package - -Shared models between: - -- Server -- Client - -πŸ‘‰ Includes DTOs, requests, responses - -### πŸ”Ή Reference Implementation - -Provides default behavior: - -- Application services -- Store interfaces -- Default implementations - -πŸ‘‰ Acts as a production-ready baseline - -### πŸ”Ή Persistence Layer - -Provides storage implementations: - -- InMemory -- Entity Framework Core - -πŸ‘‰ Additional providers (Redis, etc.) can be added - -
- -## πŸ”„ Extensibility Model - -Plugin domains are designed to be: - -- Replaceable -- Extendable -- Composable - -πŸ‘‰ You can implement your own persistence -πŸ‘‰ You can extend behavior - -
- -## ⚠️ Recommended Approach - -In most cases: - -πŸ‘‰ You should NOT replace a plugin domain entirely - -Instead: - -- Use provided implementations -- Extend via interfaces -- Customize behavior where needed - -πŸ‘‰ This ensures compatibility with the framework - -
- -## πŸ”Œ Domain Isolation - -Plugin domains are designed to be **fully isolated** from each other. - -πŸ‘‰ A plugin domain does NOT reference another plugin domain -πŸ‘‰ There are no direct dependencies between domains - -### 🧠 Why? - -This ensures: - -- Loose coupling -- Independent evolution -- Replaceability -- Clear boundaries - -## πŸ”„ Communication via Hooks - -Plugin domains communicate **only through integration hooks**. - -For example: - -- User domain triggers β†’ `OnUserCreated` -- Credential domain listens β†’ creates password credential - -πŸ‘‰ This is implemented via abstractions such as: - -- `IUserLifecycleIntegration` -- domain events / integration points - -
- -## 🧠 Mental Model - -If you remember one thing: - -πŸ‘‰ Core = authentication engine -πŸ‘‰ Plugin domains = business logic - -## 🎯 Why This Matters - -This architecture allows UltimateAuth to: - -- Stay modular -- Support multiple domains -- Enable enterprise customization -- Avoid monolithic identity systems - -πŸ‘‰ You don’t build everything from scratch -πŸ‘‰ You assemble what you need - -## ➑️ Next Step - -- Manage users β†’ Users -- Handle credentials β†’ Credentials -- Control access β†’ Authorization diff --git a/docs/content/plugin-domains/overview.md b/docs/content/plugin-domains/overview.md new file mode 100644 index 00000000..ae3f4687 --- /dev/null +++ b/docs/content/plugin-domains/overview.md @@ -0,0 +1,122 @@ +--- +title: Plugin-Domains Overview +order: 1 +group: plugin-domains +--- + +# Plugin Domains + +UltimateAuth separates **authentication infrastructure** from the domain capabilities commonly required by identity and access management systems. + +The authentication engine is responsible for establishing and validating authentication state. Managing users, credentials, and authorization policies introduces additional responsibilities that do not inherently belong to that engine. + +UltimateAuth organizes these capabilities into **Plugin Domains**: modular, independently structured domains that extend the framework beyond authentication without making their business logic part of the authentication core. + +Plugin Domains are an architectural concept specific to UltimateAuth. They are not simply runtime plugins or optional features within a monolithic identity system. + +## Authentication Core and Plugin Domains + +The distinction is based on responsibility rather than importance. + +**Authentication Core** provides the infrastructure for authentication flows, authentication modes, sessions, tokens, and runtime security boundaries. + +**Plugin Domains** provide the identity and access management capabilities built around that infrastructure. + +The framework currently organizes these responsibilities into three primary domains: + +| Domain | Responsibility | +|---|---| +| **Users** | User lifecycle, identifiers, profiles, and account management | +| **Credentials** | Credential registration, management, verification, and recovery | +| **Authorization** | Permissions, roles, policies, and access management | + +These domains are not universally required for every authentication scenario. An application may already have its own identity infrastructure or may need only a subset of UltimateAuth's domain capabilities. + +However, together they provide the foundation for using UltimateAuth as a broader identity and access management platform rather than only an authentication engine. + +## Architectural Boundaries + +Each Plugin Domain owns a distinct area of business logic. + +A domain should not need to understand the internal implementation of another domain to perform its responsibilities. + +This separation establishes several architectural properties: + +- **Independent domain boundaries:** Each domain defines and maintains its own contracts and behavior. +- **Explicit integration:** Cross-domain interactions occur through defined abstractions and integration points. +- **Replaceable infrastructure:** Storage implementations and supporting infrastructure can be substituted without changing the authentication engine. +- **Controlled extensibility:** Applications can customize domain behavior while preserving established framework contracts. + +Isolation does not mean that domains never interact. Identity management operations frequently involve multiple domains, and those interactions must remain coordinated. + +UltimateAuth addresses this through explicit integration contracts rather than direct dependencies between domain implementations. + +## Domain Package Structure + +Plugin Domains follow a layered package structure that separates integration contracts, public models, domain behavior, and persistence. + +| Layer | Purpose | +|---|---| +| **Bridge** | Minimal abstractions required for integration with UltimateAuth Server | +| **Contracts** | Shared requests, responses, and data transfer models | +| **Reference Implementation** | Default application services, domain behavior, and storage abstractions | +| **Persistence** | Concrete storage implementations, including InMemory and Entity Framework Core | + +This structure keeps the authentication server independent of the full internal implementation of each domain. + +It also allows applications to use the provided implementations while retaining control over infrastructure and customization. + +The reference implementations are intended to be practical foundations for real applications, not merely illustrative examples. + +## Cross-Domain Integration + +Some identity operations naturally cross domain boundaries. + +For example, creating a user may need to initiate credential-related behavior. That coordination should not require the Users domain to reference the Credentials domain directly. + +Instead, UltimateAuth uses integration abstractions and lifecycle hooks to connect independently implemented responsibilities. + +`IUserLifecycleIntegration` is an example of such an integration point. + +This approach allows domain-specific behavior to remain within its owning domain while supporting coordinated application workflows. + +Integration hooks are explicit extension boundaries, not a mechanism for bypassing domain ownership or security rules. + +## Extending Plugin Domains + +UltimateAuth provides reference implementations so applications do not need to rebuild common identity management capabilities from scratch. + +For most applications, the recommended approach is to **start with the provided domain implementations and customize only what is necessary**. + +Depending on the domain and its supported extension contracts, customization may include: + +- Replacing persistence implementations. +- Providing application-specific services. +- Extending supported lifecycle integrations. +- Integrating existing identity or access management infrastructure. + +Replacing an entire domain is possible as an architectural objective, but doing so requires preserving the contracts expected by the rest of the system. + +Modularity provides flexibility; it does not eliminate the need for compatible behavior. + +## Choosing the Right Capabilities + +Plugin Domains are designed to be composed according to application requirements. + +An application focused on authentication infrastructure may rely primarily on the Core and Server capabilities. + +An application requiring complete identity management can incorporate Users, Credentials, and Authorization together. + +The important distinction is that **the authentication engine does not need to become a monolithic identity management system to support either scenario**. + +This separation allows UltimateAuth to serve both focused authentication use cases and more comprehensive identity platforms while maintaining clear architectural boundaries. + +## Explore the Domains + +Continue with the individual domains to understand their responsibilities, public contracts, and extension points. + +- [Users](/docs/plugin-domains/users) β€” User identities, identifiers, and lifecycle management. +- [Credentials](/docs/plugin-domains/credentials) β€” Credential management, verification, and recovery. +- [Authorization](/docs/plugin-domains/authorization) β€” Roles, permissions, policies, and access control. + +For the underlying authentication architecture, see [Runtime Architecture](/docs/fundamentals/runtime-architecture) and [Authentication Model](/docs/fundamentals/auth-model). \ No newline at end of file diff --git a/docs/content/plugin-domains/policies.md b/docs/content/plugin-domains/policies.md index b688e5b9..ef74f0fe 100644 --- a/docs/content/plugin-domains/policies.md +++ b/docs/content/plugin-domains/policies.md @@ -1,134 +1,397 @@ --- -title: Policies +title: Policies & Access Control order: 5 group: plugin-domains --- +# Policies & Access Control -# πŸ›‘ Policies & Access Control +Authorization is not simply a question of whether an identity possesses a permission. -UltimateAuth uses a **policy-driven authorization model**. +A protected operation may involve an authenticated actor, a target identity, tenant boundaries, resource ownership, and security conditions that cannot be represented by a permission alone. -Policies are not simple checks β€” -they are **composable decision units** evaluated at runtime. +UltimateAuth addresses these requirements through a **policy-driven access control pipeline**. -## 🧠 Mental Model +Policies are independent decision units evaluated within a shared access context. The authorization orchestrator coordinates those decisions and determines whether the requested operation can proceed. -Authorization in UltimateAuth is: +This page explains how that process works. -πŸ‘‰ Context-based -πŸ‘‰ Policy-driven -πŸ‘‰ Orchestrated +For actions, permissions, roles, and scope conventions, see [Authorization](/docs/plugin-domains/authorization). -### Flow +## The Access Decision Model -1. Build `AccessContext` -2. Resolve policies -3. Execute authority -4. Allow / Deny / Reauth +UltimateAuth evaluates authorization using three primary elements: -
+| Element | Responsibility | +|---|---| +| **AccessContext** | Describes the actor, target, operation, and relevant security context | +| **Policies** | Evaluate individual authorization requirements | +| **AccessOrchestrator** | Coordinates policy resolution, evaluation, and execution | -## βš™οΈ AccessContext +These elements separate the description of an operation from the rules used to authorize it. -Every authorization decision is based on: +A permission may grant authority to perform an action, but policies determine whether that authority is valid in the current context. -- Actor (who is calling) -- Target (what is being accessed) -- Action (what is being done) -- Tenant -- Claims / permissions +For example, a caller may possess an administrative permission while attempting to access a resource belonging to another tenant. -
+The permission may match, but the operation must still satisfy tenant isolation requirements. -## πŸ”Œ Policy Resolution +## AccessContext -Policies are resolved using: +`AccessContext` represents the security context of an authorization operation. -- Action prefix matching -- Runtime filtering (`AppliesTo`) +It provides the information needed to evaluate access without requiring each policy to reconstruct the request independently. -Example: +Conceptually, the context includes: -- `users.create.admin` -- `users.*` -- `authorization.roles.*` +| Component | Purpose | +|---|---| +| **Actor** | The identity or system principal performing the operation | +| **Target** | The identity or resource affected by the operation | +| **Action** | The protected operation being requested | +| **Tenant context** | The tenant boundaries relevant to the actor and target | +| **Authorization information** | Relevant claims, permissions, and security state | -
+The context is more than a collection of request parameters. -## 🧩 Policy Types +It establishes the relationship between the authenticated actor and the operation being performed. + +### Actor and Target Are Different Concepts + +Consider an administrative operation: + +```text +users.delete.admin +``` + +The actor is the administrator requesting the deletion. + +The target is the user account being deleted. + +These identities must remain distinguishable throughout authorization and execution. + +For a self-service operation: + +```text +users.delete.self +``` + +The actor and target are expected to represent the same identity. + +That relationship must be established from trusted context, not assumed from the action suffix. + +### Trusted Context Construction + +Authorization decisions are only as reliable as the information supplied to them. + +An endpoint may receive a target user ID or tenant ID from the caller, but these values must not automatically become trusted authorization facts. + +The authentication infrastructure establishes the actor's identity. The application or domain operation resolves the intended target. The authorization context brings those elements together. + +Policies then evaluate the resulting relationships. + +This distinction is essential for preventing cross-user and cross-tenant access. + +## Policies as Decision Units + +A policy evaluates a specific security requirement. + +Rather than combining every authorization rule into one large conditional expression, UltimateAuth separates requirements into reusable policies. + +Examples include: + +- Requiring an authenticated actor. +- Requiring an active user. +- Enforcing tenant isolation. +- Verifying self-access relationships. +- Requiring an appropriate permission. +- Restricting an operation to a system context. + +Each policy evaluates its own responsibility within the available access context. + +A policy should not need to understand every other policy to make its decision. + +This allows authorization rules to be composed without losing their individual meaning. + +## Policy Resolution + +Not every policy is relevant to every operation. + +UltimateAuth supports policy resolution based on action matching and runtime applicability. + +### Action-Based Matching + +Policies can be associated with action patterns. + +For example: + +```text +users.create.admin +users.* +authorization.roles.* +``` + +An action-specific policy can target one operation, while a broader pattern can apply to a related group of operations. + +This allows common rules to be shared across a domain without duplicating policy registration for every action. + +### Runtime Applicability + +Action matching alone may not provide enough information to determine whether a policy should execute. + +A policy can also evaluate whether it applies to the current context through `AppliesTo`. + +This enables context-dependent policy selection. + +For example, a policy may apply only to administrative operations that modify account state, rather than every administrative read operation. + +**Policy resolution determines which rules participate in the decision. Policy evaluation determines whether those rules permit the operation.** + +Keeping these stages separate makes authorization behavior easier to reason about. + +## Policy Categories + +UltimateAuth distinguishes between security requirements that must be enforced broadly and policies selected for particular operations. + +### Invariants + +Invariants represent fundamental security rules that must not be bypassed by ordinary permission grants or application-level authorization decisions. + +They are evaluated before the remaining policy stages. + +An invariant is not equivalent to a role permission or an optional business rule. + +Its purpose is to preserve a security condition that must hold regardless of the requested operation's ordinary authorization configuration. ### Global Policies -Always evaluated: +Global policies apply broadly across protected operations. + +They provide common security requirements that should not need to be registered independently for every action. + +Examples of such requirements include authentication and tenant isolation. -- RequireAuthenticated -- DenyCrossTenant +Their applicability may still depend on the operation's context. An intentionally anonymous action, for example, cannot be treated identically to an authenticated administrative operation. ### Runtime Policies -Resolved dynamically: +Runtime policies are selected and evaluated according to the requested action and current access context. -- RequireActiveUser -- MustHavePermission -- RequireSelf +They support requirements such as: -### Invariants +- Permission checks. +- Self-access validation. +- Active-user requirements. +- Operation-specific restrictions. -Executed first: +This separation allows the authorization system to maintain mandatory security boundaries while remaining extensible for individual domains and applications. -- Cannot be bypassed -- Hard security rules +## Policy Evaluation Order -
+UltimateAuth's authorization pipeline follows a defined evaluation order: -## βš–οΈ Policy Evaluation +1. **Invariants** +2. **Global policies** +3. **Runtime policies** -Evaluation order: +This order establishes a clear security hierarchy. -1. Invariants -2. Global policies -3. Runtime policies +A runtime permission grant must not override an invariant that has already rejected the operation. -πŸ‘‰ First deny wins -πŸ‘‰ Allow means β€œno objection” -πŸ‘‰ Reauth can be requested +Likewise, satisfying an action-specific policy does not eliminate the need to satisfy applicable global requirements. -
+### Deny Takes Precedence -## πŸ” Example Policy +The decision model is deny-oriented. -### Deny Admin Self Modification +When a required policy denies an operation, a later policy cannot convert that denial into an authorization grant. -- Blocks admin modifying own account -- Applies only to `.admin` actions -- Ignores read operations +This prevents authorization from depending on the accidental presence of a permissive policy. -### Require Active User +### Allow Is Not a Universal Grant -- Ensures user exists -- Ensures user is active -- Skips anonymous actions +A policy returning an allow result means that its own requirement has been satisfied or that it has no objection to the operation. -
+It does not mean that all other requirements have been satisfied. -## πŸš€ Access Orchestrator +The overall decision is produced by the authorization pipeline, not by one successful policy in isolation. -The orchestrator is the entry point: +### Reauthentication -- Enriches context (claims, permissions) -- Resolves policies -- Executes authority -- Runs command if allowed +Some authorization decisions may require additional authentication rather than producing an ordinary allow or deny outcome. -## 🎯 Key Principles +UltimateAuth's decision model includes the ability to represent a reauthentication requirement. -- Policies are composable -- Authorization is deterministic -- No hidden magic -- Fully extensible +This allows authorization to distinguish between insufficient authority and situations where the actor may need to establish a stronger or more recent authentication state. ---- +The exact handling of such a result depends on the surrounding authentication and application flow. + +## AccessOrchestrator + +`AccessOrchestrator` coordinates the authorization process. + +Its responsibilities include preparing the effective access context, resolving applicable policies, evaluating the required security conditions, and coordinating execution when access is permitted. + +The conceptual pipeline is: + +```text +Requested operation + | + v +Build AccessContext + | + v +Resolve authorization information + | + v +Evaluate invariants + | + v +Evaluate global policies + | + v +Resolve applicable runtime policies + | + v +Evaluate runtime policies + | + +---- Deny ------> Reject operation + | + +---- Reauth ----> Require reauthentication + | + v + Allow + | + v +Execute authorized operation +``` + +This is a conceptual representation of the access decision pipeline, not a substitute for the concrete orchestration API. + +The orchestrator centralizes authorization coordination so that individual application services do not need to reproduce the complete evaluation process. + +## Authorization Before Execution + +Authorization should be enforced before a protected operation performs security-sensitive changes. + +Consider an administrative credential operation. + +The system may need to establish: + +1. Who is performing the operation. +2. Which account is being targeted. +3. Whether the actor possesses the required administrative permission. +4. Whether the target belongs to an allowed tenant. +5. Whether additional operation-specific restrictions apply. + +Only after the applicable requirements succeed should the protected mutation proceed. + +This separation is particularly important for operations that affect credentials, account status, roles, or sessions. + +A rejected authorization decision must not leave behind a partially completed protected operation. + +## Example: Preventing Administrative Self-Modification + +Some administrative operations may intentionally prohibit an administrator from modifying their own account through an administrative route. + +This is different from ordinary self-service account management. + +A policy enforcing this restriction could consider: + +- Whether the requested action has administrative scope. +- Whether the operation modifies security-sensitive account state. +- Whether the actor and target represent the same identity. + +If the policy applies and the actor is also the target, it denies the operation. + +The purpose is to prevent an administrative capability from bypassing the separate rules governing self-service operations. + +Such a policy illustrates why permission checks alone are insufficient. + +An administrator may possess the required action permission while still being prohibited from performing the operation against a particular target. + +## Example: Requiring an Active User + +An active-user policy evaluates whether the relevant identity satisfies the account state requirements for an operation. + +An authenticated principal may exist even after the corresponding user account has been suspended or otherwise made ineligible. + +The authorization system therefore needs a way to enforce account eligibility independently of the original authentication event. + +The policy can evaluate the relevant user state and deny operations that require an active account. + +Anonymous operations may follow different applicability rules because they do not necessarily have an authenticated user identity. + +The exact enforcement behavior also depends on how current user security state is obtained. + +## Policy Decisions and Authentication Modes + +The policy model is conceptually independent of the authentication mode, but the freshness of its input data matters. + +In session-authoritative modes, server-side state may be consulted as part of protected operations. + +In JWT-based modes, some authorization information may originate from claims issued earlier. + +This creates a distinction between: + +- Evaluating a policy against current authoritative data. +- Evaluating a policy against previously issued authorization claims. + +For example, a permission removed after token issuance may still be present in a locally validated JWT until that token expires. + +A policy can evaluate its inputs correctly while those inputs reflect an earlier authorization state. + +Applications must account for this when choosing token lifetimes, permission resolution strategies, and security requirements. + +See [Authentication Modes](/docs/fundamentals/auth-modes). + +## Application-Level Access Control + +UltimateAuth's built-in authorization rules protect framework operations, but applications also have their own resources and business requirements. + +A business application may need policies for: + +- Editing resources owned by a particular user. +- Restricting operations to a tenant. +- Requiring elevated authority for administrative changes. +- Preventing sensitive changes under particular account conditions. + +These requirements can be modeled as application-specific authorization policies. + +The recommended architectural approach is to keep security decisions at a boundary that cannot be bypassed by alternative execution paths. + +For HTTP endpoints, ASP.NET Core authorization can provide the request-level enforcement boundary. + +For direct application service calls, including in-process operations, authorization must also be enforced where the protected operation actually executes. + +The presence of an `[Authorize]` attribute on a controller does not automatically protect a service invoked directly from another part of the application. + +## Extending the Policy Pipeline + +UltimateAuth's policy model is designed to accommodate application-specific requirements without modifying the authentication engine. + +Custom policies can introduce additional authorization conditions while participating in the established access decision process. + +Such policies should preserve the framework's security boundaries: + +- Do not treat untrusted request values as established identity facts. +- Do not bypass mandatory invariants. +- Do not use broad permission grants to override target or tenant restrictions. +- Keep policy applicability and evaluation behavior explicit. + +Detailed custom policy implementation and registration examples belong in [How-to Guides](/docs/how-to), where they can be presented alongside complete application code. + +## Architectural Perspective + +UltimateAuth authorization is a coordinated evaluation of security requirements. + +`AccessContext` describes the operation. Policy resolution identifies the relevant requirements. Policy evaluation determines whether those requirements are satisfied. `AccessOrchestrator` coordinates the process and its execution boundary. + +The result is an authorization model that is **context-aware, policy-driven, and extensible without making individual permissions responsible for every security decision**. + +## Next Steps -πŸ‘‰ Authorization is not a single check -πŸ‘‰ It is a **pipeline of decisions** +- [Authorization](/docs/plugin-domains/authorization) β€” Actions, permissions, roles, and scope semantics. +- [Users](/docs/plugin-domains/users) β€” Account lifecycle and identity state. +- [Credentials](/docs/plugin-domains/credentials) β€” Credential ownership and security operations. +- [Request Lifecycle](/docs/fundamentals/request-lifecycle) β€” Authorization within protected requests. \ No newline at end of file diff --git a/docs/content/plugin-domains/users-domain.md b/docs/content/plugin-domains/users-domain.md deleted file mode 100644 index c7b27935..00000000 --- a/docs/content/plugin-domains/users-domain.md +++ /dev/null @@ -1,121 +0,0 @@ ---- -title: Users -order: 2 -group: plugin-domains ---- - - -# πŸ‘€ Users Domain - -Users in UltimateAuth are not a single entity. - -Instead, they are composed of multiple parts that together define identity. - -## 🧠 Core Concept - -A user is represented by three main components: - -- Lifecycle (security & state) -- Identifiers (login surface) -- Profile (user data) - -
- -## πŸ” Lifecycle (Security Anchor) - -Lifecycle defines: - -- When a user created -- Whether a user is active, suspended, or deleted -- Security version (used for invalidating sessions/tokens) - -πŸ‘‰ This is the core of authentication security. - -
- -## πŸ”‘ Identifiers (Login System) - -Identifiers represent how a user logs in: - -- Email -- Username -- Phone - -Each identifier has: - -- Normalized value -- Primary flag -- Verification state - -### ⭐ Login Identifiers - -Not all identifiers are used for login. - -πŸ‘‰ Only **primary identifiers** are considered login identifiers. - -#### βš™οΈ Configurable Behavior - -Developers can control: - -- Which identifier types are allowed for login -- Whether email/phone must be verified -- Whether multiple identifiers are allowed -- Global uniqueness rules - -#### πŸ”Œ Custom Login Logic - -UltimateAuth allows custom login identifier resolution. - -You can: - -- Add custom identifier types -- Override resolution logic -- Implement your own resolver - -πŸ‘‰ This means login is fully extensible. - -
- -## 🧾 Profile (User Data) - -Profile contains non-auth data: - -- Name -- Bio -- Localization -- Metadata - -πŸ‘‰ This is not used for authentication. - -
- -## βš™οΈ Application Service - -User operations are handled by an orchestration layer. - -It is responsible for: - -- Creating users -- Managing identifiers -- Applying validation rules -- Triggering integrations -- Revoking sessions when needed - -
- -## πŸ” Mental Model - -Authentication answers: - -β†’ Who are you? - -Users domain answers: - -β†’ What is this user? - -## 🎯 Summary - -- Users are composed, not singular -- Login is based on identifiers -- Login identifiers are configurable -- System is extensible by design diff --git a/docs/content/plugin-domains/users.md b/docs/content/plugin-domains/users.md new file mode 100644 index 00000000..43beb4a8 --- /dev/null +++ b/docs/content/plugin-domains/users.md @@ -0,0 +1,257 @@ +--- +title: Users +order: 2 +group: plugin-domains +--- + +# Users Domain + +A user is more than a database record, an email address, or a profile. + +In UltimateAuth, user identity is modeled through a separation of **authentication identity, account lifecycle, identifiers, and application-specific user information**. + +This distinction allows the authentication engine to operate independently of a particular user entity or persistence model, while the Users Plugin Domain provides the capabilities needed to manage identities throughout their lifecycle. + +The Users domain builds on the authentication infrastructure provided by UltimateAuth Core and Server. It does not redefine how authentication works; it defines how user identities are represented, maintained, and integrated with that infrastructure. + +## Understanding User Identity + +UltimateAuth distinguishes between three aspects of a user: + +| Component | Responsibility | +|---|---| +| **Lifecycle** | Account existence, status, and security-relevant state | +| **Identifiers** | Values through which a user can be identified or discovered | +| **Profile** | Application-specific information associated with the user | + +These components represent different responsibilities and should not be treated as interchangeable. + +For example, changing a display name should not affect authentication state. Verifying an email address should not implicitly change account status. Suspending an account should not require deleting its identifiers or profile. + +Separating these concerns makes identity operations more predictable and allows security-sensitive changes to be handled explicitly. + +## User Identity in Core and Server + +UltimateAuth does not require applications to adopt the Users Plugin Domain simply to authenticate users. + +Core and Server provide the foundational authentication infrastructure, including the contracts and runtime mechanisms needed to associate authentication operations with user identities. + +At this level, a user is primarily an **authentication subject**. + +The authentication system needs to establish which identity is involved in an operation, evaluate the relevant security conditions, and maintain the appropriate authentication state. + +It does not inherently need to know the user's display name, profile fields, or how the application's user records are stored. + +This separation is intentional: + +- **Core** defines foundational authentication concepts, contracts, and security semantics. +- **Server** executes authentication flows and enforces the relevant runtime security boundaries. +- **Users** provides higher-level identity management capabilities through a dedicated Plugin Domain. + +An application with an existing identity system can integrate that system with UltimateAuth's authentication infrastructure without necessarily adopting the reference user model. + +The authentication identity is therefore a framework-level concept, while the complete user management model belongs to the application or the Users domain. + +## What the Users Domain Adds + +The Users Plugin Domain provides a structured approach to managing user identities beyond the minimum requirements of authentication. + +Its responsibilities include: + +- Creating and managing user accounts. +- Maintaining account lifecycle and status. +- Registering and managing user identifiers. +- Enforcing identifier ownership and verification rules. +- Applying user management validation and access boundaries. +- Coordinating security-relevant changes with authentication infrastructure. +- Exposing user management operations through application services and shared contracts. + +These capabilities are distinct from credential management. + +The Users domain determines **which identity an identifier belongs to** and maintains the relevant user state. The Credentials domain manages the authentication credentials associated with identities. + +For example, an email address is a user identifier; a password is a credential. They may participate in the same login flow, but they have different ownership and lifecycle rules. + +## Account Lifecycle + +The lifecycle represents the existence and security-relevant state of a user account. + +In the reference model, lifecycle information includes account creation, account status, and security state used to coordinate authentication-related changes. + +Account status may represent conditions such as active, suspended, or deleted, according to the supported lifecycle model. + +These states are not merely descriptive profile properties. They can influence whether an account is eligible to participate in authentication and other protected operations. + +### Security State and Session Invalidation + +User lifecycle changes may require existing authentication state to be invalidated. + +UltimateAuth separates the decision to change a user's security state from the mechanisms used to enforce that change. + +Depending on the configured authentication mode, enforcement may involve server-side session invalidation, token lifecycle rules, or other supported security mechanisms. + +This distinction matters because not every authentication mode can invalidate an already-issued credential immediately. + +For example, a locally validated JWT may remain valid until expiration unless the protected resource performs additional server-side validation. + +The Users domain participates in security-sensitive account operations, while the authentication infrastructure determines how the resulting security state is enforced. + +For more information, see [Authentication Modes](/docs/fundamentals/auth-modes). + +## User Identifiers + +An identifier is a value associated with a user that allows the identity to be referenced or resolved. + +Common examples include: + +- Email addresses. +- Usernames. +- Phone numbers. +- Application-defined identifier types. + +UltimateAuth treats identifiers as independent identity records rather than assuming that every user has exactly one email address or username. + +This supports applications with different identity requirements without changing the underlying authentication model. + +### Identifier Properties + +The reference identifier model distinguishes several important properties: + +| Property | Purpose | +|---|---| +| **Type** | Identifies the kind of identifier, such as email or username | +| **Value** | The identifier value associated with the user | +| **Normalized value** | A representation used for consistent lookup and comparison | +| **Primary state** | Indicates the identifier's designated primary role | +| **Verification state** | Indicates whether ownership has been verified | + +These properties address separate questions. + +An identifier may exist without being verified. A verified identifier does not automatically become primary. A primary identifier does not necessarily satisfy every authentication policy. + +### Login Identifier Resolution + +Identifier resolution determines how an incoming login identifier is associated with a user identity. + +In the reference approach, primary identifiers define the intended login surface, subject to the configured identifier resolution and verification policies. + +Applications can control which identifier types are accepted and how identifiers are resolved. + +This allows authentication to support different identity strategies without embedding email-specific or username-specific assumptions into Core. + +The distinction is important: **an identifier is identity data; whether it is acceptable for login is an authentication policy decision.** + +### Identifier Ownership and Verification + +Identifier verification is a security operation, not a simple update to a Boolean property. + +Marking an identifier as verified must be based on an appropriate ownership proof or an explicitly authorized administrative operation. + +The reference domain therefore distinguishes between: + +- Registering an identifier. +- Establishing ownership of an identifier. +- Verifying that ownership. +- Changing an identifier's role or status. + +An anonymous or self-service request must not be able to claim verified ownership merely by supplying a verification flag. + +Likewise, operations targeting another user's identifiers must respect the applicable authorization and ownership boundaries. + +This prevents identifier management from becoming an alternative route around authentication security. + +## User Profiles + +A profile contains information about the user that is not itself an authentication credential or an identity verification proof. + +Typical profile information may include: + +- Display name and personal information. +- Localization preferences. +- Application metadata. +- Other domain-specific user attributes. + +UltimateAuth deliberately separates profile data from the security-critical identity model. + +Applications should be able to evolve their profile structure without redesigning authentication flows or changing how identifiers are verified. + +A profile attribute may still be relevant to application authorization or business rules, but it should not automatically be treated as proof of identity. + +The reference model provides a starting point; applications remain responsible for deciding which additional user attributes belong in their own domain. + +## Reference Implementation + +The Users Plugin Domain includes a reference implementation that demonstrates and provides a practical user management model. + +It brings together lifecycle, identifiers, and profile management through application services and supporting abstractions. + +Rather than exposing persistence operations as the primary application interface, the reference implementation places user management rules within the application service layer. + +This layer coordinates operations such as: + +- User creation and account management. +- Identifier registration and updates. +- Identifier verification and ownership validation. +- Account status changes. +- Integration and lifecycle notifications. +- Security-related coordination with the authentication infrastructure. + +This is an important architectural boundary. + +A request to verify an identifier, for example, is not equivalent to directly updating a database field. The application service is responsible for enforcing the rules that make the operation valid. + +The reference implementation provides a recommended starting point for these behaviors without requiring every application to use the same persistence technology or user profile structure. + +## Domain Integration + +User management frequently affects other parts of an identity platform. + +Creating a user may be followed by credential enrollment. Changing account security state may require session invalidation. Deleting an account may require coordinated cleanup. + +UltimateAuth supports these scenarios through explicit integration contracts rather than direct dependencies between Plugin Domain implementations. + +For example, `IUserLifecycleIntegration` provides an integration boundary for user lifecycle operations. + +This allows other capabilities to participate in user-related workflows while keeping the Users domain responsible for its own state and rules. + +The distinction between domain ownership and cross-domain coordination is central to UltimateAuth's modular architecture. + +## Customizing User Management + +Applications are not required to accept every detail of the reference implementation. + +Depending on their requirements and the available extension contracts, they may: + +- Provide custom identifier resolution. +- Configure supported identifier and verification policies. +- Replace storage implementations. +- Integrate existing user repositories. +- Extend supported lifecycle and application service integrations. + +For most applications, the recommended approach is to begin with the reference implementation and customize specific behavior rather than replacing the entire domain. + +More advanced scenarios, including custom domain implementations and integration with existing identity systems, will be covered separately in the customization documentation. + +## Choosing Your User Model + +There are two broad integration approaches. + +**Use the reference Users domain** when the application needs a ready-made foundation for account lifecycle, identifiers, verification, and user management. + +**Integrate an existing identity model** when the application already owns these responsibilities and needs UltimateAuth primarily for its authentication infrastructure. + +Neither approach changes the fundamental security requirements. + +Identity resolution must be trustworthy, identifier ownership must be protected, and authentication state must be managed according to the configured security model. + +The difference is which part of the application owns the user management implementation. + +## Next Steps + +The Users domain establishes identity ownership and account lifecycle, but it does not independently provide every capability of an identity platform. + +- [Credentials](/docs/plugin-domains/credentials) β€” Learn how authentication credentials are managed independently of user identifiers. +- [Authorization](/docs/plugin-domains/authorization) β€” Explore how identities are associated with permissions and access policies. +- [Plugin Domains](/docs/plugin-domains) β€” Return to the overall Plugin Domain architecture. + +For the distinction between identity and authentication infrastructure, see [Authentication Model](/docs/fundamentals/auth-model). \ No newline at end of file diff --git a/docs/content/readme.md b/docs/content/readme.md deleted file mode 100644 index 20daca39..00000000 --- a/docs/content/readme.md +++ /dev/null @@ -1,67 +0,0 @@ -# πŸ” UltimateAuth Docs - -The modern, unified auth framework with platform-level abilities for .NET - Reimagined. - -## Sections - -UltimateAuth docs consists of 7 sections. There are suggested read line for starters: - -### 1) Getting Started - -Start here to integrate UltimateAuth into your application: - -- Getting Started Guide -- Setup with different client profiles -- Basic usage - -### 2) Fundamentals - -Understand how UltimateAuth works internally: - -- Core Concepts -- Session Architecture (Root β†’ Chain β†’ Session) -- Token Model -- Authorization Model - -### 3) Auth Flows - -Explore authentication lifecycle: - -- Login Flow -- Refresh Flow -- Logout Flow -- Session Lifecycle -- Token Behavior -- Device Management - -### 4) Plugin Domains - -UltimateAuth is built as modular domains: - -- Users -- Credentials -- Authorization - -### 5) Configuration & Extensibility - -Customize behavior and integrate with your system: - -- Server Options -- Client Options -- Configuration Sources -- Advanced Configuration -- Extending Domains & Stores - -### 6) Client & API - -- Real world usage of UltimateAuth -- API and code examples of authentication, sessions, users, credentials and authorization - -### 7) Security & Advanced - -Deep dive into the security model: - -- Session Security Model -- Refresh Token Rotation -- Access Token Behavior -- Policy Pipeline diff --git a/docs/content/security/access-token-behavior.md b/docs/content/security/access-token-behavior.md deleted file mode 100644 index 4920b808..00000000 --- a/docs/content/security/access-token-behavior.md +++ /dev/null @@ -1,172 +0,0 @@ ---- -title: Access Token Behavior -order: 2 -group: security ---- - - -# 🎯 Access Token Behavior - -Access tokens in UltimateAuth are intentionally **short-lived and mode-aware**. -They are not the primary source of truth for authentication. - -## 🧠 Core Principle - -In UltimateAuth: - -πŸ‘‰ Access tokens are **transport tokens** -πŸ‘‰ Sessions are the **source of truth** - -## πŸ”‘ Token Types - -UltimateAuth supports two access token types: - -### πŸ”Ή Opaque Tokens - -- Random, non-readable values -- Stored and validated server-side -- Typically used with session-based auth - -### πŸ”Ή JWT Tokens - -- Self-contained tokens -- Contain claims (user, tenant, session, etc.) -- Signed and verifiable without storage - -## βš™οΈ Mode-Dependent Behavior - -Access token behavior depends on auth mode: - -### 🟒 PureOpaque - -- No JWT issued -- Session cookie is the primary mechanism -- Access token may not exist externally - -πŸ‘‰ Validation = session validation - -### 🟑 Hybrid - -- Opaque + JWT together -- Session still authoritative -- JWT used for API access - -πŸ‘‰ Validation = session + token - -### 🟠 SemiHybrid - -- JWT is primary access token -- Session still exists -- Refresh rotation enabled - -πŸ‘‰ Balanced approach - -### πŸ”΅ PureJwt - -- Only JWT + refresh tokens -- No session state required - -πŸ‘‰ Stateless mode - -## ⏱ Lifetime Strategy - -Access tokens are: - -- short-lived -- replaceable -- not trusted long-term - -Typical lifetime: - -```text -5–15 minutes -``` - -## πŸ”„ Refresh Interaction - -Access tokens are never extended directly. - -Instead: - -```text -Access Token β†’ expires β†’ Refresh β†’ new Access Token -``` - -πŸ‘‰ This ensures: - -- forward-only security -- no silent extension -- replay window minimization - -## 🧾 Claims Model - -JWT tokens may include: - -- sub (user id) -- tenant -- sid (session id) -- jti (token id) -- custom claims - -πŸ‘‰ Claims are generated at issuance time -πŸ‘‰ Not dynamically updated afterward - -## ⚠️ Important Implication - -If something changes: - -- user roles -- permissions -- security state - -πŸ‘‰ existing JWTs are NOT updated - -πŸ‘‰ This is why: - -- tokens are short-lived -- refresh is required -- session validation may still apply - -## πŸ›‘ Security Boundaries - -Access tokens are: - -❌ not revocable individually (JWT) -❌ not long-term identity - -βœ” tied to session or refresh flow -βœ” bounded by expiration - -## πŸ”₯ Why This Matters - -UltimateAuth avoids a common mistake: - -πŸ‘‰ treating JWT as the system of record - -Instead: - -πŸ‘‰ JWT is a **snapshot**, not truth - -## ⚠️ Design Tradeoff - -Short-lived tokens mean: - -- more refresh calls -- slightly more backend interaction - -πŸ‘‰ But this enables: - -- safer rotation -- better revocation -- reduced attack window - -## 🧠 Mental Model - -If you remember one thing: - -πŸ‘‰ Access tokens are **temporary access grants** -πŸ‘‰ Not persistent identity - -## ➑️ Next Step - -Continue to **Policy Pipeline Deep Dive** to understand how access decisions are enforced. diff --git a/docs/content/security/authorization-pipeline.md b/docs/content/security/authorization-pipeline.md new file mode 100644 index 00000000..9964c32b --- /dev/null +++ b/docs/content/security/authorization-pipeline.md @@ -0,0 +1,589 @@ +--- +title: Authorization Pipeline Internals +order: 4 +group: security +--- + +# Authorization Pipeline Internals + +UltimateAuth authorization is implemented as a coordinated evaluation process rather than a collection of unrelated permission checks. + +The authorization pipeline brings together the operation being requested, the identities involved, the applicable permissions, and the policies responsible for enforcing security requirements. + +At its center are several distinct responsibilities: + +- **Context preparation:** Establish the security information required to evaluate an operation. +- **Permission resolution:** Make effective authorization grants available to the decision process. +- **Policy resolution:** Determine which security rules apply to the requested action. +- **Policy evaluation:** Evaluate the applicable requirements in a defined order. +- **Decision enforcement:** Permit the protected operation to proceed only when the authorization result allows it. + +This page examines how those responsibilities fit together. + +For an introduction to actions, permissions, and roles, see [Authorization](/docs/plugin-domains/authorization). For policy concepts and their intended use, see [Policies & Access Control](/docs/plugin-domains/policies). + +## Pipeline Architecture + +The authorization process can be represented as a sequence of stages: + +```text +Protected Operation + | + v + AccessContext + | + v +Context Enrichment + | + v +Permission Resolution + | + v +Invariant Evaluation + | + v +Global Policy Evaluation + | + v +Runtime Policy Resolution + | + v +Runtime Policy Evaluation + | + v + Final Decision + | + +---- Deny ----------------> Reject + | + +---- Reauthentication ---> Require Reauthentication + | + +---- Allow ---------------> Execute +``` + +This diagram describes the logical responsibilities of the pipeline. + +It should not be interpreted as a guarantee that every stage performs a separate database query or that every authentication mode uses the same mechanism to obtain authorization information. + +The important property is that **authorization is evaluated against a coherent access context before the protected operation is allowed to proceed**. + +## AccessContext as the Evaluation Boundary + +`AccessContext` carries the information required to evaluate an authorization request. + +It connects the authenticated actor, the intended target, the requested action, and the relevant security environment. + +Conceptually, its responsibilities include: + +| Information | Purpose | +|---|---| +| Actor | Identifies the principal performing the operation | +| Target | Identifies the account or resource affected | +| Action | Describes the protected operation | +| Tenant context | Establishes the relevant isolation boundaries | +| Claims | Provides authentication and authorization information | +| Permissions | Represents the effective permission information available for evaluation | + +The context is shared across the authorization process so that individual policies do not need to independently reconstruct the operation. + +### Actor and Target Separation + +Consider the action: + +```text +users.delete.admin +``` + +The actor is the principal requesting the deletion. + +The target is the user account being deleted. + +These identities may be different, and the authorization pipeline must preserve that distinction. + +For a self-service action: + +```text +users.delete.self +``` + +The action indicates a self-service operation, but the action name alone does not prove that actor and target are the same identity. + +That relationship must be established from trusted context and evaluated by the applicable security rules. + +### Context Integrity + +The reliability of the authorization decision depends on the integrity of the context. + +A target identifier or tenant identifier supplied by a client must not automatically be accepted as a trusted authorization fact. + +The pipeline must operate on the authenticated actor and appropriately resolved target information. + +A correctly implemented policy cannot compensate for a context that incorrectly identifies the actor or misrepresents the target. + +## Context Enrichment + +The initial authorization context may not contain all the information required by the applicable policies. + +Context enrichment makes additional authorization information available before evaluation. + +This can include resolving effective permissions and preparing them for efficient policy checks. + +Conceptually: + +```text +Authenticated Actor + | + v +Resolve Authorization Information + | + v +Effective Permissions + | + v +CompiledPermissionSet + | + v +Enriched AccessContext +``` + +The purpose of enrichment is to separate authorization data resolution from the individual rules that consume that data. + +A permission policy should not need to independently load role assignments and rebuild the user's effective permission set every time it evaluates an action. + +### Authorization Data Freshness + +Enrichment does not automatically guarantee that all authorization information is current. + +The source of permission data matters. + +For example, permissions obtained from current server-side state may reflect a recent role change, while permissions derived exclusively from an already-issued JWT may reflect an earlier state. + +The pipeline can evaluate the information it receives consistently, but it cannot make stale claims current without consulting an authoritative source. + +This distinction is particularly relevant to SemiHybrid and PureJwt authentication. + +## Permission Compilation + +UltimateAuth uses `CompiledPermissionSet` to represent permissions in a form suitable for repeated authorization checks. + +Permission compilation separates the representation of stored grants from the process of deciding whether an action is permitted. + +For example: + +```text +Requested action: + users.delete.admin + +Effective grants: + users.read.self + users.delete.admin + credentials.list.self +``` + +A permission policy can evaluate whether the effective grants cover the requested action. + +Conceptually: + +```csharp +permissionSet.IsAllowed(action); +``` + +This illustrates the permission-checking responsibility, rather than a complete public API integration example. + +### Wildcard Permissions + +Compiled permissions may also represent wildcard grants. + +For example: + +```text +users.* +users.profile.* +* +``` + +Wildcard matching must respect the permission model's defined action hierarchy. + +A broad grant should not be inferred merely because several currently visible permissions share a prefix. + +Where permission normalization replaces a complete action group with a wildcard, the normalization must preserve the intended permission semantics. + +### Compilation Is Not Authorization + +A compiled permission set is an efficient representation of grants. + +It does not independently establish that the actor may perform the operation. + +A matching permission can still be insufficient when: + +- The actor is not permitted to access the target. +- The operation crosses a prohibited tenant boundary. +- The account does not satisfy required security conditions. +- An operation-specific policy denies the action. + +Permission matching is one requirement within the broader authorization decision. + +## Policy Registration and Resolution + +Runtime policies must be discoverable for the operations they protect. + +UltimateAuth's authorization architecture includes policy registration and compilation concepts represented by: + +- `AccessPolicyRegistry` +- `CompiledAccessPolicySet` + +These components separate policy configuration from runtime selection. + +Conceptually: + +```text +Registered Policies + | + v +AccessPolicyRegistry + | + v +CompiledAccessPolicySet + | + v +Policies Matching Requested Action +``` + +### Action-Based Selection + +Policies may be associated with action prefixes or patterns. + +For example: + +```text +users.* +users.delete.* +users.delete.admin +``` + +A request for: + +```text +users.delete.admin +``` + +may select policies associated with the relevant action hierarchy. + +This allows broad policies to cover a domain while more specific policies enforce requirements for individual operations. + +### Prefix Matching and Boundaries + +Action matching must respect the intended structure of action identifiers. + +For example: + +```text +users.delete.admin +``` + +belongs to the `users` action domain. + +But an unrelated action whose text merely begins with similar characters should not automatically be treated as belonging to that domain. + +Policy matching must therefore follow the actual action-pattern semantics implemented by the registry. + +### Resolution Versus Evaluation + +Policy resolution answers: + +**Which policies are candidates for this operation?** + +Policy evaluation answers: + +**Do those policies permit this operation in the current context?** + +A policy may match an action pattern but still be inapplicable to a particular runtime context. + +That distinction is handled through policy applicability rules. + +## Runtime Applicability + +A policy can determine whether it applies to the current authorization context. + +UltimateAuth represents this responsibility through `AppliesTo`. + +For example, a policy restricting administrative self-modification may only be relevant when: + +- The requested action has administrative scope. +- The operation changes protected account state. +- The actor and target are the same identity. + +It may not apply to administrative read operations or unrelated domains. + +Conceptually: + +```text +Policy selected by action + | + v +AppliesTo(context) + | + +---- No ----> Skip policy + | + +---- Yes ---> Evaluate policy +``` + +This enables policies to remain focused on specific security requirements without requiring every action to have an entirely separate policy implementation. + +### Applicability Is Not a Security Bypass + +An applicability rule determines when a policy participates in evaluation. + +If a mandatory security requirement can be skipped because its applicability condition is too broad or incorrectly implemented, the resulting authorization boundary may be weaker than intended. + +Policies enforcing fundamental security invariants should not depend on application-controlled applicability rules that can bypass those invariants. + +## Policy Evaluation Stages + +UltimateAuth's policy architecture distinguishes between invariants, global policies, and runtime policies. + +These categories establish the order and scope of security evaluation. + +### Stage 1: Invariants + +Invariants represent mandatory security requirements. + +They are evaluated before ordinary policy stages. + +An invariant denial prevents the requested operation from proceeding, regardless of whether another policy would otherwise permit it. + +Invariants provide the strongest enforcement boundary within the policy pipeline. + +They should not be treated as ordinary permissions that can be overridden by a broader grant. + +### Stage 2: Global Policies + +Global policies enforce broadly applicable security requirements. + +They may examine the operation's context to determine whether their rule is relevant. + +Examples include requirements related to authentication state, account eligibility, and tenant isolation. + +Global policies allow these requirements to be enforced consistently without attaching identical rules to every individual action. + +### Stage 3: Runtime Policies + +Runtime policies are resolved according to the requested action and evaluated against the current context. + +They support requirements such as: + +- Required permissions. +- Self-access restrictions. +- Administrative operation constraints. +- Domain-specific authorization rules. + +Runtime policies extend the decision process without replacing the mandatory security requirements established by earlier stages. + +## IAccessAuthority and Decision Evaluation + +`IAccessAuthority` represents the authorization decision responsibility within the pipeline. + +The authority evaluates the applicable security requirements and produces an authorization result. + +Conceptually, the result can represent: + +| Result | Meaning | +|---|---| +| **Allow** | The applicable requirements permit the operation | +| **Deny** | A required security condition rejects the operation | +| **Reauthentication required** | The operation requires an additional authentication condition before proceeding | + +These outcomes must not be confused with the result of an individual policy. + +### Policy Allow Versus Final Allow + +An individual policy may return an allow result because its own requirement has been satisfied. + +That does not necessarily mean the entire operation is authorized. + +Other applicable policies may still deny the request or require reauthentication. + +**An individual policy's approval is not a universal authorization grant.** + +### Deny Precedence + +A denial from an applicable mandatory policy prevents the operation from being authorized. + +The pipeline must not allow a later permissive policy to override a previously established denial. + +This supports predictable enforcement across multiple independent security rules. + +### Reauthentication Requirements + +Some operations may require stronger or more recent authentication. + +A policy can represent that requirement separately from an ordinary denial. + +The authorization system must distinguish between: + +- An operation that is prohibited. +- An operation that may proceed after additional authentication. +- An operation that is currently authorized. + +The exact precedence and propagation of reauthentication results must follow the implemented decision engine. + +A reauthentication result should not be interpreted as permission to execute the protected operation immediately. + +## Permission Policies + +`MustHavePermissionPolicy` represents the connection between the permission model and the policy pipeline. + +Its responsibility is to evaluate whether the actor has the permission required for the requested action. + +Conceptually: + +```text +AccessContext.Action + | + v +CompiledPermissionSet + | + v +Permission Evaluation + | + v +Policy Decision +``` + +This design separates permission storage and resolution from permission enforcement. + +Roles and assignments determine effective grants. + +The compiled permission set represents those grants. + +The permission policy evaluates the requested action against them. + +The final authorization decision also considers the other applicable security policies. + +### Permissions Are Not a Replacement for Policies + +For example, an actor may possess: + +```text +users.delete.admin +``` + +That permission does not automatically authorize deleting any user in any tenant. + +The operation must still satisfy the relevant target, tenant, and account security requirements. + +This is why the pipeline evaluates multiple requirements rather than relying on a single permission match. + +## Orchestration and Protected Execution + +`AccessOrchestrator` coordinates authorization and the protected operation. + +Its purpose is to establish a consistent enforcement boundary. + +Conceptually: + +```text +Operation Requested + | + v +Prepare AccessContext + | + v +Resolve and Evaluate Policies + | + +---- Not Allowed ---> Stop + | + v +Execute Protected Operation +``` + +The security requirement is straightforward: + +**A protected operation must not execute merely because its caller reached an application service or endpoint.** + +Authorization must succeed at an enforcement boundary that covers the operation. + +### HTTP and In-Process Execution + +HTTP endpoints may enforce authorization through ASP.NET Core authentication and authorization integration. + +However, applications may also execute operations directly through in-process services. + +For example, a Blazor Server application can invoke a server-side service without making a separate HTTP request. + +A controller-level `[Authorize]` attribute does not automatically protect such a direct service invocation. + +Where operations can be reached through multiple execution paths, authorization must be enforced at an appropriate shared boundary. + +This prevents one entry point from bypassing rules enforced by another. + +## Determinism and Security + +A structured policy pipeline makes authorization behavior easier to reason about. + +For the same effective context, policy configuration, and authoritative security state, the decision should follow the same evaluation rules. + +However, authorization can depend on mutable information: + +- Account status. +- Tenant membership. +- Role assignments. +- Effective permissions. +- Session security state. +- Target ownership. + +A policy evaluation that uses stale or inconsistent data may produce a result that differs from one using current authoritative state. + +Deterministic policy evaluation therefore depends on both the evaluation rules and the consistency of the information supplied to them. + +### Time-of-Check and Time-of-Use + +Authorization may be evaluated before a protected operation modifies data. + +If relevant security or ownership information changes between the authorization decision and the mutation, a time-of-check/time-of-use problem may arise. + +Sensitive operations may require transactional consistency, concurrency controls, or revalidation within the protected operation. + +A policy pipeline provides a structured decision process, but it does not automatically make every application-level data mutation atomic. + +## Extensibility Boundaries + +The authorization pipeline is designed to support application-specific policies. + +Custom policies can introduce new requirements without replacing the underlying permission model or authentication engine. + +However, extensibility must preserve the security hierarchy. + +A custom policy should not: + +- Override mandatory invariants. +- Trust caller-supplied identity information without validation. +- Treat an action name as proof of actor-target ownership. +- Bypass tenant isolation through a broad permission. +- Assume that JWT claims always reflect current authorization state. + +Custom policies should evaluate well-defined requirements using trusted context and participate in the established decision model. + +## Architectural Perspective + +UltimateAuth's authorization pipeline separates the responsibilities involved in an access decision. + +`AccessContext` describes the operation. + +Context enrichment and permission compilation prepare the required authorization information. + +Policy resolution identifies the relevant rules. + +`IAccessAuthority` evaluates those rules according to the pipeline's decision semantics. + +`AccessOrchestrator` coordinates authorization with protected execution. + +Together, these components provide a structured foundation for context-aware, policy-driven authorization. + +**The pipeline's security comes not from the number of policies it executes, but from consistently enforcing the applicable requirements before a protected operation is allowed to proceed.** + +## Next Steps + +- [Authorization](/docs/plugin-domains/authorization) β€” Actions, permissions, roles, and scopes. +- [Policies & Access Control](/docs/plugin-domains/policies) β€” Policy concepts and access decision behavior. +- [Session Security Model](/docs/security/session-security) β€” Authentication security state and validation boundaries. +- [Token Security](/docs/security/token-security) β€” Credential validity and authorization freshness. \ No newline at end of file diff --git a/docs/content/security/policy-pipeline.md b/docs/content/security/policy-pipeline.md deleted file mode 100644 index f7d28ba3..00000000 --- a/docs/content/security/policy-pipeline.md +++ /dev/null @@ -1,230 +0,0 @@ ---- -title: Policy Pipeline -order: 4 -group: security ---- - - -# 🧠 Policy Pipeline Deep Dive - -UltimateAuth does not rely on simple role checks. - -Authorization is executed through a **multi-stage policy pipeline** that evaluates: - -- invariants (always enforced rules) -- global policies -- runtime (action-based) policies - -## 🧠 Core Principle - -πŸ‘‰ Authorization is not a single check -πŸ‘‰ It is a **decision pipeline** - -## πŸ” High-Level Flow - -```text -AccessContext - ↓ -Enrichment (claims, permissions) - ↓ -Invariants - ↓ -Global Policies - ↓ -Runtime Policies (action-based) - ↓ -Final Decision -``` - -## 🧩 AccessContext - -Every authorization decision is based on an `AccessContext`. - -It contains: - -- actor (who is making the request) -- target (resource / user) -- tenant -- action (string-based, e.g. "users.delete") -- attributes (claims, permissions, etc.) - -πŸ‘‰ Everything in the pipeline reads from this context - -## πŸ”„ Step 1: Context Enrichment - -Before policies run, context is enriched: - -- permissions are loaded -- compiled into `CompiledPermissionSet` -- attached to context - -πŸ‘‰ This allows policies to run without hitting storage repeatedly - -## πŸ›‘ Step 2: Invariants - -Invariants are **always enforced rules**. - -They run first and cannot be bypassed. - -Examples: - -- user must be authenticated -- cross-tenant access is denied -- request must be valid - -πŸ‘‰ If an invariant fails: - -```text -β†’ DENY immediately -``` - -## 🌐 Step 3: Global Policies - -Global policies apply to all requests but are conditional. - -Examples: - -- RequireActiveUserPolicy -- DenyAdminSelfModificationPolicy - -Each policy: - -- decides if it applies (`AppliesTo`) -- evaluates (`Decide`) - -πŸ‘‰ Important: - -- β€œAllow” means *no objection* -- not final approval - -## 🎯 Step 4: Runtime Policies - -Runtime policies are: - -- action-based -- dynamically resolved - -They come from: - -```text -AccessPolicyRegistry β†’ CompiledAccessPolicySet -``` - -Policies are selected by: - -```text -context.Action.StartsWith(prefix) -``` - -Example: - -```text -Action: users.delete.admin - -Matches: -- users.* -- users.delete.* -- users.delete.admin -``` - -## βš–οΈ Step 5: Decision Engine - -All policies are evaluated by: - -πŸ‘‰ `IAccessAuthority` - -Evaluation order: - -1. Invariants -2. Global policies -3. Runtime policies - -### Decision Rules - -- First **deny** β†’ stops execution -- β€œAllow” β†’ continue -- β€œRequiresReauthentication” β†’ tracked - -Final result: - -```text -Allow -Deny(reason) -ReauthenticationRequired -``` - -## πŸ” Permission Integration - -Permissions are not directly checked in services. - -Instead: - -- permissions are compiled -- policies use them - -Example policy: - -```text -MustHavePermissionPolicy -``` - -Checks: - -```text -CompiledPermissionSet.IsAllowed(action) -``` - - -πŸ‘‰ This decouples: - -- permission storage -- authorization logic - -## πŸ”₯ Why This Matters - -This pipeline enables: - -- composable security rules -- consistent enforcement -- separation of concerns -- extensibility - -Compared to typical systems: - -| Feature | Traditional | UltimateAuth | -|---------------------|------------|-------------| -| Inline checks | βœ… | ❌ | -| Central pipeline | ❌ | βœ… | -| Policy composition | ❌ | βœ… | -| Action-based rules | ❌ | βœ… | - -## ⚠️ Design Tradeoff - -This model introduces: - -- more abstraction -- more components - -πŸ‘‰ But gives: - -- predictability -- auditability -- flexibility - -## 🧠 Mental Model - -If you remember one thing: - -πŸ‘‰ Authorization is not β€œif (role == admin)” -πŸ‘‰ It is a **pipeline of decisions** - -## ➑️ Summary - -UltimateAuth authorization: - -- is policy-driven -- runs through a structured pipeline -- separates invariants, global rules, and action rules -- produces deterministic decisions - -πŸ‘‰ This makes it suitable for complex, multi-tenant, security-sensitive systems diff --git a/docs/content/security/refresh-rotation.md b/docs/content/security/refresh-rotation.md index 51c00e3f..6abf54a8 100644 --- a/docs/content/security/refresh-rotation.md +++ b/docs/content/security/refresh-rotation.md @@ -1,170 +1,470 @@ --- -title: Refresh Rotation +title: Refresh Rotation & Replay Protection order: 3 group: security --- +# Refresh Rotation & Replay Protection -# πŸ”„ Refresh Token Rotation +Refresh credentials allow authentication to continue without requiring the user to repeat the original login process. -Refresh tokens in UltimateAuth are not simple long-lived tokens. +Because a refresh credential may remain useful longer than an access credential, its compromise can have significant security consequences. -They are part of a **rotation-based security system** designed to: +**Refresh rotation** is one mechanism for reducing this risk. It replaces previously issued refresh authentication material as part of a successful continuation operation. -- prevent token replay -- detect token theft -- enforce forward-only session progression +However, secure rotation involves more than generating a new token. It requires consistent state transitions, concurrency control, invalid credential handling, and a clear policy for suspected credential reuse. -## 🧠 Why Rotation? +This page examines those security properties and their limitations. -In traditional systems: +For token formats, access token validation, and general refresh behavior, see [Token Security](/docs/security/token-security). -- refresh tokens are long-lived -- they can be reused multiple times +## Rotation as a Security Protocol -πŸ‘‰ If stolen, an attacker can: +In a rotating refresh strategy, a successfully consumed refresh credential is replaced with new authentication material. -- silently keep refreshing access tokens -- stay undetected +Conceptually: -UltimateAuth solves this with: +```text +Refresh A + | + | Successful continuation + v +Refresh A consumed +Refresh B issued + | + | Successful continuation + v +Refresh B consumed +Refresh C issued +``` + +The security objective is to prevent an older refresh credential from remaining indefinitely reusable after a successful continuation. + +A rotating refresh mechanism must maintain enough authoritative state to distinguish between credentials that are currently eligible for use and credentials that have already been consumed or invalidated. + +This is fundamentally different from validating a self-contained access token using only its signature and expiration. + +### What Rotation Does and Does Not Guarantee + +Rotation can: + +- Limit the continued usefulness of previously consumed credentials. +- Provide a signal when an invalidated credential is presented again. +- Support targeted security responses to suspicious continuation attempts. +- Reduce the opportunities for prolonged reuse of the same refresh credential. + +Rotation does not inherently: + +- Prevent the initial theft of a refresh credential. +- Prove that a reuse attempt was made by an attacker. +- Guarantee that the legitimate client wins a race against an attacker. +- Invalidate every previously issued access token. +- Guarantee immediate containment without an enforced revocation mechanism. + +The strength of the protection depends on how the refresh strategy implements and validates its state transitions. + +## Authentication Mode Considerations + +Refresh behavior is not identical across UltimateAuth's authentication modes. + +| Mode | Refresh and rotation considerations | +|---|---| +| **PureOpaque** | Uses opaque session continuation rather than a separate access/refresh credential pair | +| **Hybrid** | Uses opaque access and refresh credentials with server-authoritative session state | +| **SemiHybrid** | Uses JWT access tokens and server-managed refresh lifecycle state | +| **PureJwt** | Uses locally validated JWT access tokens and may use an optional refresh store | + +The presence of a refresh operation does not necessarily mean that a separate refresh credential is replaced on every invocation. + +Depending on the configured mode and strategy, a continuation operation may rotate credentials, update existing server-managed state, or follow another supported lifecycle behavior. + +**Rotation guarantees must be evaluated against the selected refresh strategy, not assumed from the existence of a refresh endpoint.** + +See [Authentication Modes](/docs/fundamentals/auth-modes). + +## Refresh Credential State + +A rotating refresh mechanism needs to distinguish credentials that remain eligible for continuation from credentials that are no longer acceptable. + +Conceptually, relevant conditions include: + +| Condition | Meaning | +|---|---| +| **Active** | The credential is eligible for a refresh attempt, subject to all applicable checks | +| **Consumed or replaced** | The credential has already participated in a successful rotation | +| **Revoked** | The credential or associated refresh authority has been explicitly invalidated | +| **Expired** | The credential has exceeded its permitted lifetime | +| **Invalid or unknown** | The presented value cannot be resolved to an acceptable refresh credential | + +These are conceptual security conditions, not necessarily distinct persisted enum values in every implementation. + +A consumed credential and an administratively revoked credential may both be unusable, but the reasons for rejection are different. + +That distinction matters when evaluating whether a failed refresh represents ordinary expiration, an invalid request, or potential credential reuse. + +## The Rotation Transaction + +A successful rotating refresh operation involves a sequence of security-sensitive steps. + +Conceptually: + +1. Resolve the presented refresh credential. +2. Validate its eligibility for continuation. +3. Validate the applicable session, account, and client context. +4. Establish that the credential has not already been consumed. +5. Apply the configured rotation transition. +6. Issue the replacement authentication material. +7. Persist the resulting state consistently. +8. Return the renewed credentials to the client. + +The exact implementation and ordering depend on the selected refresh strategy. -πŸ‘‰ **single-use refresh tokens (rotation)** +The important security requirement is that the transition must not leave multiple conflicting refresh credentials valid because two requests observed the same earlier state. -## πŸ” Rotation Model +### Atomicity -Each refresh token is: +Consider a refresh credential `A`. -- used exactly once -- replaced with a new token -- linked to a chain +Two requests arrive almost simultaneously: ```text -Token A β†’ Token B β†’ Token C β†’ ... +Request 1: Refresh(A) +Request 2: Refresh(A) ``` -When a refresh happens: +An unsafe implementation might allow both requests to read `A` as active before either marks it consumed. -1. Token A is validated -2. Token A is **revoked** -3. Token B is issued -4. Token A is marked as replaced by B +Both requests could then issue different replacement credentials: -## πŸ” Token State +```text + Refresh A + | + +------+------+ + | | + Request 1 Request 2 + | | + v v + Refresh B Refresh C +``` + +If both replacements remain valid, the system has unintentionally created competing continuation paths. + +A single-use rotation strategy must prevent this outcome through an appropriate atomic state transition or concurrency mechanism. -A refresh token can be: +Possible implementation techniques include transactional updates, compare-and-swap operations, or other storage-specific concurrency controls. -- Active β†’ valid and usable -- Revoked β†’ already used or manually revoked -- Expired β†’ lifetime exceeded -- Replaced β†’ already rotated +The precise mechanism is an implementation concern, but the security property is essential: -πŸ‘‰ Only **active tokens** are valid +**Only the transition permitted by the configured rotation semantics may succeed.** -## 🚨 Reuse Detection +## Concurrent Refresh Requests -This is the most critical security feature. +Concurrent refresh requests are not necessarily malicious. -If a refresh token is used **after it has already been rotated**, it means: +They can occur when: -πŸ‘‰ The token was reused -πŸ‘‰ Likely stolen +- Several API requests discover an expired access token at the same time. +- Multiple browser tabs attempt to refresh authentication. +- A client retries a request after a network interruption. +- Background and foreground operations initiate refresh simultaneously. -### What happens? +Without coordination, a legitimate client can submit the same refresh credential more than once. -When reuse is detected: +### Client-Side Coordination -- the system identifies the session chain -- the entire chain can be revoked -- all related sessions become invalid +Applications should avoid issuing unnecessary concurrent refresh requests. -πŸ‘‰ This immediately cuts off both: +A common approach is to coordinate refresh through a single in-progress operation and allow other requests to await its result. + +Conceptually: + +```text +API Request A ──┐ +API Request B ──┼──> One refresh operation +API Request C β”€β”€β”˜ | + v + Renewed credentials + | + +-----------+-----------+ + | | | + v v v + Retry A Retry B Retry C +``` -- attacker -- legitimate user (forcing reauthentication) +Client coordination improves reliability and reduces accidental reuse attempts. -## πŸ”— Chain Awareness +However, it is not a replacement for server-side concurrency enforcement. -Refresh tokens belong to a **session chain**. +Different devices, processes, or compromised clients cannot be assumed to share the same coordination mechanism. -This enables: +### Server-Side Enforcement -- tracking rotation history -- detecting anomalies -- applying revocation at the correct scope +The server remains responsible for enforcing the configured refresh credential lifecycle. -Without chains: +A client-side lock cannot guarantee that an attacker or another client instance will not present the same credential. -❌ You cannot safely detect reuse -❌ You cannot know which tokens belong together +For single-use rotation, the server must ensure that a consumed credential cannot successfully establish another independent continuation path. -## πŸ”„ Rotation Flow +## Refresh Credential Reuse + +Refresh credential reuse occurs when a credential is presented after it is no longer eligible for the operation. + +For a single-use rotating strategy, presenting a previously consumed credential is a particularly important case. ```text -Client β†’ Refresh(Token A) - β†’ Validate A - β†’ Revoke A - β†’ Issue B - β†’ Return new tokens +Initial state: + Refresh A is active + +First request: + Refresh(A) succeeds + A is consumed + B is issued + +Later request: + Refresh(A) is attempted again + A is no longer eligible ``` -## ⚠️ Invalid Scenarios +The later request must not be accepted as a new successful use of `A`. + +### Reuse Is a Security Signal + +A consumed refresh credential may be presented again because: + +- An attacker copied the credential before rotation. +- A legitimate client retried after losing the refresh response. +- Two client operations raced to refresh simultaneously. +- Client credential storage was not updated consistently. -### 1. Expired Token +The server may not have enough information to distinguish these situations conclusively. + +Therefore, **reuse detection identifies a suspicious credential lifecycle event; it does not automatically prove credential theft.** + +### Detection Versus Response + +Detecting invalid reuse and responding to it are separate responsibilities. + +A refresh strategy may reject the request without taking broader action. + +A stricter security policy may invalidate additional refresh state or revoke an associated session or chain. + +The appropriate response depends on the implementation, authentication mode, and supported security policy. + +A documentation claim that reuse always causes chain-wide revocation would be correct only if that behavior is actually enforced by the selected strategy. + +## Replay and Token Theft Scenarios + +Rotation is particularly useful when reasoning about stolen refresh credentials. + +### Scenario 1: The Legitimate Client Refreshes First + +An attacker copies refresh credential `A`, but the legitimate client uses it before the attacker. ```text -Token expired β†’ reject +Legitimate client: + A -> B + +Attacker: + Attempts A + A is already consumed ``` -### 2. Revoked Token +If the rotation state is enforced correctly, the attacker's attempt to use `A` cannot produce another successful continuation. + +The server may also be able to identify the attempt as reuse. + +### Scenario 2: The Attacker Refreshes First + +An attacker copies active refresh credential `A` and successfully uses it before the legitimate client. ```text -Token already used β†’ reuse detected +Attacker: + A -> B + +Legitimate client: + Attempts A + A is already consumed ``` -### 3. Session Mismatch +In this case, the attacker may already possess the replacement credential `B`. + +Rejecting the legitimate client's later use of `A` is necessary, but it does not by itself remove the attacker's newly obtained credential. + +A broader revocation response may be required to contain the compromise. + +This illustrates why rotation and reuse detection are not identical to automatic compromise recovery. + +### Scenario 3: A Refresh Response Is Lost + +The legitimate client sends `A`. + +The server successfully consumes `A` and issues `B`, but the response never reaches the client. + +The client still possesses `A`. + +A retry may now appear to be reuse. + +This creates an important reliability and security tradeoff. + +Strict single-use semantics can reject the retry, while a more tolerant continuation strategy requires carefully defined behavior that does not accidentally create an exploitable replay window. + +The chosen strategy must handle this situation consistently. + +## Session and Chain Association + +In session-managed authentication modes, refresh operations can be associated with session and chain state. + +This allows the authentication infrastructure to evaluate continuation within the relevant security context. + +For example, a refresh operation may need to establish that: + +- The refresh credential belongs to the expected authentication state. +- The associated session remains eligible for continuation. +- The relevant chain has not been revoked. +- The account-level security state permits continuation. +- Any configured device context requirements are satisfied. + +The exact checks depend on the mode and implementation. + +### Chain-Level Containment + +When a refresh compromise is associated with a session chain, chain-level revocation can provide a broader response than invalidating a single refresh credential. + +This may be appropriate when multiple related sessions or continuation credentials could be affected. + +However, a chain is a logical session continuity boundary, not a cryptographically verified physical device. + +Chain-level revocation controls the associated authentication state; it does not permanently prevent the physical device from authenticating again. + +See [Session Security Model](/docs/security/session-security). + +## Invalid Refresh Conditions + +Not every failed refresh should be classified as token reuse. + +Different failure conditions have different security meanings. + +| Condition | Expected security interpretation | +|---|---| +| Unknown credential | No acceptable refresh authority was established | +| Expired credential | The permitted continuation lifetime has ended | +| Explicitly revoked credential | The refresh authority has been invalidated | +| Previously consumed credential | Potential reuse under a single-use rotation strategy | +| Session or chain mismatch | Presented credential is inconsistent with expected authentication context | +| Account security invalidation | Continuation is no longer permitted under current account security state | +| Concurrent use | Multiple attempts may compete for the same continuation authority | + +A secure implementation must reject invalid continuation attempts according to the selected strategy. + +The application should not assume that every rejection requires the same logging, revocation, or user-facing response. + +## Rotation and Access Token Validity + +Refresh rotation affects authentication continuation. + +It does not necessarily affect access tokens that were issued earlier. + +For example: ```text -Token does not belong to expected session β†’ reject +JWT Access A issued +Refresh A issued + | + v +Refresh A rotated to Refresh B +JWT Access B issued ``` -## 🧠 Security Guarantees +If `JWT Access A` has not expired, it may remain valid under local JWT validation even after refresh rotation. + +Similarly, revoking `Refresh B` may prevent future refresh operations without immediately invalidating `JWT Access B`. + +This distinction is especially important in SemiHybrid and PureJwt. + +### Revocation Scope + +There are separate questions to answer: + +- Can the old refresh credential be used again? +- Can the replacement refresh credential still be used? +- Is the associated server-managed session valid? +- Are previously issued access tokens still accepted? + +A refresh security operation may affect some of these conditions without affecting all of them. + +The authentication mode determines where current server-side state is consulted. + +## Rotation and Credential Storage + +Refresh rotation depends on maintaining authoritative information about credential eligibility. + +For opaque refresh credentials, an implementation may store a one-way verifier or hash rather than the original credential value. + +This reduces the exposure of usable credentials if the persistence store is disclosed. + +However, secure storage alone does not provide rotation. + +The implementation must also maintain the necessary lifecycle state and enforce transitions correctly. + +### Operational Requirements + +A production refresh mechanism should account for: + +- Secure credential generation. +- Appropriate credential expiration. +- Protected transport and client storage. +- Consistent persistence updates. +- Concurrency control. +- Safe handling of invalid and consumed credentials. +- Revocation behavior appropriate to the authentication mode. + +The exact persistence and concurrency mechanisms are implementation-specific. + +## Security Tradeoffs + +Strict rotation improves control over credential reuse, but it introduces operational complexity. + +| Benefit | Corresponding consideration | +|---|---| +| Consumed credentials cannot remain reusable | Requires authoritative lifecycle state | +| Reuse attempts can become observable | Legitimate retries may resemble attacks | +| Continuation can be invalidated independently | Revocation must be consistently enforced | +| Replacement credentials limit prolonged reuse | Concurrent refresh requests require coordination | +| Compromise responses can target session state | Broader revocation may interrupt legitimate sessions | + +These tradeoffs are not reasons to avoid rotation. + +They are reasons to treat it as a security protocol rather than a simple token replacement operation. + +## Security Design Principles -Rotation ensures: +**Rotation must have defined semantics.** A refresh operation should not be described as single-use unless the configured strategy actually enforces single-use behavior. -- refresh tokens are forward-only -- old tokens cannot be reused safely -- stolen tokens are detectable -- compromise triggers containment +**State transitions must be consistent.** Concurrent requests must not create conflicting valid continuation paths under a single-use strategy. -## πŸ”₯ Why This Matters +**Reuse detection is not theft attribution.** A reused credential is suspicious, but the cause may be malicious or accidental. -Compared to traditional refresh tokens: +**Detection and containment are separate.** Rejecting reuse, revoking refresh state, and revoking a session chain are different security actions. -| Feature | Traditional | UltimateAuth | -|----------------------|------------|-------------| -| Reusable tokens | βœ… | ❌ | -| Reuse detection | ❌ | βœ… | -| Chain awareness | ❌ | βœ… | -| Automatic containment| ❌ | βœ… | +**Client coordination is helpful but insufficient.** The server must enforce credential eligibility independently. -## ⚠️ Design Tradeoff +**Access and refresh validity remain distinct.** Refresh rotation does not automatically invalidate previously issued JWT access tokens. -Rotation requires: +**Mode-specific behavior matters.** The same security guarantee must not be attributed to every authentication mode without considering its actual refresh implementation. -- token storage -- state tracking -- additional validation logic +## Architectural Perspective -πŸ‘‰ UltimateAuth chooses security over simplicity. +Refresh rotation is a mechanism for controlling how authentication authority continues over time. -## 🧠 Mental Model +Its security value comes from enforcing credential lifecycle transitions, preventing invalid continuation, and supporting an appropriate response when a previously consumed credential is presented again. -If you remember one thing: +UltimateAuth's authentication modes provide different continuation models. Rotation behavior must therefore be understood in the context of the selected mode, its server-managed state, and its configured refresh strategy. -πŸ‘‰ A refresh token is not a reusable key -πŸ‘‰ It is a **one-time step in a chain** +**A secure rotating refresh mechanism does not merely issue a new credential. It ensures that the previous credential can no longer establish an unauthorized continuation path.** -## ➑️ Next Step +## Next Steps -Continue to **Access Token Behavior** to understand how short-lived tokens interact with rotation. +- [Token Security](/docs/security/token-security) β€” Access and refresh credential security fundamentals. +- [Session Security Model](/docs/security/session-security) β€” Hierarchical validation and revocation boundaries. +- [Session Model & Lifecycle](/docs/sessions-devices/session-lifecycle) β€” Session continuity, expiration, and revocation. +- [Authentication Modes](/docs/fundamentals/auth-modes) β€” Mode-specific authentication and refresh behavior. \ No newline at end of file diff --git a/docs/content/security/session-security-model.md b/docs/content/security/session-security-model.md deleted file mode 100644 index e905d138..00000000 --- a/docs/content/security/session-security-model.md +++ /dev/null @@ -1,221 +0,0 @@ ---- -title: Session Security Model -order: 1 -group: security ---- - - -# πŸ” Session Security Model - -UltimateAuth is built around a **hierarchical session model**. - -This is the foundation of its security design. - -## 🧠 Why a Session Model? - -Many systems treat authentication as one of these: - -- a cookie -- a bearer token -- a login flag - -That works for simple apps. - -It breaks down when you need: - -- per-device isolation -- targeted revocation -- security state propagation -- reliable reauthentication boundaries - -UltimateAuth solves this by modeling authentication as: - -```text -Root β†’ Chain β†’ Session -``` - -
- -## 🧩 The Three Layers - -### πŸ”Ή Root - -A **Root** represents the user-level authentication authority. - -It is: - -- tenant-bound -- user-bound -- long-lived -- security-versioned - -The root is the highest-level trust anchor for authentication state. - -### πŸ“± Chain - -A **Chain** represents a device-level authentication boundary. - -It groups sessions that belong to the same device or client context. - -A chain is where UltimateAuth models: - -- device continuity -- touch activity -- rotation tracking -- device-level revoke - -### πŸ”‘ Session - -A **Session** is a single authentication instance. - -It is the most granular identity proof in the system. - -A session contains: - -- creation time -- expiration time -- revocation state -- security version snapshot -- chain relationship -- device snapshot -- claims snapshot - - -## πŸ”— Relationship Model - -```text -User - └── Root - └── Chain (device) - └── Session (login instance) -``` - -πŸ‘‰ Root answers: **what is the current security authority for this user?** -πŸ‘‰ Chain answers: **which device context is this?** -πŸ‘‰ Session answers: **which authentication instance is being used?** - -## πŸ›‘ Security Versioning - -One of the most important protections in UltimateAuth is **security versioning**. - -A root maintains a security version. - -Each session stores the security version that existed at creation time. - -Validation compares them. - -If they no longer match: - -```text -Session β†’ invalid -``` - -This is how UltimateAuth can invalidate existing sessions after events such as: - -- password change -- credential reset -- account recovery -- administrative security action - -## πŸ” Validation Model - -Session validation is not a single check. - -It is a layered verification process. - -A session is considered valid only if all of these still hold: - -- the session exists -- the session is active -- the chain exists -- the chain is active -- the chain belongs to the expected tenant -- the chain matches the session -- the root exists -- the root is not revoked -- the root matches the chain -- the root security version matches the session snapshot - -## πŸ“± Device Awareness - -Chains provide device-level isolation. - -When a device identifier is available, validation can compare: - -- device stored on chain -- device presented by request - -If they do not match, UltimateAuth can reject the request depending on configuration. - -πŸ‘‰ This makes device-bound security enforceable without turning every authentication flow into a custom implementation. - -## ⏱ Expiration and Activity - -UltimateAuth separates: - -- session expiration -- chain activity -- root security state - -This is important. - -A session may expire because of time. -A chain may become inactive because of idle timeout. -A root may invalidate everything because security state changed. - -These are different failure modes with different meanings. - -## πŸšͺ Revocation Boundaries - -Revocation is also hierarchical. - -### Session revoke -Invalidates one authentication instance. - -### Chain revoke -Invalidates all sessions for one device. - -### Root revoke -Invalidates all chains and sessions for the user. - -πŸ‘‰ This gives UltimateAuth targeted containment. - -Instead of β€œlog out everywhere or nowhere,” -you can revoke exactly the right scope. - -## πŸ”₯ Why This Matters - -This model gives you controls that flat token systems usually do not provide: - -- revoke one device without affecting others -- invalidate sessions after security changes -- reason about device trust explicitly -- separate authentication lifetime from token lifetime - -## ⚠️ Design Tradeoff - -This model is more sophisticated than plain JWT-only authentication. - -That is intentional. - -UltimateAuth chooses: - -- explicit state -- revocability -- traceable security decisions - -over: - -- minimal infrastructure -- purely stateless assumptions - -## 🧠 Mental Model - -If you remember one thing: - -πŸ‘‰ Authentication in UltimateAuth is not β€œa token” -πŸ‘‰ It is a **security hierarchy with revocation boundaries** - -## ➑️ Next Step - -Continue to **Refresh Token Rotation** to understand how continuation security works after login. diff --git a/docs/content/security/session-security.md b/docs/content/security/session-security.md new file mode 100644 index 00000000..395e18ae --- /dev/null +++ b/docs/content/security/session-security.md @@ -0,0 +1,463 @@ +--- +title: Session Security Model +order: 1 +group: security +--- + +# Session Security Model + +A valid authentication credential is not necessarily sufficient to establish that an authentication session should still be trusted. + +A session may have been revoked, its associated account security state may have changed, or the client may no longer satisfy the conditions under which authentication was established. + +UltimateAuth addresses these concerns through a **hierarchical session security model**, with explicit boundaries for session validity, device-associated continuity, and account-level invalidation. + +In session-managed authentication modes, the security hierarchy is: + +**Root β†’ Chain β†’ Session** + +This page focuses on the security guarantees, validation requirements, and enforcement boundaries of that hierarchy. + +For the underlying entities and their lifecycle, see [Session Model & Lifecycle](/docs/sessions-devices/session-lifecycle). + +## Security Objectives + +The session security model is designed to support several distinct requirements. + +| Objective | Security purpose | +|---|---| +| **Session isolation** | Invalidate a particular authentication session without necessarily affecting others | +| **Chain isolation** | Control sessions associated with a particular device or client context | +| **Account-level invalidation** | Invalidate authentication state across the account's session hierarchy | +| **Security state propagation** | Detect changes to account-level authentication security | +| **Context consistency** | Ensure that related session, chain, root, and tenant information remains consistent | +| **Lifecycle enforcement** | Reject authentication state that has expired or been revoked | + +These requirements are related, but they should not be collapsed into a single token expiration check. + +A token can remain structurally valid even when the server-managed authentication state associated with it is no longer acceptable. + +Whether that state is consulted during a protected request depends on the authentication mode. + +## Security Boundaries + +UltimateAuth's session hierarchy separates security responsibilities into three levels. + +```text +Account-level security + | + v + SessionRoot + | + | Account-wide security state + | Security version + | + v + SessionChain + | + | Session continuity + | Device context + | Chain-level revocation + | + v + Session + | + | Individual validity + | Expiration + | Revocation + | + v + Authentication state +``` + +Each level provides a distinct boundary for evaluating or invalidating authentication state. + +### Root Security Boundary + +The root represents account-level authentication security. + +It provides a common authority for security state that affects sessions associated with the same account. + +This enables account-wide invalidation without requiring the user identity itself to be deleted or recreated. + +### Chain Security Boundary + +A chain groups related authentication sessions and may be associated with a device or client environment. + +It provides a boundary for revoking related sessions together. + +A chain is not proof of physical device identity, and its existence does not automatically mean that the associated device is trusted. + +### Session Security Boundary + +A session represents individual server-managed authentication state. + +It can be independently subject to expiration, revocation, and consistency checks. + +A session is not equivalent to an access token: the token is presented by the client, while the session represents server-managed security state where the selected mode uses it. + +## Layered Session Validation + +In a session-authoritative authentication mode, accepting a presented credential requires more than recognizing its identifier. + +The associated session hierarchy must satisfy the applicable validity and consistency requirements. + +These requirements can be understood in several categories. + +### Session Validity + +The session must satisfy its own security conditions. + +Relevant checks include: + +- The session exists in the authoritative session store. +- The session has not been revoked. +- The session has not exceeded its applicable expiration limits. +- The session is associated with the expected chain. + +A credential referencing a missing, expired, or revoked session must not establish a valid session-authoritative authentication state. + +### Chain Validity + +The associated chain must satisfy the applicable chain security conditions. + +These may include: + +- The chain exists. +- The chain has not been revoked. +- The chain belongs to the expected account and tenant context. +- The session-to-chain relationship is consistent. +- Any applicable chain-level expiration or inactivity conditions are satisfied. + +Chain validation prevents an otherwise recognizable session from bypassing a security restriction applied to its broader session continuity boundary. + +### Root Validity + +The root must satisfy the account-level security requirements. + +Relevant conditions include: + +- The expected root exists. +- The root is associated with the expected identity and tenant context. +- The chain belongs to the expected root. +- The root has not been invalidated. +- Applicable security version requirements are satisfied. + +This prevents a valid-looking session from being accepted when its account-level security authority has changed. + +### Relationship Consistency + +The hierarchy must also be internally consistent. + +A session cannot be considered valid merely because a session record, a chain record, and a root record each exist independently. + +Their ownership and association must match the expected authentication context. + +This is especially important in multi-tenant applications, where a mismatched tenant or account association must not be accepted as a valid authentication relationship. + +**The security decision concerns the entire applicable hierarchy, not an isolated record.** + +## Security Versioning + +Security versioning allows UltimateAuth to represent changes in account-level authentication security. + +A root maintains a security version, and a session can retain the version associated with its creation. + +During validation, the system can compare these values. + +### Matching Security State + +```text +Root.SecurityVersion = 7 +Session.SecurityVersionAtCreation = 7 + +Security version check: PASS +``` + +The session was created under the current security version. + +This does not mean that the session is automatically valid; expiration, revocation, and other applicable checks must still succeed. + +### Changed Security State + +```text +Root.SecurityVersion = 8 +Session.SecurityVersionAtCreation = 7 + +Security version check: FAIL +``` + +The session was created under an earlier account-level security state. + +When security version validation is enforced, the mismatch invalidates that session. + +### Why Versioning Matters + +Without a shared security version, an account-wide security change may require every individual session to be located and updated before the change can be enforced. + +A security version provides a common condition against which existing session state can be evaluated. + +This can support responses to events such as: + +- Credential changes or recovery. +- Administrative security actions. +- Account compromise. +- Account-level session invalidation. + +However, the triggering operation and configured session revocation policy determine whether a security version change should occur. + +Not every credential change must automatically invalidate every session. + +For example, UltimateAuth supports credential-related session handling policies such as `KeepAll`, `KeepCurrent`, and `RevokeAll`. + +A security operation must preserve the intended policy semantics rather than indiscriminately invalidating the entire account hierarchy. + +See [Credentials](/docs/plugin-domains/credentials). + +## Device Context Validation + +Device-associated session management can add security conditions to session validation. + +A chain may be associated with a device identifier or other client context. + +When device binding checks are configured and enforced, the system can compare the expected context with the context presented by a request. + +For example: + +```text +Chain.DeviceId = device-A +Request.DeviceId = device-B + +Device context mismatch +``` + +A configured mismatch policy may reject the operation. + +However, this comparison must be interpreted correctly. + +### Identification Is Not Authentication + +A device identifier is an identifying value, not necessarily a secret. + +If an attacker can copy or forge that value, matching the identifier does not prove that the request originated from the legitimate physical device. + +Device context validation can enforce consistency with recorded metadata, but stronger device authentication requires an appropriate proof mechanism. + +### Device Metadata Is Not a Security Guarantee + +Operating system, browser, platform, and IP information may provide useful context. + +They are not universally stable or unique. + +Changes to this information can occur during legitimate usage, and matching values can occur across unrelated clients. + +Applications should distinguish between: + +- Informational device metadata. +- Configured device consistency requirements. +- Cryptographically established device binding. + +The security guarantee must reflect the mechanism actually enforced. + +See [Device Management](/docs/sessions-devices/device-management). + +## Expiration and Inactivity + +Expiration is a security control, not merely a cleanup mechanism. + +UltimateAuth distinguishes between time-based validity and explicit security invalidation. + +Depending on the configured mode and options, relevant conditions may include: + +| Condition | Security meaning | +|---|---| +| Session lifetime | The authentication state has exceeded its permitted duration | +| Idle timeout | The required activity has not occurred within the allowed interval | +| Sliding expiration | Qualifying activity can extend validity within configured limits | +| Absolute lifetime | Authentication state cannot remain valid beyond a fixed boundary | +| Revocation | Authentication state has been explicitly invalidated | + +A session may be expired without being revoked. + +Similarly, a session may be revoked before reaching its natural expiration. + +These conditions should remain distinguishable for correct enforcement and security diagnostics. + +### Activity Tracking Is Not Universal + +Session activity may be recorded through touch operations. + +However, a locally validated JWT does not necessarily cause a server-side session touch on every protected request. + +Therefore, inactivity enforcement depends on where authoritative activity state is maintained and when it is checked. + +An application should not assume that all authentication modes provide identical idle-timeout guarantees. + +## Hierarchical Revocation + +Revocation allows authentication state to be invalidated before its normal expiration. + +UltimateAuth's hierarchy supports different revocation scopes. + +### Session-Level Revocation + +Targets an individual session. + +Other sessions may remain valid if their own security conditions continue to be satisfied. + +This is appropriate for terminating a particular authentication instance. + +### Chain-Level Revocation + +Targets the session continuity associated with a chain. + +It can invalidate related sessions without necessarily affecting other chains belonging to the same account. + +This supports targeted containment when a particular client environment is suspected of compromise. + +### Root-Level Revocation + +Targets the account-level session security boundary. + +It can invalidate the applicable session hierarchy, including chains and sessions associated with that root. + +Security versioning can contribute to this mechanism by invalidating sessions created under earlier account-level security state. + +### Revocation Scope and Enforcement + +```text +Session revoke + -> Targeted session + +Chain revoke + -> Sessions associated with the chain + +Root revoke + -> Account-level session hierarchy +``` + +These are logical revocation scopes. + +The effect on protected requests depends on whether the authentication mode consults the revoked state. + +**Revoking server-side state is not equivalent to cryptographically invalidating an already-issued JWT.** + +## Authentication Mode Enforcement + +The most important limitation of hierarchical session security is that its enforcement depends on the selected authentication mode. + +| Mode | Protected request enforcement | Revocation considerations | +|---|---|---| +| **PureOpaque** | Uses authoritative server-side session validation | Revoked session state can be rejected on subsequent validation | +| **Hybrid** | Uses server-authoritative opaque credential and session validation | Session and chain revocation can be enforced through authoritative validation | +| **SemiHybrid** | JWT access tokens are validated locally for protected requests | Server-side revocation may not invalidate an already-issued JWT until expiration | +| **PureJwt** | JWT access tokens are primarily validated without a server-side session lookup | Immediate session-hierarchy revocation must not be assumed | + +### Server-Authoritative Modes + +PureOpaque and Hybrid can evaluate protected requests against current server-managed session state. + +This allows applicable expiration, revocation, and security version conditions to affect subsequent authentication decisions. + +### Locally Validated JWT Modes + +SemiHybrid and PureJwt can accept a valid JWT without consulting current session hierarchy state on every protected request. + +Consequently, changes to server-side session security may not immediately affect an already-issued access token. + +The duration of this exposure depends on token lifetime and any additional validation or revocation mechanism configured by the application. + +### Security Tradeoff + +This is a deliberate architectural distinction. + +Server-authoritative validation provides stronger immediate control over active authentication state at the cost of server-side validation work. + +Locally validated JWTs reduce that dependency but cannot inherently provide the same immediate revocation behavior. + +See [Authentication Modes](/docs/fundamentals/auth-modes). + +## Threat Scenarios + +The session security model helps address several common security scenarios. + +### A Session Credential Is Stolen + +An attacker obtains authentication material associated with one session. + +If the compromise is detected, the affected session can be revoked. + +In server-authoritative modes, subsequent attempts to use that session can be rejected through session validation. + +If the attacker possesses an independently valid JWT, the result depends on JWT validation and revocation behavior. + +### A Client Environment Is Compromised + +An attacker may have access to authentication material associated with a particular browser or application installation. + +Chain-level revocation provides a broader response than revoking only one session. + +It can invalidate related session state while preserving unrelated chains where appropriate. + +### An Account Is Compromised + +An account-level security event may require invalidating authentication state across multiple devices. + +Root-level invalidation provides a common security boundary for the applicable session hierarchy. + +The operation must still account for the configured authentication mode and credential-related revocation policy. + +### A Tenant Relationship Is Inconsistent + +A session, chain, or root references an unexpected tenant or account relationship. + +A session-authoritative validation process must not accept the credential solely because the individual records exist. + +The relationships must satisfy the expected security context. + +### A Device Identifier Is Spoofed + +An attacker presents a device identifier matching a known client. + +The identifier match alone does not establish device ownership. + +The request must still satisfy the authentication and applicable session validation requirements. + +These scenarios illustrate how different controls address different classes of risk. + +No single session metadata field or policy provides complete protection against every form of credential compromise. + +## Security Design Principles + +The session security model follows several important principles. + +**Validate relationships, not just identifiers.** Session, chain, root, account, and tenant associations must remain consistent. + +**Separate authentication credentials from authoritative state.** A credential can reference security state without being equivalent to that state. + +**Apply revocation at the appropriate scope.** Session, chain, and root invalidation serve different security purposes. + +**Do not equate device recognition with device proof.** Client metadata provides context but not necessarily strong device authentication. + +**Respect authentication mode boundaries.** A security control is only effective where its underlying state is actually checked. + +**Keep lifecycle policy explicit.** Credential changes, recovery, expiration, and administrative actions may require different invalidation behavior. + +## Architectural Perspective + +UltimateAuth's hierarchical session model provides multiple security boundaries instead of relying on a single token validity check. + +The root represents account-level security authority, the chain organizes related session continuity, and the session represents individual server-managed authentication state. + +Together, they support targeted revocation, security versioning, relationship validation, and device-aware session control. + +Their practical security guarantees depend on the authentication mode and the validation mechanisms enforced during protected operations. + +**A secure session is not merely one that has not expired. It is one whose applicable security state, ownership relationships, and validation requirements still hold.** + +## Next Steps + +- [Session Model & Lifecycle](/docs/sessions-devices/session-lifecycle) β€” Understand the underlying hierarchy and lifecycle. +- [Device Management](/docs/sessions-devices/device-management) β€” Explore device context and chain association. +- [Authentication Modes](/docs/fundamentals/auth-modes) β€” Compare enforcement across authentication modes. +- [Credentials](/docs/plugin-domains/credentials) β€” Understand credential changes and session revocation policies. \ No newline at end of file diff --git a/docs/content/security/token-security.md b/docs/content/security/token-security.md new file mode 100644 index 00000000..a8848122 --- /dev/null +++ b/docs/content/security/token-security.md @@ -0,0 +1,560 @@ +--- +title: Token Security +order: 2 +group: security +--- + +# Token Security + +Authentication tokens allow clients to present authentication state to protected applications and services. + +However, not all tokens provide the same security guarantees. + +An opaque credential requires authoritative server-side validation. A JSON Web Token (JWT) can be verified locally using its signature and claims. A refresh credential enables authentication to continue without repeating the original login process. + +UltimateAuth supports these different mechanisms through its authentication modes. + +**Token security depends on the credential's purpose, how it is validated, how long it remains valid, and which security state is consulted during validation.** + +This page explains the security characteristics of access and refresh credentials, including issuance, expiration, rotation, replay risks, and revocation boundaries. + +For the overall authentication architecture, see [Authentication Modes](/docs/fundamentals/auth-modes). + +## Authentication State and Credentials + +A token is authentication material presented by a client. + +It is not the user identity itself. + +Depending on the authentication mode, the token may reference server-managed authentication state or contain signed claims that can be validated without retrieving a session. + +These approaches establish different trust boundaries. + +### Server-Authoritative Authentication + +In server-authoritative modes, a presented opaque credential is validated against server-managed authentication state. + +The server can evaluate the current validity of the corresponding session and applicable security conditions. + +This allows revocation and other authoritative state changes to affect subsequent protected requests. + +### Locally Validated Authentication + +In JWT-based modes, a protected resource can validate a token using its signature, issuer, audience, expiration, and other applicable token requirements. + +The resource may not need to consult the issuing server or a session store for every request. + +This reduces the dependency on server-side session lookups but changes how quickly revocation and authorization changes can be enforced. + +**Neither model makes the token an identity record. The token is a credential whose validity is determined by the selected authentication mechanism.** + +## Token Types + +UltimateAuth uses two principal access credential formats: opaque credentials and JWTs. + +Refresh credentials serve a separate purpose. + +| Credential | Purpose | Validation model | +|---|---|---| +| **Opaque access credential** | Authenticate protected requests | Authoritative server-side validation | +| **JWT access token** | Authenticate protected requests | Cryptographic and claims validation | +| **Opaque refresh credential** | Continue authentication and obtain renewed credentials | Refresh-specific validation and state checks | + +The distinction between access and refresh credentials is important. + +Access credentials are presented to protected resources. + +Refresh credentials are presented to the authentication system for a continuation operation. They should not be accepted as ordinary access credentials. + +## Opaque Access Credentials + +An opaque credential is a value whose contents do not expose the authenticated identity or authorization claims to the client. + +Conceptually: + +```text +Access credential: +q8Vn2...random-opaque-value...M7x +``` + +The client presents the credential, but cannot determine its authorization meaning by decoding it. + +The server resolves and validates the associated authentication state. + +### Security Properties + +Opaque access credentials provide several useful characteristics: + +- Their meaning is determined by authoritative server-side state. +- They do not need to expose identity claims in a client-readable payload. +- Their validity can be affected by session expiration or revocation. +- They can participate in account-level and chain-level security enforcement. + +However, opaque credentials are still bearer credentials unless an additional proof mechanism is enforced. + +An attacker who obtains a usable opaque credential may be able to present it. + +Opacity alone does not prevent credential theft or replay. + +### Server-Side Validation + +In a session-authoritative mode, validation can enforce the applicable session hierarchy requirements, including expiration, revocation, ownership, and security state consistency. + +See [Session Security Model](/docs/security/session-security). + +## JWT Access Tokens + +A JWT is a structured token containing signed claims. + +A typical JWT contains three encoded sections: + +```text +header.payload.signature +``` + +Its payload may describe the authenticated subject, tenant, authorization information, token lifetime, and other relevant context. + +A JWT is not encrypted merely because it is encoded and signed. + +Applications should not place secrets or unnecessarily sensitive information in a JWT payload. + +### Cryptographic Validation + +A protected resource must validate the JWT according to the configured token requirements. + +Relevant checks include: + +- Signature validity. +- Accepted signing algorithm and key. +- Expected issuer. +- Intended audience. +- Expiration and other applicable time constraints. +- Required token claims and application-specific restrictions. + +A successfully decoded JWT is not necessarily a valid JWT. + +Likewise, a correctly signed JWT is not automatically acceptable to every resource. + +The resource must verify that the token was issued for its intended security context. + +### Local Validation + +JWTs can be validated without loading current server-side session state. + +This is useful for distributed APIs and services that need to authenticate requests efficiently. + +However, local validation introduces an important limitation: + +**A token can remain cryptographically valid after the underlying account or authorization state has changed.** + +Whether such changes immediately affect access depends on additional validation and revocation mechanisms. + +## Authentication Mode Behavior + +UltimateAuth selects credential behavior according to the configured authentication mode. + +| Mode | Access mechanism | Continuation mechanism | Protected request authority | +|---|---|---|---| +| **PureOpaque** | Opaque session credential | Session lifecycle | Server-managed session state | +| **Hybrid** | Opaque access credential | Opaque refresh credential | Server-managed session state | +| **SemiHybrid** | JWT access token | Server-managed refresh flow | Locally validated JWT | +| **PureJwt** | JWT access token | Optional refresh mechanism | Locally validated JWT | + +### PureOpaque + +PureOpaque uses an opaque session credential. + +It does not require a separate access-token and refresh-token pair. + +The credential is validated through the authoritative session mechanism. + +Session lifecycle operations govern the continuation and validity of authentication state. + +### Hybrid + +Hybrid uses distinct opaque access and refresh credentials. + +Access requests are validated against authoritative server-managed state. + +Refresh operations use the refresh credential to continue authentication according to the configured lifecycle and rotation behavior. + +**Hybrid does not require JWT access tokens.** + +### SemiHybrid + +SemiHybrid issues JWT access tokens while maintaining server-side session state for relevant lifecycle operations. + +Protected requests can validate JWT access tokens locally. + +Refresh and logout interact with the server-managed authentication lifecycle. + +This means that server-side revocation does not necessarily invalidate an already-issued JWT immediately. + +### PureJwt + +PureJwt uses JWT access tokens without requiring the Root β†’ Chain β†’ Session hierarchy for ordinary protected request validation. + +An optional refresh mechanism may maintain refresh-specific state. + +The existence of a refresh store does not make JWT access validation session-authoritative. + +For more detail, see [Authentication Modes](/docs/fundamentals/auth-modes). + +## Access Token Lifetime + +Access credentials should have lifetimes appropriate to their authentication mode and security requirements. + +Short-lived JWT access tokens are particularly important because a token may remain usable until expiration even after server-side authorization state changes. + +For example, an application might choose a JWT access lifetime of several minutes. + +However, **a lifetime such as 5–15 minutes is an example of an application security choice, not a universal UltimateAuth default or requirement**. + +The appropriate value depends on factors such as: + +- Sensitivity of protected resources. +- Expected frequency of authorization changes. +- Acceptable revocation delay. +- Availability and security of the refresh mechanism. +- Client behavior and network conditions. + +### Lifetime Versus Revocation + +Expiration limits how long a credential can be accepted. + +Revocation invalidates authentication state before its ordinary expiration. + +These mechanisms are related but not interchangeable. + +In server-authoritative modes, current revocation state can be checked during protected requests. + +In locally validated JWT modes, expiration may remain the primary limit on an already-issued access token unless an additional revocation check is implemented. + +## Refresh Credentials + +Refresh credentials allow clients to continue authentication without repeating the original authentication ceremony. + +They are intended for the authentication system's refresh operation, not for general API access. + +In UltimateAuth, refresh behavior is mode-dependent. + +PureOpaque does not require a separate refresh credential pair, while Hybrid and SemiHybrid use refresh flows. PureJwt can support an optional refresh mechanism. + +### Refresh Security Requirements + +A secure refresh operation must establish that the presented credential is eligible for continued authentication. + +Depending on the mode and configured strategy, this may involve checking: + +- Credential existence and validity. +- Expiration. +- Revocation or consumption state. +- Associated user or session context. +- Applicable client and device context. +- Rotation and reuse conditions. + +A valid refresh credential should not automatically bypass account-level or session-level security requirements that apply to the selected mode. + +### Access and Refresh Separation + +Access and refresh credentials have different responsibilities. + +```text +Authentication + | + v +Access credential + | + | Used for protected requests + v +Access expires + | + v +Refresh operation + | + | Validate refresh credential + | Apply continuation rules + v +Renewed access credential +``` + +The refresh operation does not extend the expiration timestamp of an already-issued JWT. + +It issues new authentication material when continuation is permitted. + +## Refresh Rotation + +Refresh rotation replaces refresh authentication material according to the configured refresh strategy. + +A rotating refresh mechanism can invalidate the previously used refresh credential and issue a replacement. + +Conceptually: + +```text +Refresh credential A + | + | Successful refresh + v +Refresh credential A consumed +Refresh credential B issued +``` + +This reduces the period during which an older refresh credential remains reusable. + +However, **rotation behavior is not identical across every UltimateAuth mode or refresh strategy**. + +Some session continuation operations may update existing server-side state without replacing a separate refresh credential. + +The effective behavior must follow the selected mode and its configured refresh strategy. + +### Rotation Is Not Replay Prevention by Itself + +Rotation limits credential reuse only when the server enforces the relevant consumption or replacement state. + +A secure rotating refresh mechanism must account for situations such as: + +- The same credential being submitted again. +- Concurrent refresh requests. +- A credential being reused after replacement. +- An interrupted response after a successful rotation. +- An attacker and legitimate client attempting to use the same credential. + +The exact outcome depends on the implemented refresh strategy and concurrency guarantees. + +Rotation should not be described as automatically eliminating every replay risk. + +## Refresh Token Reuse + +Refresh token reuse occurs when a credential is presented after it has already been consumed, replaced, or otherwise invalidated. + +For a single-use rotating refresh credential, this is a security-relevant event. + +Conceptually: + +```text +Client refreshes with A + | + v +A replaced by B + | + v +Another request presents A + | + v +Reuse / invalid credential condition +``` + +A reuse attempt may indicate credential theft, but it can also arise from concurrent requests or client synchronization failures. + +The refresh implementation must distinguish these situations according to its supported guarantees and policy. + +### Security Response + +Possible security responses include: + +- Rejecting the invalid refresh attempt. +- Invalidating the associated refresh state. +- Revoking an affected session. +- Revoking a broader session chain where supported and appropriate. + +These are distinct actions. + +The presence of a reuse-detection mechanism does not imply that every reuse attempt automatically revokes the entire device-associated chain. + +Such behavior must be explicitly supported by the configured refresh strategy. + +## Refresh Credential Storage + +Refresh credentials are generally more sensitive to long-term compromise because they may allow new access credentials to be obtained. + +Their storage therefore deserves stronger protection than ordinary client-visible access metadata. + +### Server-Side Storage + +For persisted opaque refresh credentials, storing a one-way verifier or hash instead of the raw credential can reduce the impact of a storage disclosure. + +However, the exact persistence model depends on the selected authentication mode and refresh implementation. + +It should not be assumed that every authentication mode uses the same refresh storage structure. + +### Client-Side Storage + +The appropriate client storage mechanism depends on the application environment. + +For browser applications, HttpOnly cookies can prevent JavaScript from directly reading refresh credentials, although cookie-based refresh flows still require appropriate CSRF and origin protections. + +Native applications may use platform-provided secure storage where suitable. + +Refresh credentials should not be exposed through application logs, diagnostic output, URLs, or unrelated API responses. + +### Transport Security + +Authentication credentials must be protected in transit. + +Production authentication and refresh endpoints should use HTTPS. + +Applications should also avoid sending refresh credentials to services that do not need to receive them. + +## Claims and Authorization Freshness + +JWT claims represent information established when the token was issued. + +They may include standard or application-specific information such as: + +```text +sub Subject identifier +iss Issuer +aud Audience +exp Expiration +iat Issued-at time +jti Token identifier +``` + +Depending on the application, a JWT may also include tenant information, roles, permissions, or session references. + +These claims are not automatically updated when the underlying account changes. + +### Example: Permission Removal + +Consider the following sequence: + +1. A JWT is issued containing an administrative permission. +2. An administrator removes that permission from the user. +3. The client continues presenting the previously issued JWT. + +If the protected resource relies only on the signed claims in that JWT, the removed permission may remain effective until the token expires. + +The signature remains valid because the token has not been modified. + +### Authorization Strategies + +Applications can address this limitation through their security architecture, including: + +- Shorter JWT lifetimes. +- Reissuing claims during refresh. +- Consulting authoritative authorization state for sensitive operations. +- Using server-authoritative authentication modes where immediate state enforcement is required. + +The appropriate approach depends on the security requirements of the application. + +See [Authorization](/docs/plugin-domains/authorization) and [Policies & Access Control](/docs/plugin-domains/policies). + +## Token Revocation + +Revocation behavior differs fundamentally between opaque credentials and locally validated JWTs. + +### Opaque Credential Revocation + +An opaque credential depends on authoritative validation. + +If the corresponding server-managed authentication state is revoked, subsequent validation can reject the credential. + +The precise scope depends on whether the security operation targets a session, chain, or account-level root. + +### JWT Revocation + +A signed JWT cannot be made cryptographically invalid merely by deleting a server-side session record. + +Its signature and expiration remain unchanged. + +To reject an otherwise valid JWT before expiration, the resource must enforce an additional condition, such as an authoritative revocation check or another supported invalidation mechanism. + +Without that additional enforcement, a locally validated JWT may remain acceptable until expiration. + +### Refresh Revocation + +Revoking a refresh credential can prevent further successful refresh operations. + +However, that does not necessarily invalidate access tokens already issued before the revocation. + +**Access revocation and refresh revocation are separate security concerns.** + +## Session Security and Token Security + +Token security and session security are closely related, but they operate at different levels. + +| Concern | Token Security | Session Security | +|---|---|---| +| Credential format | Opaque or JWT | Root, chain, and session state | +| Cryptographic validation | JWT signature and claims | Not a substitute for token signature validation | +| Expiration | Token validity period | Session and chain lifecycle limits | +| Rotation | Replacement of authentication material | Continuity and associated state transitions | +| Revocation | Whether a presented credential remains acceptable | Which authentication state has been invalidated | +| Device context | May be associated with token use | Managed through applicable chain and session context | + +A token may be valid according to its own format but unacceptable under authoritative session security rules. + +Conversely, server-side session state may remain active while a particular access token has expired. + +Both levels must be understood to reason correctly about the selected authentication mode. + +## Common Security Misconceptions + +### A Valid Signature Means Current Authorization + +A valid JWT signature proves that the token has not been modified and was signed by an accepted key. + +It does not prove that the user's permissions or account status have remained unchanged since issuance. + +### A Device Identifier Prevents Token Theft + +A device identifier may support context consistency checks. + +It does not independently prevent a stolen bearer credential from being replayed. + +### Refresh Rotation Makes Token Theft Impossible + +Rotation can reduce reuse opportunities and support detection of invalid reuse. + +It does not prevent a stolen credential from being used before the legitimate client or server detects the compromise. + +### Logout Invalidates Every JWT Immediately + +Logout may revoke server-managed session or refresh state. + +Previously issued JWT access tokens can remain valid until expiration unless protected resources enforce an additional invalidation mechanism. + +### Short-Lived Tokens Eliminate the Need for Session Security + +Short lifetimes reduce the exposure window of a compromised token. + +They do not replace account-level security policies, credential recovery protections, or session revocation controls where those capabilities are required. + +## Security Design Principles + +UltimateAuth's token model is best understood through a few principles. + +**Use credentials only for their intended purpose.** Access and refresh credentials must not be interchangeable. + +**Validate according to the selected mode.** Opaque credentials require authoritative validation; JWTs require correct cryptographic and claims validation. + +**Treat JWT claims as issuance-time information.** Current authorization state may differ from what an older token contains. + +**Keep access credentials appropriately short-lived.** Token lifetime should reflect the application's acceptable exposure window. + +**Enforce refresh continuation rules.** Rotation, expiration, consumption, and reuse behavior must be consistent with the configured strategy. + +**Distinguish token expiration from session revocation.** They invalidate authentication through different mechanisms. + +**Do not promise immediate JWT revocation without enforcement.** Server-side state changes affect locally validated tokens only when the resource checks the relevant state. + +## Architectural Perspective + +UltimateAuth does not impose a single token model on every application. + +PureOpaque and Hybrid emphasize server-authoritative authentication state. + +SemiHybrid and PureJwt support locally validated JWT access, with different approaches to authentication continuation. + +These choices affect performance, storage requirements, revocation behavior, and authorization freshness. + +The purpose of the token security model is to make those tradeoffs explicit. + +**A token establishes access only while its applicable validation requirements remain satisfied. The strength and freshness of those requirements depend on the authentication mode.** + +## Next Steps + +- [Session Security Model](/docs/security/session-security) β€” Understand authoritative session validation and hierarchical revocation. +- [Authentication Modes](/docs/fundamentals/auth-modes) β€” Compare credential behavior across modes. +- [Session Model & Lifecycle](/docs/sessions-devices/session-lifecycle) β€” Explore session creation, continuation, and invalidation. +- [Credentials](/docs/plugin-domains/credentials) β€” Understand credential management and recovery security. \ No newline at end of file diff --git a/docs/content/sessions-devices/device-management.md b/docs/content/sessions-devices/device-management.md new file mode 100644 index 00000000..f134bb05 --- /dev/null +++ b/docs/content/sessions-devices/device-management.md @@ -0,0 +1,308 @@ +--- +title: Device Management +order: 2 +group: sessions-devices +--- + +# Device Management + +Authentication does not happen in isolation from the device or client environment in which it takes place. + +A user may authenticate from a desktop browser, a mobile application, or several different environments simultaneously. Each environment can have its own authentication state, activity history, and security requirements. + +UltimateAuth supports **device-aware session management** by associating authentication sessions with device context and organizing related sessions through `SessionChain`. + +This provides a foundation for device-level visibility, session continuity, and targeted security operations without treating device identity as a substitute for user authentication. + +## Understanding Device-Aware Authentication + +Traditional token-based authentication can be implemented without explicitly modeling the client environment. + +A token is issued, validated, and eventually expires or is revoked. + +That approach is sufficient for some applications, but it becomes limiting when an application needs to distinguish between different client environments belonging to the same user. + +For example: + +- A user signs in from both a desktop browser and a mobile application. +- An administrator needs to revoke sessions associated with one client environment. +- A session is refreshed repeatedly while remaining associated with the same device context. +- An application needs to apply session limits or track activity across client environments. + +UltimateAuth separates these concerns through its session and chain model. + +The objective is not to prove the physical identity of a device. It is to provide a structured way to associate authentication activity with a recognizable client context. + +## Device Context and SessionChain + +The central concept in UltimateAuth's device-aware model is `SessionChain`. + +A session represents authentication state. + +A `SessionChain` groups related session activity under a persistent chain identity, which can be associated with a device or client environment. + +Conceptually: + +```text +User Identity + | + +-- SessionChain (Desktop Browser) + | | + | +-- Session + | +-- Session / Rotation History + | + +-- SessionChain (Mobile Application) + | + +-- Session + +-- Session / Rotation History +``` + +This diagram illustrates the relationship between identity, chain, and session. It does not imply that every authentication mode retains multiple simultaneous session records or stores a complete rotation history. + +### A Chain Is Not a Physical Device + +A `SessionChain` should not be understood as a physical hardware identifier. + +It represents a logical continuity boundary for related authentication sessions. + +A single physical device may use different browsers, application installations, or isolated client storage environments. + +Conversely, device identifiers supplied by clients are not necessarily reliable proof that requests originate from the same physical device. + +The practical relationship is: + +**A device context can be associated with a SessionChain, but the chain itself is an authentication-session concept.** + +This distinction is important when designing security rules around device recognition and session revocation. + +## Device Information + +Device-aware session management can use information describing the client environment. + +Relevant information may include: + +| Information | Purpose | +|---|---| +| `DeviceId` | Identifies a client-reported or application-managed device context | +| Platform | Distinguishes environments such as web and mobile | +| Operating system | Provides client environment metadata | +| Browser | Describes the browser environment when applicable | +| IP address | Provides network context for security and activity analysis | + +The availability and reliability of this information depend on the client platform and the application's integration. + +Not every client can provide the same metadata. + +### DeviceId and Trust + +A `DeviceId` is useful for identifying a client environment across requests or authentication operations. + +However, an identifier alone is not proof of device ownership. + +A value stored in browser storage, for example, may be copied, cleared, or replaced. An identifier generated by a mobile application may change after application data is removed. + +For that reason, device recognition must not be confused with authentication. + +UltimateAuth's authentication and session validation rules remain responsible for determining whether a request is authorized to use the associated authentication state. + +### Device Fingerprinting + +Platform, operating system, browser, and network information may help describe a client environment. + +These attributes are not inherently unique or stable. + +Multiple devices can share similar attributes, and a single device can present different attributes over time. + +They should therefore be treated as contextual signals rather than a guaranteed device fingerprint. + +Applications requiring stronger device assurance need an appropriate proof or binding mechanism in addition to ordinary metadata. + +## Device Continuity + +Device continuity describes how authentication activity remains associated with an existing client context. + +A session chain provides the logical structure for maintaining that continuity. + +### First Authentication + +When an authentication flow establishes a new session, the session infrastructure determines the appropriate chain association. + +If a new chain is required, the session is associated with that chain. + +The resulting chain can represent the beginning of a device-associated authentication history. + +### Subsequent Authentication + +A later authentication from the same client environment may be associated with an existing chain when the configured matching and security conditions permit it. + +However, recognizing a device identifier does not automatically require the system to reuse a previous chain. + +Chain association must respect the applicable lifecycle, validity, and security rules. + +For example, a revoked chain should not become trusted again merely because a client presents the same device identifier. + +### Device Continuity Is Not Session Continuity + +A client environment can remain recognizable even when its previous authentication session has expired. + +Likewise, a new authentication session can be established without inheriting the validity of an earlier session. + +The chain model allows related activity to be organized while keeping session validity subject to authentication and security rules. + +## Activity Tracking + +Device-associated session management can maintain information about authentication activity. + +Examples include: + +- When a chain was created. +- When related authentication activity was last observed. +- How often relevant session activity was recorded. +- How many rotation operations were associated with the chain. + +Fields such as `LastSeenAt`, `TouchCount`, and `RotationCount` help describe these aspects of the session lifecycle. + +### Touch Operations + +A touch operation records qualifying session activity according to the configured session behavior. + +Depending on the authentication mode and session strategy, this may update activity metadata such as the last-seen timestamp or touch counter. + +Touch behavior should not be interpreted as a guarantee that every authenticated HTTP request updates persistent device activity. + +Different authentication modes have different requirements for consulting and updating server-side session state. + +### Rotation Tracking + +Token or session rotation may replace authentication material while preserving the logical relationship with an existing chain. + +Where rotation tracking is supported, the chain can maintain information about those transitions. + +This makes it possible to distinguish the continuity of a client environment from the lifecycle of an individual credential or session. + +Rotation counts are operational signals. They do not independently establish that a device is legitimate or that suspicious activity has occurred. + +## Logout and Device Revocation + +Logout and chain revocation address different security scopes. + +### Logout + +Logout terminates the authentication state targeted by the logout operation. + +Its precise effect depends on the authentication mode, the credentials involved, and the configured session behavior. + +A logout operation does not necessarily mean that every session associated with the same device context has been revoked. + +It also does not necessarily require deleting the associated chain record. + +### Chain Revocation + +Chain revocation targets the logical group of sessions associated with a chain. + +It provides a broader security operation than terminating a single session. + +This is useful when an application needs to invalidate the authentication continuity associated with a particular client environment. + +For example, if a user believes a mobile application session has been compromised, the application may revoke the corresponding chain rather than revoking unrelated sessions associated with other environments. + +### Revocation Does Not Establish Device Trust + +A retained chain record is not equivalent to a trusted device. + +Likewise, removing or revoking a chain does not physically block a device from attempting to authenticate again. + +Whether a new authentication attempt is permitted depends on the authentication flow and the applicable security policies. + +The chain model provides a boundary for managing authentication state; it is not a permanent device allowlist or hardware security mechanism. + +## Device Binding and Security Boundaries + +Device-aware authentication can associate session state with device context. + +However, the strength of that association depends on what is actually validated. + +There is an important distinction between: + +- **Device metadata:** Information describing a client environment. +- **Device identification:** An identifier used to recognize a client context. +- **Device binding:** A security requirement connecting authentication material to a validated device-related condition. +- **Device authentication:** Proof that a client possesses an appropriate device-bound secret or cryptographic key. + +These concepts provide different levels of assurance. + +An application must not assume that supplying a `DeviceId` alone prevents stolen credentials from being reused elsewhere. + +Security guarantees depend on the binding and validation mechanisms enforced by the configured authentication infrastructure. + +## Device Management Across Authentication Modes + +UltimateAuth supports different authentication modes, and device-aware session behavior must be understood within those modes. + +| Mode | Device and session considerations | +|---|---| +| **PureOpaque** | Server-side session state is authoritative for protected requests, allowing session validity to be evaluated against server-managed state. | +| **Hybrid** | Opaque access and refresh credentials are associated with server-authoritative session state. | +| **SemiHybrid** | Server-side session state participates in lifecycle operations, while issued JWT access tokens may remain valid until expiration. | +| **PureJwt** | Protected requests are primarily validated from JWT contents; server-side device and session enforcement depends on the configured supporting infrastructure. | + +The presence of a device-associated chain does not imply identical enforcement behavior in every mode. + +### Revocation and JWT Validity + +In modes that validate protected requests against authoritative server-side session state, session revocation can affect subsequent requests through that validation. + +In JWT-based modes, a previously issued access token may remain valid until expiration if protected requests do not consult current revocation state. + +Therefore, revoking a device-associated chain must not be described as universally invalidating every previously issued access token immediately. + +See [Authentication Modes](/docs/fundamentals/auth-modes). + +## Session and Device Limits + +Applications may need to limit how many authentication sessions or client environments a user can maintain. + +UltimateAuth's session configuration provides the architectural place for managing supported session and chain constraints. + +Depending on the configured capabilities, these constraints may concern: + +- The number of chains associated with a user. +- The number of sessions associated with a chain. +- Session expiration and activity behavior. +- The treatment of device context changes. +- Revocation and lifecycle rules. + +The exact available options and enforcement behavior depend on the authentication mode and session configuration. + +Device management should not be understood as a separate enforcement system operating independently of session management. + +## Operational Visibility + +Device-associated session information can help applications present useful security information to users or administrators. + +For example, an account management interface may display recognizable client environments and provide an option to revoke related sessions. + +Such interfaces should distinguish between recorded information and verified facts. + +A browser name or IP address may help a user recognize an activity record, but it does not conclusively identify the person or physical device involved. + +Likewise, activity counters can support monitoring without independently providing anomaly detection. + +UltimateAuth provides session and chain concepts that applications can use for these experiences, while the application's security and user-interface requirements determine how the information should be presented. + +## Architectural Perspective + +Device management in UltimateAuth is built on the relationship between identity, session, and chain. + +**The user identity identifies the account. The session represents authentication state. The SessionChain organizes related session continuity, potentially associated with a device context.** + +This model enables targeted session management and device-aware security operations without making device metadata an independent source of authentication trust. + +It also preserves an important architectural boundary: device recognition, session validity, and authorization remain distinct security concerns. + +## Next Steps + +- [Session Model](/docs/sessions-devices/session-lifecycle) β€” Understand sessions, chains, and their lifecycle. +- [Session Security](/docs/security/session-security) β€” Explore expiration, rotation, touch, and revocation. +- [Authentication Modes](/docs/fundamentals/auth-modes) β€” Compare session validation across authentication modes. +- [Request Lifecycle](/docs/fundamentals/request-lifecycle) β€” Understand how protected requests are authenticated and authorized. \ No newline at end of file diff --git a/docs/content/sessions-devices/session-lifecycle.md b/docs/content/sessions-devices/session-lifecycle.md new file mode 100644 index 00000000..24a7b465 --- /dev/null +++ b/docs/content/sessions-devices/session-lifecycle.md @@ -0,0 +1,424 @@ +--- +title: Session Model & Lifecycle +order: 1 +group: sessions-devices +--- + +# Session Model & Lifecycle + +Authentication is more than issuing a credential and checking its expiration. + +In stateful authentication systems, successful authentication establishes security state that must be managed throughout its lifetime. Sessions may be refreshed, extended, revoked, or invalidated in response to changes in account security. + +UltimateAuth models this state through a structured hierarchy: + +**Root β†’ Chain β†’ Session** + +This hierarchy separates account-level authentication security, device-associated session continuity, and individual authentication sessions. + +The model provides the foundation for session management, security versioning, targeted revocation, and lifecycle enforcement. + +Its runtime behavior depends on the configured authentication mode. + +## Understanding the Session Hierarchy + +UltimateAuth organizes server-managed authentication state into three levels. + +| Level | Represents | Primary responsibility | +|---|---|---| +| **SessionRoot** | Account-level authentication security | Global session state and security versioning | +| **SessionChain** | Related session continuity | Device association, activity, and chain-level control | +| **Session** | Individual authentication state | Session validity, expiration, and revocation | + +Conceptually: + +```text +User Identity + | + └── SessionRoot + | + β”œβ”€β”€ SessionChain (Desktop Browser) + | | + | β”œβ”€β”€ Session + | └── Session + | + └── SessionChain (Mobile Application) + | + └── Session +``` + +This is a logical representation of the hierarchy. It does not imply that every authentication mode stores the same records or maintains multiple simultaneous sessions within each chain. + +Each level addresses a different security concern. + +## SessionRoot: Account-Level Security + +`SessionRoot` represents the account-level security boundary for server-managed authentication sessions. + +It associates the authentication session hierarchy with a user identity and maintains security information relevant across that identity's sessions. + +The root is not the user account itself. + +The Users domain may own the user's identifiers, profile, and lifecycle. The session root belongs to authentication security management. + +### Root Responsibilities + +A session root provides a common boundary for: + +- Associating session chains with an identity. +- Maintaining account-level session security state. +- Coordinating global session invalidation. +- Supporting security version checks. + +The distinction is important because changes to authentication security do not necessarily require changes to the user entity. + +For example, invalidating existing sessions should not require recreating the user or changing the user's identifiers. + +### Security Versioning + +UltimateAuth's session model supports security versioning. + +A root can maintain a security version representing the current account-level authentication security state. + +Sessions can retain the corresponding version from their creation. + +Conceptually: + +```text +Root.SecurityVersion = 4 +Session.SecurityVersionAtCreation = 4 + +Result: Version matches +``` + +After an account-level security invalidation: + +```text +Root.SecurityVersion = 5 +Session.SecurityVersionAtCreation = 4 + +Result: Version mismatch +``` + +When the applicable session validation checks detect a version mismatch, the older session is no longer valid. + +This allows the authentication system to invalidate previously established session state through a shared security boundary. + +The effect on already-issued JWT access tokens still depends on whether protected requests consult current server-side security state. + +## SessionChain: Session Continuity + +`SessionChain` represents a logical grouping of related authentication sessions. + +A chain can be associated with a particular device or client environment, allowing session activity to be managed within a recognizable continuity boundary. + +For example, a user may have separate chains for a desktop browser and a mobile application. + +However, a chain is not a physical device identity or a guarantee that the device has been cryptographically verified. + +### Chain Responsibilities + +A session chain can provide a boundary for: + +- Associating related sessions. +- Maintaining device or client context. +- Recording relevant activity. +- Tracking session rotation. +- Applying chain-level security operations. +- Revoking related sessions together. + +This allows UltimateAuth to distinguish between the lifecycle of an individual session and the continuity of the environment associated with it. + +### Device Association + +A chain may include device-related information such as a device identifier, platform, operating system, or browser. + +These attributes help describe the client environment. + +They must not be treated as independent proof of device ownership. + +See [Device Management](/docs/sessions-devices/device-management) for the device association model and its security boundaries. + +## Session: Individual Authentication State + +A `Session` represents an individual unit of server-managed authentication state. + +It is associated with a chain and participates in the validity rules established by the authentication mode and session configuration. + +A session may contain information relevant to: + +- Its associated identity and chain. +- Creation and expiration. +- Revocation status. +- Security version at creation. +- Authentication lifecycle and validity. + +A session is not necessarily equivalent to an access token. + +In opaque authentication modes, a client credential may reference server-managed session state. + +In JWT-based modes, an issued access token may be validated locally without consulting the session hierarchy on every protected request. + +**The token is authentication material. The session is a security-state concept.** + +Keeping these concepts separate is essential for understanding UltimateAuth's authentication modes. + +## Session Creation + +A session lifecycle begins when an authentication flow successfully establishes the required authentication state. + +For modes using the server-managed session hierarchy, the process conceptually involves: + +1. Resolving the authenticated user identity. +2. Resolving or establishing the corresponding session root. +3. Determining the appropriate session chain. +4. Creating or updating the required session state. +5. Issuing the authentication material required by the selected mode. + +The authentication flow must satisfy its verification and security requirements before establishing a valid authenticated session. + +### Root Resolution + +An existing account-level session root can be reused when appropriate. + +If the required root does not exist, the session infrastructure can establish it as part of session creation. + +### Chain Resolution + +The session infrastructure determines whether authentication should be associated with an existing chain or a new chain. + +A previously observed device identifier does not automatically establish that an existing chain is valid or eligible for reuse. + +The decision must respect the applicable chain lifecycle and security conditions. + +### Session Establishment + +Once the appropriate security context has been established, the authentication system creates or updates the session state required by the authentication mode. + +The resulting client credential is then issued according to that mode. + +## Active Session Usage + +After authentication, the client presents the appropriate authentication material when accessing protected resources. + +What happens next depends on the configured mode. + +In session-authoritative modes, protected requests can be evaluated against current server-managed session state. + +In locally validated JWT modes, protected requests may be authenticated using the token's cryptographic validity and claims without loading the current session record. + +This distinction affects session activity tracking, expiration enforcement, and revocation behavior. + +### Touch Operations + +A touch operation records qualifying session activity. + +Depending on the session strategy, it may update information such as: + +- `LastSeenAt` +- `TouchCount` +- Relevant session activity or expiration state + +Touch behavior is not necessarily performed for every HTTP request. + +Applications should not assume that an authenticated request always results in a persistent session update. + +### Sliding Expiration + +Sliding expiration allows eligible authentication state to remain valid as qualifying activity occurs. + +Unlike a fixed expiration, sliding expiration can extend a session's usable lifetime according to configured rules. + +However, sliding expiration does not necessarily remove absolute lifetime restrictions. + +A session may still become invalid when a maximum permitted lifetime is reached. + +The exact behavior depends on the selected authentication mode and its session configuration. + +## Refresh and Rotation + +Refresh and rotation are related to session lifecycle but are not identical operations. + +**Refresh** is an authentication operation used to continue or renew authentication state. + +**Rotation** replaces authentication material or session-related credentials according to the configured security strategy. + +The relationship between these operations varies by authentication mode. + +### PureOpaque + +PureOpaque uses server-authoritative opaque session authentication. + +It does not require a separate access-token and refresh-token pair. + +Session continuity and activity management are handled through the applicable opaque session lifecycle. + +### Hybrid + +Hybrid uses distinct opaque access and refresh credentials backed by server-authoritative session state. + +Refresh operations can renew authentication material while preserving the appropriate session or chain relationship. + +The exact rotation behavior follows the configured refresh strategy. + +### SemiHybrid + +SemiHybrid uses JWT access tokens while maintaining server-side session state for lifecycle operations such as refresh and logout. + +Refresh may issue new JWT access material and update the relevant server-managed state. + +Already-issued access tokens may remain valid until expiration because protected requests do not necessarily consult that state. + +### PureJwt + +PureJwt primarily relies on stateless JWT validation for protected requests. + +A refresh mechanism may use an independent refresh store, but the server-managed Root β†’ Chain β†’ Session hierarchy must not be assumed to govern every protected request. + +This is an important distinction between the framework's common authentication concepts and the persistence requirements of individual modes. + +See [Authentication Modes](/docs/fundamentals/auth-modes). + +## Session Expiration + +Session expiration defines when authentication state is no longer eligible for continued use. + +Depending on the authentication mode and configuration, expiration may involve: + +| Expiration mechanism | Meaning | +|---|---| +| **Fixed lifetime** | Authentication state expires after a defined duration | +| **Idle timeout** | Authentication state expires after insufficient qualifying activity | +| **Sliding expiration** | Eligible activity extends the usable lifetime | +| **Absolute expiration** | Authentication state cannot extend beyond a maximum lifetime | + +These mechanisms are not necessarily all enabled or applicable in every authentication mode. + +### Expired Versus Revoked + +Expiration and revocation represent different reasons for invalidation. + +**Expiration** occurs because a configured time-based condition has been reached. + +**Revocation** occurs because authentication state has been explicitly invalidated through a security operation. + +Both can make a session unusable, but they represent different lifecycle events and may be recorded or enforced differently. + +## Session Revocation + +UltimateAuth's hierarchical session model supports security operations at different levels. + +The scope of revocation determines how broadly authentication state is affected. + +### Session-Level Revocation + +Session-level revocation targets an individual session. + +It is appropriate when a specific authentication instance must be invalidated without necessarily affecting other sessions associated with the same user. + +### Chain-Level Revocation + +Chain-level revocation targets the session continuity represented by a chain. + +It allows related sessions to be invalidated together. + +This can be useful when a particular client environment is suspected of compromise or when a user wants to terminate sessions associated with one device context. + +Revoking a chain does not mean that the corresponding physical device is permanently blocked from authenticating again. + +### Root-Level Revocation + +Root-level revocation affects the account-level authentication security boundary. + +It provides a mechanism for invalidating authentication state associated with the user's session hierarchy. + +Security version changes can support this operation by making previously created sessions fail subsequent version validation. + +This is useful for broad security responses, including operations that require invalidating all applicable existing sessions. + +### Revocation Scope + +Conceptually: + +```text +Session revocation + └── One session + +Chain revocation + └── Related sessions in one chain + +Root revocation + └── Account-level session hierarchy +``` + +The effective invalidation of protected requests depends on the authentication mode. + +In particular, server-side revocation does not automatically invalidate a previously issued JWT that is still cryptographically valid and is not checked against current revocation state. + +## Session Security and Credential Changes + +Session lifecycle is closely related to credential security. + +A password change or recovery operation may require existing authentication sessions to be preserved or revoked. + +UltimateAuth supports session handling policies for credential changes and resets, including: + +| Policy | Behavior | +|---|---| +| `KeepAll` | Preserve applicable existing sessions | +| `KeepCurrent` | Preserve the current session while revoking other applicable sessions | +| `RevokeAll` | Revoke all applicable sessions | + +These policies allow applications to choose the security response appropriate to the operation. + +For example, an authenticated password change and an anonymous password recovery operation may require different session handling. + +The Credentials domain initiates the relevant security behavior, while the session infrastructure applies the corresponding lifecycle operations. + +See [Credentials](/docs/plugin-domains/credentials). + +## Session Configuration + +Session behavior is configured through UltimateAuth's session options, including `UAuthSessionOptions`. + +The configuration model governs supported aspects of session lifecycle such as: + +- Session lifetimes. +- Activity and expiration behavior. +- Refresh and rotation strategies. +- Session and chain limits. +- Device-related session constraints. +- Revocation and lifecycle enforcement. + +Not every option applies to every authentication mode. + +For example, a configuration setting that controls server-side session activity may not affect the validation of a stateless JWT access token. + +Applications should configure session behavior with the selected authentication mode and its enforcement boundaries in mind. + +Detailed configuration examples belong in the configuration reference and How-to Guides. + +## Architectural Perspective + +UltimateAuth separates authentication identity, session continuity, and individual authentication state. + +The Root β†’ Chain β†’ Session hierarchy expresses those responsibilities at different security levels: + +- **Root** provides account-level authentication security. +- **Chain** organizes related session continuity. +- **Session** represents individual server-managed authentication state. + +This structure supports targeted session management without requiring all security operations to act on every session belonging to a user. + +At the same time, UltimateAuth does not assume that every authentication mode requires identical server-side state. + +The hierarchy is most directly relevant to session-managed authentication, while JWT-based modes preserve their own validation and lifecycle characteristics. + +**Authentication establishes trust. Session management determines how that trust is maintained, renewed, and invalidated over time.** + +## Next Steps + +- [Device Management](/docs/sessions-devices/device-management) β€” Device context, chain association, and device-aware session management. +- [Session Security](/docs/security/session-security) β€” Session expiration, rotation, revocation, and security controls. +- [Authentication Modes](/docs/fundamentals/auth-modes) β€” Understand how session behavior differs between modes. +- [Request Lifecycle](/docs/fundamentals/request-lifecycle) β€” Learn how protected requests validate authentication state. \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Layout/DocsLayout.razor b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Layout/DocsLayout.razor index e0cba0bf..4de87865 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Layout/DocsLayout.razor +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Layout/DocsLayout.razor @@ -1,6 +1,7 @@ ο»Ώ@using CodeBeam.UltimateAuth.Docs.Wasm.Client.Pages @inherits LayoutComponentBase @layout MainLayout +@inject NavigationManager Nav @@ -11,8 +12,15 @@ @Body - - - - + @if (!IsDocsLandingPage) + { + + + + + } + +@code { + private bool IsDocsLandingPage => Nav.ToBaseRelativePath(Nav.Uri).Trim('/').Equals("docs", StringComparison.OrdinalIgnoreCase); +} 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 943d43cf..6301b33e 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 @@ -77,6 +77,51 @@ @Body + + + + + + + + + + + + UltimateAuth + + + Open-source unified Auth for modern .NET. + + + + + Docs + Samples + GitHub + Discord + + + + + + + + + Β© @DateTime.Now.Year CodeBeam. + + + + Built for the .NET community. + + + + + + +
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 0f80a41e..9650497f 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 @@ -41,10 +41,7 @@ else if (_doc is not null) { - - Previous - - + Previous @_prev.Title @@ -58,9 +55,7 @@ else if (_doc is not null) { - - Next - + Next @_next.Title @@ -79,7 +74,6 @@ else if (_doc is not null) private PersistingComponentStateSubscription _subscription; private DocPage? _doc; - private bool _loading = true; private bool _notFound; private ElementReference _contentRef; @@ -90,16 +84,13 @@ else if (_doc is not null) protected override async Task OnParametersSetAsync() { - _loading = true; _notFound = false; _doc = null; _prev = null; _next = null; PageState.Clear(); - var slug = string.IsNullOrWhiteSpace(Slug) - ? "index" - : Slug!.Trim('/'); + var slug = string.IsNullOrWhiteSpace(Slug) ? "index" : Slug!.Trim('/'); try { @@ -132,15 +123,13 @@ else if (_doc is not null) Console.WriteLine($"[Docs SSR] {slug}: {ex.Message}"); _notFound = true; } - - _loading = false; } protected override async Task OnAfterRenderAsync(bool firstRender) { - if (!_loading && _doc != null) + if (firstRender) { - // await JS.InvokeVoidAsync("docsEnhance", _contentRef); + await JS.InvokeVoidAsync("highlightDocsCode"); } } diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsLandingPage.razor b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsLandingPage.razor index 6c71a602..62ec35e3 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsLandingPage.razor +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsLandingPage.razor @@ -57,7 +57,7 @@ auth modes, client profiles, and runtime architecture. - + Open Fundamentals @@ -74,7 +74,7 @@ token behavior, and device-aware authentication flows. - + Open Auth Flows @@ -91,7 +91,7 @@ Everything from basic login/logout to advanced session management should call with client. - + Open Client & API @@ -107,7 +107,7 @@ Explore the user, credential, and authorization modules that not required for core Auth, but essential. - + Open Plugin Domains @@ -126,9 +126,7 @@ refresh rotation, and the policy pipeline. - + Open Security Docs 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 b8d750aa..f83a73ec 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 @@ -13,7 +13,8 @@ DOCUMENTATION @* UAuth Docs *@ - + + } @@ -40,7 +41,8 @@ Overview @foreach (var group in _groups) { - + @foreach (var item in group.Value) { @item.Title @@ -53,7 +55,6 @@ @code { private Dictionary>? _groups; - private bool _expanded = false; private bool _loadFailed; private PersistingComponentStateSubscription _subscription; @@ -63,9 +64,54 @@ [Parameter] public bool Inline { get; set; } + private readonly Dictionary _expandedGroups = new(); + + private string? _activeGroup; + + private bool IsGroupExpanded(string group) + { + return _expandedGroups.TryGetValue(group, out var expanded) + ? expanded + : group == _activeGroup; + } + + private void SetGroupExpanded(string group, bool expanded) + { + _expandedGroups[group] = expanded; + StateHasChanged(); + } + + private void UpdateActiveGroup() + { + if (_groups is null) + return; + + var currentPath = Nav.ToBaseRelativePath(Nav.Uri) + .Split('?', '#')[0] + .Trim('/'); + + var activeItem = _groups + .SelectMany(g => g.Value) + .FirstOrDefault(item => + currentPath.Equals( + $"docs/{item.Slug.Trim('/')}", + StringComparison.OrdinalIgnoreCase)); + + var newGroup = activeItem?.Group; + + if (newGroup == _activeGroup) + return; + + _activeGroup = newGroup; + + if (newGroup is not null) + _expandedGroups[newGroup] = true; + } + protected override void OnInitialized() { _subscription = PersistentState.RegisterOnPersisting(PersistSidebarAsync); + Nav.LocationChanged += OnLocationChanged; } protected override async Task OnInitializedAsync() @@ -92,6 +138,7 @@ g => g.Key, g => g.OrderBy(x => x.Order).ToList() ); + UpdateActiveGroup(); } catch (HttpRequestException ex) { @@ -105,6 +152,15 @@ } } + private void OnLocationChanged(object? sender, LocationChangedEventArgs e) + { + _ = InvokeAsync(() => + { + UpdateActiveGroup(); + StateHasChanged(); + }); + } + private List? _items; private Task PersistSidebarAsync() @@ -131,12 +187,30 @@ if (string.IsNullOrWhiteSpace(group)) return "General"; - return System.Globalization.CultureInfo.CurrentCulture.TextInfo - .ToTitleCase(group.Replace("-", " ")); + if (group == "client") + return "Client SDK"; + + return System.Globalization.CultureInfo.CurrentCulture.TextInfo.ToTitleCase(group.Replace("-", " ")); + } + + private bool HasExpandedGroups => _groups is { Count: > 0 } && _groups.Keys.Any(IsGroupExpanded); + + private void ToggleAllGroups() + { + if (_groups is null) + return; + + bool expand = !HasExpandedGroups; + + foreach (var group in _groups.Keys) + { + _expandedGroups[group] = expand; + } } public void Dispose() { + Nav.LocationChanged -= OnLocationChanged; _subscription.Dispose(); } diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/Home.razor b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/Home.razor index 5be34301..e00fb76c 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/Home.razor +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/Home.razor @@ -90,6 +90,98 @@ + +
+ + + + + + + Explore UltimateAuth + + + + From documentation to working examples β€” + discover how UltimateAuth fits into your applications. + + + + + + + + + + + + Documentation + + + + Understand the architecture, authentication + flows, security model and integration patterns. + + + + Read the Docs + + + + + + + + + + + + Working Samples + + + + Explore real implementations across Blazor Server, + WebAssembly, UAuthHub and Resource APIs. + + + + Explore Samples + + + + + + + + + Explore the Source on GitHub + + + + + +