diff --git a/Directory.Build.props b/Directory.Build.props
index 758c57e5..7b23949a 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -1,13 +1,13 @@
- 0.1.0-preview.1
+ 0.1.0-preview.2
$(NoWarn);1591
CodeBeam
CodeBeam
https://github.com/CodeBeamOrg/UltimateAuth
- https://github.com/CodeBeamOrg/UltimateAuth
+ https://ultimateauth.com
Apache-2.0
true
diff --git a/UltimateAuth.slnx b/UltimateAuth.slnx
index 4d101bde..c449693f 100644
--- a/UltimateAuth.slnx
+++ b/UltimateAuth.slnx
@@ -25,6 +25,9 @@
+
+
+
diff --git a/docs/content/_groups.json b/docs/content/_groups.json
new file mode 100644
index 00000000..bae8f92e
--- /dev/null
+++ b/docs/content/_groups.json
@@ -0,0 +1,9 @@
+{
+ "getting-started": 1,
+ "fundamentals": 2,
+ "auth-flows": 3,
+ "plugin-domains": 4,
+ "client": 5,
+ "configuration": 6,
+ "security": 7
+}
\ No newline at end of file
diff --git a/docs/content/auth-flows/device-management.md b/docs/content/auth-flows/device-management.md
index 9e9305bf..a2184b8f 100644
--- a/docs/content/auth-flows/device-management.md
+++ b/docs/content/auth-flows/device-management.md
@@ -1,3 +1,10 @@
+---
+title: Device Management
+order: 7
+group: auth-flows
+---
+
+
# π± Device Management
In UltimateAuth, devices are not an afterthought.
diff --git a/docs/content/auth-flows/index.md b/docs/content/auth-flows/index.md
index 94d06100..4db4c8b3 100644
--- a/docs/content/auth-flows/index.md
+++ b/docs/content/auth-flows/index.md
@@ -1,3 +1,10 @@
+---
+title: Auth Flows
+order: 1
+group: auth-flows
+---
+
+
# π Auth Flows
Authentication in UltimateAuth is not a single operation.
diff --git a/docs/content/auth-flows/login-flow.md b/docs/content/auth-flows/login-flow.md
index c5913c3f..0df83bb1 100644
--- a/docs/content/auth-flows/login-flow.md
+++ b/docs/content/auth-flows/login-flow.md
@@ -1,3 +1,10 @@
+---
+title: Login
+order: 2
+group: auth-flows
+---
+
+
# π Login Flow
The login flow in UltimateAuth is not just credential validation.
diff --git a/docs/content/auth-flows/logout-flow.md b/docs/content/auth-flows/logout-flow.md
index f6b933f0..74d0eec7 100644
--- a/docs/content/auth-flows/logout-flow.md
+++ b/docs/content/auth-flows/logout-flow.md
@@ -1,3 +1,10 @@
+---
+title: Logout
+order: 4
+group: auth-flows
+---
+
+
# πͺ Logout Flow
The logout flow in UltimateAuth is not a single action.
diff --git a/docs/content/auth-flows/refresh-flow.md b/docs/content/auth-flows/refresh-flow.md
index 52ca0a68..9ff51683 100644
--- a/docs/content/auth-flows/refresh-flow.md
+++ b/docs/content/auth-flows/refresh-flow.md
@@ -1,3 +1,10 @@
+---
+title: Refresh
+order: 3
+group: auth-flows
+---
+
+
# π Refresh Flow
The refresh flow in UltimateAuth is not a single fixed operation.
diff --git a/docs/content/auth-flows/session-lifecycle.md b/docs/content/auth-flows/session-lifecycle.md
index f628bab8..6f79a04c 100644
--- a/docs/content/auth-flows/session-lifecycle.md
+++ b/docs/content/auth-flows/session-lifecycle.md
@@ -1,3 +1,10 @@
+---
+title: Session Lifecycle
+order: 5
+group: auth-flows
+---
+
+
# 𧬠Session Lifecycle
UltimateAuth is built around a structured session model.
diff --git a/docs/content/auth-flows/token-behavior.md b/docs/content/auth-flows/token-behavior.md
index f98d056e..18c76126 100644
--- a/docs/content/auth-flows/token-behavior.md
+++ b/docs/content/auth-flows/token-behavior.md
@@ -1,3 +1,10 @@
+---
+title: Token Behavior
+order: 6
+group: auth-flows
+---
+
+
# π Token Behavior
In UltimateAuth, tokens are not the foundation of authentication.
diff --git a/docs/content/client/authentication.md b/docs/content/client/authentication.md
index 4148c02d..53630cdf 100644
--- a/docs/content/client/authentication.md
+++ b/docs/content/client/authentication.md
@@ -1,3 +1,10 @@
+---
+title: Authentication
+order: 2
+group: client
+---
+
+
# π Authentication Guide
This section explains how to use the UltimateAuth client for authentication flows.
diff --git a/docs/content/client/authorization.md b/docs/content/client/authorization.md
index 8880bc18..b3c4a880 100644
--- a/docs/content/client/authorization.md
+++ b/docs/content/client/authorization.md
@@ -1,3 +1,10 @@
+---
+title: Authorization
+order: 7
+group: client
+---
+
+
# π‘ Authorization Guide
This section explains how to manage roles, permissions, and access control using the UltimateAuth client.
diff --git a/docs/content/client/credentials.md b/docs/content/client/credentials.md
index 0df5b06c..6ce81fb6 100644
--- a/docs/content/client/credentials.md
+++ b/docs/content/client/credentials.md
@@ -1,3 +1,10 @@
+---
+title: Credentials
+order: 6
+group: client
+---
+
+
# π Credential Management Guide
This section explains how to manage user credentials (such as passwords) using the UltimateAuth client.
diff --git a/docs/content/client/identifiers.md b/docs/content/client/identifiers.md
index 0629a49e..ead1b971 100644
--- a/docs/content/client/identifiers.md
+++ b/docs/content/client/identifiers.md
@@ -1,3 +1,10 @@
+---
+title: Identifiers
+order: 5
+group: client
+---
+
+
# π User Identifiers Guide
This section explains how UltimateAuth manages user identifiers such as email, username, and phone.
diff --git a/docs/content/client/index.md b/docs/content/client/index.md
index a933839f..7f75a687 100644
--- a/docs/content/client/index.md
+++ b/docs/content/client/index.md
@@ -1,3 +1,10 @@
+---
+title: Client & API
+order: 1
+group: client
+---
+
+
# π Client Usage Guide
UltimateAuth Client is a **high-level SDK** designed to simplify authentication flows.
diff --git a/docs/content/client/session-management.md b/docs/content/client/session-management.md
index d5885e9a..67e86767 100644
--- a/docs/content/client/session-management.md
+++ b/docs/content/client/session-management.md
@@ -1,3 +1,10 @@
+---
+title: Session Management
+order: 3
+group: client
+---
+
+
# π± Session Management Guide
This section explains how to manage sessions and devices using the UltimateAuth client.
diff --git a/docs/content/client/user-management.md b/docs/content/client/user-management.md
index 53df4f0a..f6c7c72e 100644
--- a/docs/content/client/user-management.md
+++ b/docs/content/client/user-management.md
@@ -1,3 +1,10 @@
+---
+title: User Management
+order: 4
+group: client
+---
+
+
# π€ User Management Guide
This section explains how to manage users using the UltimateAuth client.
diff --git a/docs/content/configuration/advanced-configuration.md b/docs/content/configuration/advanced-configuration.md
index 39b6815a..7ff33835 100644
--- a/docs/content/configuration/advanced-configuration.md
+++ b/docs/content/configuration/advanced-configuration.md
@@ -1,3 +1,10 @@
+---
+title: Advanced Configuration
+order: 6
+group: configuration
+---
+
+
# π§ Advanced Configuration
UltimateAuth is designed to be flexible β but not fragile.
diff --git a/docs/content/configuration/client-options.md b/docs/content/configuration/client-options.md
index ce417097..4ce9c7c3 100644
--- a/docs/content/configuration/client-options.md
+++ b/docs/content/configuration/client-options.md
@@ -1,3 +1,10 @@
+---
+title: Client Options
+order: 4
+group: configuration
+---
+
+
# π§© Client Options
Client Options define how UltimateAuth behaves on the **client side**.
diff --git a/docs/content/configuration/configuration-overview.md b/docs/content/configuration/configuration-overview.md
index 50767eb7..fe9bd7a7 100644
--- a/docs/content/configuration/configuration-overview.md
+++ b/docs/content/configuration/configuration-overview.md
@@ -1,3 +1,10 @@
+---
+title: Configuration Overview
+order: 2
+group: configuration
+---
+
+
# π§ Configuration Overview
UltimateAuth is not configured as a static system.
@@ -161,6 +168,5 @@ If you remember one thing:
## β‘οΈ Next Step
-- Deep dive into behavior β Core Options
- Control runtime β Server Options
- Configure clients β Client Options
diff --git a/docs/content/configuration/configuration-sources.md b/docs/content/configuration/configuration-sources.md
index 964efc8e..3386a379 100644
--- a/docs/content/configuration/configuration-sources.md
+++ b/docs/content/configuration/configuration-sources.md
@@ -1,3 +1,10 @@
+---
+title: Configuration Sources
+order: 5
+group: configuration
+---
+
+
# βοΈ Configuration Sources
UltimateAuth supports multiple configuration sources.
diff --git a/docs/content/configuration/index.md b/docs/content/configuration/index.md
index 534552eb..2aff7b31 100644
--- a/docs/content/configuration/index.md
+++ b/docs/content/configuration/index.md
@@ -1,3 +1,10 @@
+---
+title: Configuration
+order: 1
+group: configuration
+---
+
+
# βοΈ Configuration & Extensibility
UltimateAuth is designed to be flexible.
diff --git a/docs/content/configuration/server-options.md b/docs/content/configuration/server-options.md
index 1ad00e20..1656c29a 100644
--- a/docs/content/configuration/server-options.md
+++ b/docs/content/configuration/server-options.md
@@ -1,3 +1,10 @@
+---
+title: Server Options
+order: 3
+group: configuration
+---
+
+
# π§© Server Options
UltimateAuth is configured primarily through **Server Options**.
diff --git a/docs/content/fundamentals/auth-model.md b/docs/content/fundamentals/auth-model.md
index b5026353..0616637f 100644
--- a/docs/content/fundamentals/auth-model.md
+++ b/docs/content/fundamentals/auth-model.md
@@ -1,3 +1,10 @@
+---
+title: Authentication Model
+order: 2
+group: fundamentals
+---
+
+
# π§ Authentication Model
UltimateAuth is built around a simple but powerful idea:
diff --git a/docs/content/fundamentals/auth-modes.md b/docs/content/fundamentals/auth-modes.md
index 941d14e3..756c202f 100644
--- a/docs/content/fundamentals/auth-modes.md
+++ b/docs/content/fundamentals/auth-modes.md
@@ -1,3 +1,10 @@
+---
+title: Authentication Modes
+order: 4
+group: fundamentals
+---
+
+
> Note: SemiHybrid and PureJwt modes will be available on future releases. For now you can safely use PureOpaque and Hybrid modes.
# π Authentication Modes
diff --git a/docs/content/fundamentals/client-profiles.md b/docs/content/fundamentals/client-profiles.md
index 7c4387fa..cfa72dd0 100644
--- a/docs/content/fundamentals/client-profiles.md
+++ b/docs/content/fundamentals/client-profiles.md
@@ -1,3 +1,10 @@
+---
+title: Client Profiles
+order: 5
+group: fundamentals
+---
+
+
# π§© Client Profiles
UltimateAuth adapts its authentication behavior based on the client.
diff --git a/docs/content/fundamentals/flow-based-auth.md b/docs/content/fundamentals/flow-based-auth.md
index ba4081dc..6e60d06a 100644
--- a/docs/content/fundamentals/flow-based-auth.md
+++ b/docs/content/fundamentals/flow-based-auth.md
@@ -1,3 +1,10 @@
+---
+title: Flow Based Authentication
+order: 3
+group: fundamentals
+---
+
+
# π Flow-Based Authentication
UltimateAuth is not cookie-based or token-based.
diff --git a/docs/content/fundamentals/index.md b/docs/content/fundamentals/index.md
index cd2cfed7..fbe6c93b 100644
--- a/docs/content/fundamentals/index.md
+++ b/docs/content/fundamentals/index.md
@@ -1,12 +1,22 @@
-# π§ Fundamentals
+---
+title: Fundamental Overview
+order: 1
+group: fundamentals
+---
+
+# Overview
+
This section explains how UltimateAuth works internally.
+
+# Section Order
+
If you are new, follow this order:
-1. [Authentication Model](./auth-model.md)
-2. [Flow-Based Authentication](./flow-based-auth.md)
-3. [Authentication Modes](./auth-modes.md)
-4. [Client Profiles](./client-profiles.md)
-5. [Runtime Architecture](./runtime-architecture.md)
-6. [Request Lifecycle](./request-lifecycle.md)
+- Authentication Model
+- Flow-Based Authentication
+- Authentication Modes
+- Client Profiles
+- Runtime Architecture
+- Request Lifecycle
diff --git a/docs/content/fundamentals/request-lifecycle.md b/docs/content/fundamentals/request-lifecycle.md
index 4cf2cf34..94ca5f76 100644
--- a/docs/content/fundamentals/request-lifecycle.md
+++ b/docs/content/fundamentals/request-lifecycle.md
@@ -1,3 +1,10 @@
+---
+title: Request Lifecycle
+order: 7
+group: fundamentals
+---
+
+
# π Request Lifecycle
This section explains what happens when a request enters UltimateAuth.
diff --git a/docs/content/fundamentals/runtime-architecture.md b/docs/content/fundamentals/runtime-architecture.md
index 8d857469..992ed5a7 100644
--- a/docs/content/fundamentals/runtime-architecture.md
+++ b/docs/content/fundamentals/runtime-architecture.md
@@ -1,3 +1,10 @@
+---
+title: Runtime Architecture
+order: 6
+group: fundamentals
+---
+
+
# π Runtime Architecture
UltimateAuth processes authentication through a structured execution pipeline.
diff --git a/docs/content/getting-started/index.md b/docs/content/getting-started/index.md
index e8b7dcda..2fe4abd8 100644
--- a/docs/content/getting-started/index.md
+++ b/docs/content/getting-started/index.md
@@ -1,3 +1,9 @@
+---
+title: Getting Started
+order: 1
+group: getting-started
+---
+
# π Getting Started
Welcome to **UltimateAuth** β the modern authentication framework for .NET.
diff --git a/docs/content/getting-started/quickstart.md b/docs/content/getting-started/quickstart.md
index f6b5c27d..8d77e763 100644
--- a/docs/content/getting-started/quickstart.md
+++ b/docs/content/getting-started/quickstart.md
@@ -1,3 +1,9 @@
+---
+title: QuickStart
+order: 2
+group: getting-started
+---
+
# β‘ Quick Start
In this guide, you will set up UltimateAuth in a few minutes and perform your **first login**.
diff --git a/docs/content/getting-started/real-world-setup.md b/docs/content/getting-started/real-world-setup.md
index 48578b07..574cf928 100644
--- a/docs/content/getting-started/real-world-setup.md
+++ b/docs/content/getting-started/real-world-setup.md
@@ -1,3 +1,9 @@
+---
+title: Real World Setup
+order: 3
+group: getting-started
+---
+
# π Real-World Setup
The Quick Start uses an in-memory setup for simplicity.
diff --git a/docs/content/plugin-domains/authorization-domain.md b/docs/content/plugin-domains/authorization-domain.md
index 735ab0e8..1b9e0c37 100644
--- a/docs/content/plugin-domains/authorization-domain.md
+++ b/docs/content/plugin-domains/authorization-domain.md
@@ -1,3 +1,10 @@
+---
+title: Authorization
+order: 4
+group: plugin-domains
+---
+
+
# π Authorization & Policies
UltimateAuth provides a flexible and extensible authorization system based on:
diff --git a/docs/content/plugin-domains/credential-domain.md b/docs/content/plugin-domains/credential-domain.md
index 50a8c2e3..1f9f5813 100644
--- a/docs/content/plugin-domains/credential-domain.md
+++ b/docs/content/plugin-domains/credential-domain.md
@@ -1,3 +1,10 @@
+---
+title: Credentials
+order: 3
+group: plugin-domains
+---
+
+
# π Credentials Domain
Credentials in UltimateAuth define how a user proves their identity.
diff --git a/docs/content/plugin-domains/index.md b/docs/content/plugin-domains/index.md
index 596d2e23..14950f03 100644
--- a/docs/content/plugin-domains/index.md
+++ b/docs/content/plugin-domains/index.md
@@ -1,3 +1,10 @@
+---
+title: Plugin Domains
+order: 1
+group: plugin-domains
+---
+
+
# π§© Plugin Domains
Authentication alone is not enough.
diff --git a/docs/content/plugin-domains/policies.md b/docs/content/plugin-domains/policies.md
index 111f0bbe..b688e5b9 100644
--- a/docs/content/plugin-domains/policies.md
+++ b/docs/content/plugin-domains/policies.md
@@ -1,3 +1,10 @@
+---
+title: Policies
+order: 5
+group: plugin-domains
+---
+
+
# π‘ Policies & Access Control
UltimateAuth uses a **policy-driven authorization model**.
diff --git a/docs/content/plugin-domains/users-domain.md b/docs/content/plugin-domains/users-domain.md
index 45630abb..c7b27935 100644
--- a/docs/content/plugin-domains/users-domain.md
+++ b/docs/content/plugin-domains/users-domain.md
@@ -1,3 +1,10 @@
+---
+title: Users
+order: 2
+group: plugin-domains
+---
+
+
# π€ Users Domain
Users in UltimateAuth are not a single entity.
diff --git a/docs/content/security/access-token-behavior.md b/docs/content/security/access-token-behavior.md
index 4313693e..4920b808 100644
--- a/docs/content/security/access-token-behavior.md
+++ b/docs/content/security/access-token-behavior.md
@@ -1,3 +1,10 @@
+---
+title: Access Token Behavior
+order: 2
+group: security
+---
+
+
# π― Access Token Behavior
Access tokens in UltimateAuth are intentionally **short-lived and mode-aware**.
diff --git a/docs/content/security/policy-pipeline.md b/docs/content/security/policy-pipeline.md
index 1ff9fc78..f7d28ba3 100644
--- a/docs/content/security/policy-pipeline.md
+++ b/docs/content/security/policy-pipeline.md
@@ -1,3 +1,10 @@
+---
+title: Policy Pipeline
+order: 4
+group: security
+---
+
+
# π§ Policy Pipeline Deep Dive
UltimateAuth does not rely on simple role checks.
diff --git a/docs/content/security/refresh-rotation.md b/docs/content/security/refresh-rotation.md
index 2db9b053..51c00e3f 100644
--- a/docs/content/security/refresh-rotation.md
+++ b/docs/content/security/refresh-rotation.md
@@ -1,3 +1,10 @@
+---
+title: Refresh Rotation
+order: 3
+group: security
+---
+
+
# π Refresh Token Rotation
Refresh tokens in UltimateAuth are not simple long-lived tokens.
diff --git a/docs/content/security/session-security-model.md b/docs/content/security/session-security-model.md
index f61c00fd..e905d138 100644
--- a/docs/content/security/session-security-model.md
+++ b/docs/content/security/session-security-model.md
@@ -1,3 +1,10 @@
+---
+title: Session Security Model
+order: 1
+group: security
+---
+
+
# π Session Security Model
UltimateAuth is built around a **hierarchical session model**.
diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/CodeBeam.UltimateAuth.Docs.Wasm.Client.csproj b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/CodeBeam.UltimateAuth.Docs.Wasm.Client.csproj
index bb9cbd6e..3ac3c530 100644
--- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/CodeBeam.UltimateAuth.Docs.Wasm.Client.csproj
+++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/CodeBeam.UltimateAuth.Docs.Wasm.Client.csproj
@@ -17,4 +17,10 @@
+
+
+
+
+
+
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
new file mode 100644
index 00000000..e0cba0bf
--- /dev/null
+++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Layout/DocsLayout.razor
@@ -0,0 +1,18 @@
+ο»Ώ@using CodeBeam.UltimateAuth.Docs.Wasm.Client.Pages
+@inherits LayoutComponentBase
+@layout MainLayout
+
+
+
+
+
+
+
+ @Body
+
+
+
+
+
+
+
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 40ae601b..4fe38e2d 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
@@ -1,4 +1,5 @@
ο»Ώ@using CodeBeam.UltimateAuth.Docs.Wasm.Client.Brand
+@using CodeBeam.UltimateAuth.Docs.Wasm.Client.Pages
@inherits LayoutComponentBase
@inject NavigationManager Nav
@@ -18,7 +19,7 @@
- Docs
+ Docs
Samples
Donate
@@ -39,8 +40,10 @@
Home
- Docs
- Samples
+
+
+
+ Samples
Donate
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
new file mode 100644
index 00000000..e2b4bbfb
--- /dev/null
+++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsContent.razor
@@ -0,0 +1,197 @@
+ο»Ώ@using System.Net.Http.Json
+@inject DocsPageState PageState
+@inject HttpClient Http
+@inject NavigationManager Nav
+@inject IJSRuntime JS
+
+@if (_loading || !RendererInfo.IsInteractive)
+{
+
+
+
+ return;
+}
+else if (_notFound)
+{
+
+
+ Document not found.
+
+
+}
+else if (_doc is not null)
+{
+
+ @_doc.Title
+
+
+
+ @((MarkupString)_doc.Html)
+
+
+
+
+
+ @if (_prev != null)
+ {
+
+
+
+ Previous
+
+
+
+ @_prev.Title
+
+
+
+ }
+
+
+
+ @if (_next != null)
+ {
+
+
+
+ Next
+
+
+
+ @_next.Title
+
+
+
+ }
+
+
+
+}
+
+@code {
+ [Parameter]
+ public string? Slug { get; set; }
+
+ private DocPage? _doc;
+ private bool _loading = true;
+ private bool _notFound;
+ private ElementReference _contentRef;
+
+ 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('/');
+
+ try
+ {
+ _doc = await Http.GetFromJsonAsync($"docs/{slug}.json");
+
+ if (_doc == null)
+ {
+ _notFound = true;
+ }
+ else
+ {
+ PageState.SetPage(slug, _doc.Title, _doc.Headings);
+ await EnsureNavLoaded();
+ ComputePrevNext();
+ }
+ }
+ catch
+ {
+ _notFound = true;
+ }
+
+ _loading = false;
+ }
+
+ protected override async Task OnAfterRenderAsync(bool firstRender)
+ {
+ if (!_loading && _doc != null)
+ {
+ // await JS.InvokeVoidAsync("docsEnhance", _contentRef);
+ }
+ }
+
+ private sealed class DocPage
+ {
+ public string Slug { get; set; } = "";
+ public string Title { get; set; } = "";
+ public string Html { get; set; } = "";
+ public List Headings { get; set; } = [];
+ }
+
+ private List _nav = [];
+ private List? _navCache;
+ private DocNavItem? _prev;
+ private DocNavItem? _next;
+
+ private class DocNavItem
+ {
+ public string Title { get; set; } = "";
+ public string Slug { get; set; } = "";
+ }
+
+ private async Task EnsureNavLoaded()
+ {
+ if (_navCache is not null)
+ return;
+
+ _navCache = await Http.GetFromJsonAsync>("docs/docs-index.json") ?? [];
+ }
+
+ private void ComputePrevNext()
+ {
+ _prev = null;
+ _next = null;
+
+ if (_navCache is null)
+ return;
+
+ var currentPath = Nav.ToBaseRelativePath(Nav.Uri);
+
+ var currentSlug = currentPath.StartsWith("docs/")
+ ? currentPath.Substring(5)
+ : currentPath;
+
+ currentSlug = currentSlug.Trim('/');
+
+ var index = _navCache.FindIndex(x =>
+ x.Slug.Trim('/').Equals(currentSlug, StringComparison.OrdinalIgnoreCase));
+
+ if (index > 0)
+ _prev = _navCache[index - 1];
+
+ if (index >= 0 && index < _navCache.Count - 1)
+ _next = _navCache[index + 1];
+ }
+
+ private async Task LoadNavAsync()
+ {
+ _nav = await Http.GetFromJsonAsync>("docs/docs-index.json") ?? [];
+
+ var currentPath = Nav.ToBaseRelativePath(Nav.Uri);
+
+ var currentSlug = currentPath.StartsWith("docs/")
+ ? currentPath.Substring(5)
+ : currentPath;
+
+ var index = _nav.FindIndex(x => x.Slug == currentSlug);
+
+ Console.WriteLine($"Slug: {currentSlug}, Index: {index}");
+
+ if (index > 0)
+ _prev = _nav[index - 1];
+
+ if (index >= 0 && index < _nav.Count - 1)
+ _next = _nav[index + 1];
+ }
+}
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 be6a91bc..6c71a602 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
@@ -1,30 +1,178 @@
ο»Ώ@page "/docs"
+@layout DocsLayout
-
-
- UltimateAuth Docs
- The modern way to understand authentication β unified, simple and powerful.
-
-
-
- This page is preparing.
-
-
- But the documentation is currently available in markdown format and covers core concepts, architecture, flows and integration guides.
-
-
-
- Start exploring the docs to understand how UltimateAuth simplifies authentication across sessions, cookies and tokens.
-
-
-
- Read Documentation
-
-
-
- Full documentation site is coming soon.
-
+
+
+
+
+ UltimateAuth Docs
+ The modern way to understand authentication β unified, simple and powerful.
+
+
+ Learn the architecture, flows, client APIs, security model, and integration patterns of UltimateAuth:
+
+
+
+
+
+
+ Start Here
+
+
+
+ If you are new to UltimateAuth, start with Getting Started and
+ Fundamentals. If you care about usage, continue with Client & API.
+ If you are evaluating architecture or enterprise adoption,
+ continue with Auth Flows, Security, and Plugin Domains.
+
+
+
+
+
+
+
+
+ Getting Started
+
+
+ Set up UltimateAuth quickly, understand the first login flow,
+ and move into real-world application setup.
+
+
+
+ Open Getting Started
+
+
+
+
+
+
+
+
+ Fundamentals
+
+
+ Understand the authentication model, flow-based execution,
+ auth modes, client profiles, and runtime architecture.
+
+
+
+ Open Fundamentals
+
+
+
+
+
+
+
+
+ Auth Flows
+
+
+ Explore login, refresh, logout, session lifecycle,
+ token behavior, and device-aware authentication flows.
+
+
+
+ Open Auth Flows
+
+
+
+
+
+
+
+
+ Client & API
+
+
+ Explore the usage, the service client layer of UltimateAuth.
+ Everything from basic login/logout to advanced session management should call with client.
+
+
+
+ Open Client & API
+
+
+
+
+
+
+
+
+ Plugin Domains
+
+
+ Explore the user, credential, and authorization modules that not required for core Auth, but essential.
+
+
+
+ Open Plugin Domains
+
+
+
+
+
+
+
+
+
+ Security
+
+
+
+ Review the session security model, token behavior,
+ refresh rotation, and the policy pipeline.
+
+
+
+ Open Security Docs
+
+
+
+
+
+
+
+
+
+ Coming Next
+
+
+
+ The documentation experience will continue to expand with richer navigation,
+ improved code rendering, and dedicated reference surfaces.
+
+
+
+ API Reference
+ Interactive authentication sandbox
+ Sample-driven guides
+ Integration recipes for Blazor, WASM, MAUI, MVC, and APIs
+
+
+
+
+
+
+ Browse Documentation
+
+
+
+ View Markdown Source
+
+
+
-
\ No newline at end of file
+
diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsPage.razor b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsPage.razor
new file mode 100644
index 00000000..930e0873
--- /dev/null
+++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsPage.razor
@@ -0,0 +1,9 @@
+ο»Ώ@page "/docs/{**slug}"
+@layout DocsLayout
+
+
+
+@code {
+ [Parameter]
+ public string? slug { get; set; }
+}
diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsPageState.cs b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsPageState.cs
new file mode 100644
index 00000000..78ba56dc
--- /dev/null
+++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsPageState.cs
@@ -0,0 +1,46 @@
+ο»Ώnamespace CodeBeam.UltimateAuth.Docs.Wasm.Client.Pages;
+
+public sealed class DocsPageState
+{
+ public string? CurrentSlug { get; private set; }
+ public string? CurrentTitle { get; private set; }
+ public IReadOnlyList Headings { get; private set; } = [];
+
+ public string? ActiveHeadingId { get; private set; }
+
+ public event Action? Changed;
+
+ public void SetPage(string? slug, string? title, IReadOnlyList headings)
+ {
+ CurrentSlug = slug;
+ CurrentTitle = title;
+ Headings = headings;
+ ActiveHeadingId = headings.Count > 0 ? headings[0].Id : null;
+ Changed?.Invoke();
+ }
+
+ public void SetActiveHeading(string? id)
+ {
+ if (string.Equals(ActiveHeadingId, id, StringComparison.Ordinal))
+ return;
+
+ ActiveHeadingId = id;
+ Changed?.Invoke();
+ }
+
+ public void Clear()
+ {
+ CurrentSlug = null;
+ CurrentTitle = null;
+ Headings = [];
+ ActiveHeadingId = null;
+ Changed?.Invoke();
+ }
+}
+
+public sealed class DocsHeadingItem
+{
+ public string Id { get; set; } = "";
+ public string Text { get; set; } = "";
+ public int Level { get; set; }
+}
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
new file mode 100644
index 00000000..08c0b652
--- /dev/null
+++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsSidebar.razor
@@ -0,0 +1,88 @@
+ο»Ώ@inject HttpClient Http
+@inject NavigationManager Nav
+
+
+ @if (!Inline)
+ {
+
+ UAuth Docs
+
+
+ }
+
+
+
+ @if (_groups == null)
+ {
+
+ }
+ else
+ {
+
+ Overview
+ @foreach (var group in _groups)
+ {
+
+ @foreach (var item in group.Value)
+ {
+ @item.Title
+ }
+
+ }
+
+ }
+
+
+@code {
+ private Dictionary>? _groups;
+ private DocIndexItem? _selectedValue;
+ private bool _expanded = true;
+
+ [Parameter]
+ public bool Inline { get; set; }
+
+ protected override async Task OnAfterRenderAsync(bool firstRender)
+ {
+ if (!firstRender)
+ return;
+
+ var items = await Http.GetFromJsonAsync>("/docs/docs-index.json") ?? [];
+
+ _groups = items
+ .GroupBy(x => x.Group)
+ .OrderBy(g => g.Min(x => x.GroupOrder))
+ .ToDictionary(
+ g => g.Key,
+ g => g.OrderBy(x => x.Order).ToList()
+ );
+
+ StateHasChanged();
+ }
+
+ private string GetItemClass(DocIndexItem item)
+ {
+ var current = Nav.ToBaseRelativePath(Nav.Uri);
+
+ return current == $"docs/{item.Slug}"
+ ? "mud-selected-item"
+ : "";
+ }
+
+ private string FormatGroup(string group)
+ {
+ if (string.IsNullOrWhiteSpace(group))
+ return "General";
+
+ return System.Globalization.CultureInfo.CurrentCulture.TextInfo
+ .ToTitleCase(group.Replace("-", " "));
+ }
+
+ private class DocIndexItem
+ {
+ public string Title { get; set; } = "";
+ public string Slug { get; set; } = "";
+ public int Order { get; set; }
+ public string Group { get; set; } = "";
+ public int GroupOrder { get; set; } = 999;
+ }
+}
diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsToc.razor b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsToc.razor
new file mode 100644
index 00000000..700b47d5
--- /dev/null
+++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsToc.razor
@@ -0,0 +1,74 @@
+ο»Ώ@inject DocsPageState PageState
+@inject NavigationManager Nav
+@inject IJSRuntime JS
+
+@if (PageState.Headings.Count == 0)
+{
+ return;
+}
+
+
+ On this page
+
+
+ @foreach (var item in PageState.Headings)
+ {
+
+ @item.Text
+
+ }
+
+
+
+@code {
+ private DotNetObjectReference? _ref;
+
+ protected override void OnInitialized()
+ {
+ PageState.Changed += OnStateChanged;
+ }
+
+ protected override async Task OnAfterRenderAsync(bool firstRender)
+ {
+ if (_ref == null && PageState.Headings.Count > 0)
+ {
+ _ref = DotNetObjectReference.Create(this);
+ await JS.InvokeVoidAsync("uaDocsScrollSpy.start", _ref);
+ }
+ }
+
+ private void OnStateChanged()
+ {
+ InvokeAsync(StateHasChanged);
+ }
+
+ private string GetHref(string id)
+ {
+ return $"{Nav.Uri.Split('#')[0]}#{id}";
+ }
+
+ [JSInvokable]
+ public Task SetActiveHeading(string id)
+ {
+ PageState.SetActiveHeading(id);
+ return Task.CompletedTask;
+ }
+
+ private async Task OnClick(string id)
+ {
+ PageState.SetActiveHeading(id);
+ await JS.InvokeVoidAsync("uaDocsScrollSpy.scrollTo", id);
+ }
+
+ private string GetClass(DocsHeadingItem item)
+ => item.Id == PageState.ActiveHeadingId ? "active toc-active" : "";
+
+ private Task ScrollTo(string id)
+ => JS.InvokeVoidAsync("uaDocsScrollSpy.scrollTo", id).AsTask();
+
+ public void Dispose()
+ {
+ PageState.Changed -= OnStateChanged;
+ }
+}
diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Program.cs b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Program.cs
index c8dbf753..c84ac5a8 100644
--- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Program.cs
+++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Program.cs
@@ -1,3 +1,4 @@
+using CodeBeam.UltimateAuth.Docs.Wasm.Client.Pages;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using MudBlazor.Services;
using MudExtensions.Services;
@@ -7,4 +8,12 @@
builder.Services.AddMudServices();
builder.Services.AddMudExtensions();
+builder.Services.AddScoped();
+
+builder.Services.AddScoped(sp =>
+ new HttpClient
+ {
+ BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)
+ });
+
await builder.Build().RunAsync();
diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/device-management.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/device-management.json
new file mode 100644
index 00000000..966fac57
--- /dev/null
+++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/device-management.json
@@ -0,0 +1,97 @@
+{
+ "Slug": "auth-flows/device-management",
+ "Title": "Device Management",
+ "Html": "\n\u003Cp\u003EIn UltimateAuth, devices are not an afterthought.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 They are a \u003Cstrong\u003Efirst-class concept\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022why-device-matters\u0022\u003E\uD83E\uDDE0 Why Device Matters\u003C/h2\u003E\n\u003Cp\u003EMost authentication systems ignore devices.\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EA user logs in\u003C/li\u003E\n\u003Cli\u003EA token is issued\u003C/li\u003E\n\u003Cli\u003EEverything is treated the same\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This breaks down when you need:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EMulti-device control\u003C/li\u003E\n\u003Cli\u003ESession visibility\u003C/li\u003E\n\u003Cli\u003ESecurity enforcement\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 UltimateAuth solves this with \u003Cstrong\u003Edevice-aware authentication\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022core-concept-chain-device\u0022\u003E\uD83E\uDDE9 Core Concept: Chain = Device\u003C/h2\u003E\n\u003Cp\u003EIn UltimateAuth:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 A \u003Cstrong\u003ESessionChain represents a device\u003C/strong\u003E\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003EDevice \u2192 Chain \u2192 Sessions\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EEach chain:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EIs bound to a device\u003C/li\u003E\n\u003Cli\u003EGroups sessions\u003C/li\u003E\n\u003Cli\u003ETracks activity\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 A device is not inferred \u2014 it is explicitly modeled\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022what-defines-a-device\u0022\u003E\uD83D\uDD17 What Defines a Device?\u003C/h2\u003E\n\u003Cp\u003EA chain includes:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EDeviceId\u003C/li\u003E\n\u003Cli\u003EPlatform (web, mobile, etc.)\u003C/li\u003E\n\u003Cli\u003EOperating System\u003C/li\u003E\n\u003Cli\u003EBrowser\u003C/li\u003E\n\u003Cli\u003EIP (optional binding)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This forms a \u003Cstrong\u003Edevice fingerprint\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022device-lifecycle\u0022\u003E\uD83D\uDD04 Device Lifecycle\u003C/h2\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022first-login\u0022\u003E1\uFE0F\u20E3 First Login\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ENew device detected\u003C/li\u003E\n\u003Cli\u003ENew chain is created\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022subsequent-logins\u0022\u003E2\uFE0F\u20E3 Subsequent Logins\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ESame device \u2192 reuse chain\u003C/li\u003E\n\u003Cli\u003ENew device \u2192 new chain\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Device continuity is preserved\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022activity-touch\u0022\u003E3\uFE0F\u20E3 Activity (Touch)\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EChain \u003Ccode\u003ELastSeenAt\u003C/code\u003E updated\u003C/li\u003E\n\u003Cli\u003E\u003Ccode\u003ETouchCount\u003C/code\u003E increases\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Tracks real usage\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022token-rotation\u0022\u003E4\uFE0F\u20E3 Token Rotation\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession changes\u003C/li\u003E\n\u003Cli\u003EChain remains\u003C/li\u003E\n\u003Cli\u003E\u003Ccode\u003ERotationCount\u003C/code\u003E increases\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Device identity stays stable\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022logout\u0022\u003E5\uFE0F\u20E3 Logout\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession removed\u003C/li\u003E\n\u003Cli\u003EChain remains\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Device still trusted\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022revoke\u0022\u003E6\uFE0F\u20E3 Revoke\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EChain invalidated\u003C/li\u003E\n\u003Cli\u003EAll sessions removed\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Device trust is reset\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022security-model\u0022\u003E\uD83D\uDD10 Security Model\u003C/h2\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022device-binding\u0022\u003E\uD83D\uDD17 Device Binding\u003C/h3\u003E\n\u003Cp\u003ESessions and tokens are tied to:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EChain\u003C/li\u003E\n\u003Cli\u003EDevice context\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Prevents cross-device reuse\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022rotation-tracking\u0022\u003E\uD83D\uDD01 Rotation Tracking\u003C/h3\u003E\n\u003Cp\u003EChains track:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERotationCount\u003C/li\u003E\n\u003Cli\u003ETouchCount\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Enables anomaly detection\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022revoke-cascade\u0022\u003E\uD83D\uDEA8 Revoke Cascade\u003C/h3\u003E\n\u003Cp\u003EIf a device is compromised:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EEntire chain can be revoked\u003C/li\u003E\n\u003Cli\u003EAll sessions invalidated\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Immediate containment\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022configuration\u0022\u003E\u2699\uFE0F Configuration\u003C/h2\u003E\n\u003Cp\u003EDevice behavior is configurable via session options:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EMax chains per user\u003C/li\u003E\n\u003Cli\u003EMax sessions per chain\u003C/li\u003E\n\u003Cli\u003EPlatform-based limits\u003C/li\u003E\n\u003Cli\u003EDevice mismatch behavior\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Fine-grained control for enterprise scenarios\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Device = Chain\u003Cbr /\u003E\n\uD83D\uDC49 Not just metadata\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022key-takeaways\u0022\u003E\uD83D\uDCCC Key Takeaways\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EDevices are explicitly modeled\u003C/li\u003E\n\u003Cli\u003EEach device has its own chain\u003C/li\u003E\n\u003Cli\u003ESessions belong to chains\u003C/li\u003E\n\u003Cli\u003ESecurity is enforced per device\u003C/li\u003E\n\u003Cli\u003ELogout and revoke operate on device scope\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003EContinue to \u003Cstrong\u003EConfiguration \u0026amp; Extensibility\u003C/strong\u003E\u003C/p\u003E\n",
+ "Headings": [
+ {
+ "Id": "why-device-matters",
+ "Text": "\uD83E\uDDE0 Why Device Matters",
+ "Level": 0
+ },
+ {
+ "Id": "core-concept-chain-device",
+ "Text": "\uD83E\uDDE9 Core Concept: Chain = Device",
+ "Level": 0
+ },
+ {
+ "Id": "what-defines-a-device",
+ "Text": "\uD83D\uDD17 What Defines a Device?",
+ "Level": 0
+ },
+ {
+ "Id": "device-lifecycle",
+ "Text": "\uD83D\uDD04 Device Lifecycle",
+ "Level": 0
+ },
+ {
+ "Id": "first-login",
+ "Text": "1\uFE0F\u20E3 First Login",
+ "Level": 1
+ },
+ {
+ "Id": "subsequent-logins",
+ "Text": "2\uFE0F\u20E3 Subsequent Logins",
+ "Level": 1
+ },
+ {
+ "Id": "activity-touch",
+ "Text": "3\uFE0F\u20E3 Activity (Touch)",
+ "Level": 1
+ },
+ {
+ "Id": "token-rotation",
+ "Text": "4\uFE0F\u20E3 Token Rotation",
+ "Level": 1
+ },
+ {
+ "Id": "logout",
+ "Text": "5\uFE0F\u20E3 Logout",
+ "Level": 1
+ },
+ {
+ "Id": "revoke",
+ "Text": "6\uFE0F\u20E3 Revoke",
+ "Level": 1
+ },
+ {
+ "Id": "security-model",
+ "Text": "\uD83D\uDD10 Security Model",
+ "Level": 0
+ },
+ {
+ "Id": "device-binding",
+ "Text": "\uD83D\uDD17 Device Binding",
+ "Level": 1
+ },
+ {
+ "Id": "rotation-tracking",
+ "Text": "\uD83D\uDD01 Rotation Tracking",
+ "Level": 1
+ },
+ {
+ "Id": "revoke-cascade",
+ "Text": "\uD83D\uDEA8 Revoke Cascade",
+ "Level": 1
+ },
+ {
+ "Id": "configuration",
+ "Text": "\u2699\uFE0F Configuration",
+ "Level": 0
+ },
+ {
+ "Id": "mental-model",
+ "Text": "\uD83E\uDDE0 Mental Model",
+ "Level": 0
+ },
+ {
+ "Id": "key-takeaways",
+ "Text": "\uD83D\uDCCC Key Takeaways",
+ "Level": 0
+ },
+ {
+ "Id": "next-step",
+ "Text": "\u27A1\uFE0F Next Step",
+ "Level": 0
+ }
+ ]
+}
\ No newline at end of file
diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/index.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/index.json
new file mode 100644
index 00000000..bb7849a2
--- /dev/null
+++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/index.json
@@ -0,0 +1,77 @@
+{
+ "Slug": "auth-flows/index",
+ "Title": "Auth Flows",
+ "Html": "\n\u003Cp\u003EAuthentication in UltimateAuth is not a single operation.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 It is a \u003Cstrong\u003Eflow-driven system\u003C/strong\u003E.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022what-is-an-auth-flow\u0022\u003E\uD83E\uDDE0 What is an Auth Flow?\u003C/h2\u003E\n\u003Cp\u003EAn auth flow represents a complete authentication operation, such as:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELogging in\u003C/li\u003E\n\u003Cli\u003ERefreshing a session\u003C/li\u003E\n\u003Cli\u003ELogging out\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EEach flow:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EHas a defined lifecycle\u003C/li\u003E\n\u003Cli\u003ERuns through the orchestration pipeline\u003C/li\u003E\n\u003Cli\u003EProduces a controlled authentication outcome\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Instead of calling isolated APIs, you execute \u003Cstrong\u003Eflows\u003C/strong\u003E.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022why-flow-based\u0022\u003E\uD83D\uDD04 Why Flow-Based?\u003C/h2\u003E\n\u003Cp\u003ETraditional systems treat authentication as:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EA login endpoint\u003C/li\u003E\n\u003Cli\u003EA token generator\u003C/li\u003E\n\u003Cli\u003EA cookie setter\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 These approaches often lead to fragmented logic.\u003C/p\u003E\n\u003Cp\u003EUltimateAuth solves this by:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EStructuring authentication as flows\u003C/li\u003E\n\u003Cli\u003EEnforcing a consistent execution model\u003C/li\u003E\n\u003Cli\u003ECentralizing security decisions\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022what-happens-during-a-flow\u0022\u003E\uD83E\uDDE9 What Happens During a Flow?\u003C/h2\u003E\n\u003Cp\u003EEvery flow follows the same pattern:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003EFlow \u2192 Context \u2192 Orchestrator \u2192 Authority \u2192 Result\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cul\u003E\n\u003Cli\u003EThe \u003Cstrong\u003Eflow\u003C/strong\u003E defines the intent\u003C/li\u003E\n\u003Cli\u003EThe \u003Cstrong\u003Econtext\u003C/strong\u003E carries state\u003C/li\u003E\n\u003Cli\u003EThe \u003Cstrong\u003Eorchestrator\u003C/strong\u003E coordinates execution\u003C/li\u003E\n\u003Cli\u003EThe \u003Cstrong\u003Eauthority\u003C/strong\u003E enforces rules\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This ensures consistent and secure behavior across all operations.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022types-of-flows\u0022\u003E\uD83D\uDD10 Types of Flows\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth provides built-in flows for common scenarios:\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022login-flow\u0022\u003E\uD83D\uDD11 Login Flow\u003C/h3\u003E\n\u003Cp\u003EEstablishes authentication by:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EValidating credentials\u003C/li\u003E\n\u003Cli\u003ECreating session hierarchy (root, chain, session)\u003C/li\u003E\n\u003Cli\u003EIssuing tokens if required\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Ca href=\u0022./login-flow.md\u0022\u003ELearn more\u003C/a\u003E\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022refresh-flow\u0022\u003E\uD83D\uDD04 Refresh Flow\u003C/h3\u003E\n\u003Cp\u003EExtends an existing session:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERotates refresh tokens\u003C/li\u003E\n\u003Cli\u003EMaintains session continuity\u003C/li\u003E\n\u003Cli\u003EApplies sliding expiration\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Ca href=\u0022./refresh-flow.md\u0022\u003ELearn more\u003C/a\u003E\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022logout-flow\u0022\u003E\uD83D\uDEAA Logout Flow\u003C/h3\u003E\n\u003Cp\u003ETerminates authentication:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERevokes session(s)\u003C/li\u003E\n\u003Cli\u003EInvalidates tokens\u003C/li\u003E\n\u003Cli\u003ESupports device-level or global logout\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Ca href=\u0022./logout-flow.md\u0022\u003ELearn more\u003C/a\u003E\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022supporting-concepts\u0022\u003E\uD83E\uDDE0 Supporting Concepts\u003C/h2\u003E\n\u003Cp\u003EThese flows operate on top of deeper system models:\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022session-lifecycle\u0022\u003E\uD83E\uDDEC Session Lifecycle\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ERoot \u2192 Chain \u2192 Session hierarchy\u003C/li\u003E\n\u003Cli\u003EDevice-aware session structure\u003C/li\u003E\n\u003Cli\u003ELifecycle management and revocation\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Ca href=\u0022./session-lifecycle.md\u0022\u003ELearn more\u003C/a\u003E\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022token-behavior\u0022\u003E\uD83C\uDF9F Token Behavior\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EAccess token vs refresh token\u003C/li\u003E\n\u003Cli\u003EOpaque vs JWT\u003C/li\u003E\n\u003Cli\u003EMode-dependent behavior\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Ca href=\u0022./token-behavior.md\u0022\u003ELearn more\u003C/a\u003E\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022device-management\u0022\u003E\uD83D\uDCF1 Device Management\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EDevice binding\u003C/li\u003E\n\u003Cli\u003EMulti-device sessions\u003C/li\u003E\n\u003Cli\u003ESecurity implications\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Ca href=\u0022./device-management.md\u0022\u003ELearn more\u003C/a\u003E\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Cstrong\u003EAuthentication is not a single step\u003C/strong\u003E\u003Cbr /\u003E\n\uD83D\uDC49 \u003Cstrong\u003EIt is a controlled flow of state transitions\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022key-takeaways\u0022\u003E\uD83D\uDCCC Key Takeaways\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EAuthentication is executed as flows\u003C/li\u003E\n\u003Cli\u003EEach flow follows a consistent pipeline\u003C/li\u003E\n\u003Cli\u003ESessions and tokens are created as part of flows\u003C/li\u003E\n\u003Cli\u003ESecurity is enforced centrally\u003C/li\u003E\n\u003C/ul\u003E\n\u003Chr /\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003EStart with the most important flow:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Continue to \u003Cstrong\u003ELogin Flow\u003C/strong\u003E\u003C/p\u003E\n",
+ "Headings": [
+ {
+ "Id": "what-is-an-auth-flow",
+ "Text": "\uD83E\uDDE0 What is an Auth Flow?",
+ "Level": 0
+ },
+ {
+ "Id": "why-flow-based",
+ "Text": "\uD83D\uDD04 Why Flow-Based?",
+ "Level": 0
+ },
+ {
+ "Id": "what-happens-during-a-flow",
+ "Text": "\uD83E\uDDE9 What Happens During a Flow?",
+ "Level": 0
+ },
+ {
+ "Id": "types-of-flows",
+ "Text": "\uD83D\uDD10 Types of Flows",
+ "Level": 0
+ },
+ {
+ "Id": "login-flow",
+ "Text": "\uD83D\uDD11 Login Flow",
+ "Level": 1
+ },
+ {
+ "Id": "refresh-flow",
+ "Text": "\uD83D\uDD04 Refresh Flow",
+ "Level": 1
+ },
+ {
+ "Id": "logout-flow",
+ "Text": "\uD83D\uDEAA Logout Flow",
+ "Level": 1
+ },
+ {
+ "Id": "supporting-concepts",
+ "Text": "\uD83E\uDDE0 Supporting Concepts",
+ "Level": 0
+ },
+ {
+ "Id": "session-lifecycle",
+ "Text": "\uD83E\uDDEC Session Lifecycle",
+ "Level": 1
+ },
+ {
+ "Id": "token-behavior",
+ "Text": "\uD83C\uDF9F Token Behavior",
+ "Level": 1
+ },
+ {
+ "Id": "device-management",
+ "Text": "\uD83D\uDCF1 Device Management",
+ "Level": 1
+ },
+ {
+ "Id": "mental-model",
+ "Text": "\uD83E\uDDE0 Mental Model",
+ "Level": 0
+ },
+ {
+ "Id": "key-takeaways",
+ "Text": "\uD83D\uDCCC Key Takeaways",
+ "Level": 0
+ },
+ {
+ "Id": "next-step",
+ "Text": "\u27A1\uFE0F Next Step",
+ "Level": 0
+ }
+ ]
+}
\ No newline at end of file
diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/login-flow.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/login-flow.json
new file mode 100644
index 00000000..497b1331
--- /dev/null
+++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/login-flow.json
@@ -0,0 +1,102 @@
+{
+ "Slug": "auth-flows/login-flow",
+ "Title": "Login",
+ "Html": "\n\u003Cp\u003EThe login flow in UltimateAuth is not just credential validation.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 It is a \u003Cstrong\u003Econtrolled session establishment process\u003C/strong\u003E.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022what-is-login\u0022\u003E\uD83E\uDDE0 What is Login?\u003C/h2\u003E\n\u003Cp\u003EIn traditional systems:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EValidate credentials\u003C/li\u003E\n\u003Cli\u003EIssue a token\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EIn UltimateAuth:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Login creates a \u003Cstrong\u003Esession hierarchy\u003C/strong\u003E\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003ERoot \u2192 Chain \u2192 Session\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003ELogin does not create a token\u003Cbr /\u003E\n\u2192 It creates a session\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Cp\u003ETokens are optional outputs derived from the session.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022step-by-step-execution\u0022\u003E\uD83D\uDD04 Step-by-Step Execution\u003C/h2\u003E\n\u003Cp\u003EThe login flow follows a structured pipeline:\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022identifier-resolution\u0022\u003E1\uFE0F\u20E3 Identifier Resolution\u003C/h3\u003E\n\u003Cp\u003EThe system resolves the user identity:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EUsername / email / phone \u2192 \u003Ccode\u003EUserKey\u003C/code\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022user-security-state\u0022\u003E2\uFE0F\u20E3 User \u0026amp; Security State\u003C/h3\u003E\n\u003Cp\u003EThe system loads:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EUser existence\u003C/li\u003E\n\u003Cli\u003EAccount state\u003C/li\u003E\n\u003Cli\u003EFactor (credential) state\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EChecks include:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EIs the account locked?\u003C/li\u003E\n\u003Cli\u003EIs reauthentication required?\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022credential-validation\u0022\u003E3\uFE0F\u20E3 Credential Validation\u003C/h3\u003E\n\u003Cp\u003ECredentials are validated using providers:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EPassword\u003C/li\u003E\n\u003Cli\u003E(Extensible: OTP, external providers, etc.)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022authority-decision\u0022\u003E4\uFE0F\u20E3 Authority Decision\u003C/h3\u003E\n\u003Cp\u003EThe \u003Cstrong\u003ELoginAuthority\u003C/strong\u003E evaluates the attempt.\u003C/p\u003E\n\u003Cp\u003EPossible outcomes:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u2705 Allow\u003C/li\u003E\n\u003Cli\u003E\u274C Deny\u003C/li\u003E\n\u003Cli\u003E\u26A0\uFE0F Challenge (e.g. MFA)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 No session is created before this decision.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022device-chain-resolution\u0022\u003E5\uFE0F\u20E3 Device \u0026amp; Chain Resolution\u003C/h3\u003E\n\u003Cp\u003EThe system checks if the device is known:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EExisting device \u2192 reuse chain\u003C/li\u003E\n\u003Cli\u003ENew device \u2192 create new chain\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 A \u003Cstrong\u003EChain represents a device\u003C/strong\u003E\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022session-creation\u0022\u003E6\uFE0F\u20E3 Session Creation\u003C/h3\u003E\n\u003Cp\u003EA new session is issued:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELinked to user\u003C/li\u003E\n\u003Cli\u003ELinked to device (chain)\u003C/li\u003E\n\u003Cli\u003EBound to tenant\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003ESession hierarchy:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003EUser \u2192 Root \u2192 Chain \u2192 Session\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022token-issuance-optional\u0022\u003E7\uFE0F\u20E3 Token Issuance (Optional)\u003C/h3\u003E\n\u003Cp\u003EDepending on the mode and request:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EAccess token may be issued\u003C/li\u003E\n\u003Cli\u003ERefresh token may be issued\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Tokens are derived from the session\u003Cbr /\u003E\n\uD83D\uDC49 Not the source of truth\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022event-dispatch\u0022\u003E8\uFE0F\u20E3 Event Dispatch\u003C/h3\u003E\n\u003Cp\u003EThe system emits:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELogin events\u003C/li\u003E\n\u003Cli\u003EAudit information\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022what-gets-created\u0022\u003E\uD83E\uDDE9 What Gets Created?\u003C/h2\u003E\n\u003Cp\u003EA successful login creates:\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022root\u0022\u003E\uD83D\uDD39 Root\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EOne per user\u003C/li\u003E\n\u003Cli\u003ERepresents global authentication state\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022chain\u0022\u003E\uD83D\uDD39 Chain\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EOne per device\u003C/li\u003E\n\u003Cli\u003EManages device lifecycle\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022session\u0022\u003E\uD83D\uDD39 Session\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EIndividual authentication instance\u003C/li\u003E\n\u003Cli\u003ERepresents a single login\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022device-awareness\u0022\u003E\uD83D\uDCF1 Device Awareness\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth is device-aware by design:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EEach device gets its own chain\u003C/li\u003E\n\u003Cli\u003ESessions are grouped by device\u003C/li\u003E\n\u003Cli\u003ELogout can target device or all sessions\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022security-considerations\u0022\u003E\uD83D\uDD10 Security Considerations\u003C/h2\u003E\n\u003Cp\u003EThe login flow includes built-in protections:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EAccount lockout\u003C/li\u003E\n\u003Cli\u003EFailed attempt tracking\u003C/li\u003E\n\u003Cli\u003EDevice binding\u003C/li\u003E\n\u003Cli\u003ESecurity version validation\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Security decisions are centralized in the Authority\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Login = session creation\u003Cbr /\u003E\n\uD83D\uDC49 Not token issuance\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022key-takeaways\u0022\u003E\uD83D\uDCCC Key Takeaways\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003ELogin is a flow, not a function\u003C/li\u003E\n\u003Cli\u003EAuthority decides before any state change\u003C/li\u003E\n\u003Cli\u003ESessions are the source of truth\u003C/li\u003E\n\u003Cli\u003ETokens are optional representations\u003C/li\u003E\n\u003Cli\u003EDevice context is always considered\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003EContinue to \u003Cstrong\u003ERefresh Flow\u003C/strong\u003E\u003C/p\u003E\n",
+ "Headings": [
+ {
+ "Id": "what-is-login",
+ "Text": "\uD83E\uDDE0 What is Login?",
+ "Level": 0
+ },
+ {
+ "Id": "step-by-step-execution",
+ "Text": "\uD83D\uDD04 Step-by-Step Execution",
+ "Level": 0
+ },
+ {
+ "Id": "identifier-resolution",
+ "Text": "1\uFE0F\u20E3 Identifier Resolution",
+ "Level": 1
+ },
+ {
+ "Id": "user-security-state",
+ "Text": "2\uFE0F\u20E3 User \u0026 Security State",
+ "Level": 1
+ },
+ {
+ "Id": "credential-validation",
+ "Text": "3\uFE0F\u20E3 Credential Validation",
+ "Level": 1
+ },
+ {
+ "Id": "authority-decision",
+ "Text": "4\uFE0F\u20E3 Authority Decision",
+ "Level": 1
+ },
+ {
+ "Id": "device-chain-resolution",
+ "Text": "5\uFE0F\u20E3 Device \u0026 Chain Resolution",
+ "Level": 1
+ },
+ {
+ "Id": "session-creation",
+ "Text": "6\uFE0F\u20E3 Session Creation",
+ "Level": 1
+ },
+ {
+ "Id": "token-issuance-optional",
+ "Text": "7\uFE0F\u20E3 Token Issuance (Optional)",
+ "Level": 1
+ },
+ {
+ "Id": "event-dispatch",
+ "Text": "8\uFE0F\u20E3 Event Dispatch",
+ "Level": 1
+ },
+ {
+ "Id": "what-gets-created",
+ "Text": "\uD83E\uDDE9 What Gets Created?",
+ "Level": 0
+ },
+ {
+ "Id": "root",
+ "Text": "\uD83D\uDD39 Root",
+ "Level": 1
+ },
+ {
+ "Id": "chain",
+ "Text": "\uD83D\uDD39 Chain",
+ "Level": 1
+ },
+ {
+ "Id": "session",
+ "Text": "\uD83D\uDD39 Session",
+ "Level": 1
+ },
+ {
+ "Id": "device-awareness",
+ "Text": "\uD83D\uDCF1 Device Awareness",
+ "Level": 0
+ },
+ {
+ "Id": "security-considerations",
+ "Text": "\uD83D\uDD10 Security Considerations",
+ "Level": 0
+ },
+ {
+ "Id": "mental-model",
+ "Text": "\uD83E\uDDE0 Mental Model",
+ "Level": 0
+ },
+ {
+ "Id": "key-takeaways",
+ "Text": "\uD83D\uDCCC Key Takeaways",
+ "Level": 0
+ },
+ {
+ "Id": "next-step",
+ "Text": "\u27A1\uFE0F Next Step",
+ "Level": 0
+ }
+ ]
+}
\ No newline at end of file
diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/logout-flow.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/logout-flow.json
new file mode 100644
index 00000000..4d17bcda
--- /dev/null
+++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/logout-flow.json
@@ -0,0 +1,122 @@
+{
+ "Slug": "auth-flows/logout-flow",
+ "Title": "Logout",
+ "Html": "\n\u003Cp\u003EThe logout flow in UltimateAuth is not a single action.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 It represents different \u003Cstrong\u003Elevels of authentication termination\u003C/strong\u003E.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022what-is-logout\u0022\u003E\uD83E\uDDE0 What is Logout?\u003C/h2\u003E\n\u003Cp\u003EIn traditional systems:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELogout = remove cookie or token\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EIn UltimateAuth:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Logout affects \u003Cstrong\u003Esession, device, or identity scope\u003C/strong\u003E\u003C/p\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003ELogout is not just removing access\u003Cbr /\u003E\n\u2192 It is controlling session lifecycle\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022logout-vs-revoke\u0022\u003E\uD83D\uDD00 Logout vs Revoke\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth distinguishes between two concepts:\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022logout-soft-termination\u0022\u003E\uD83D\uDD39 Logout (Soft Termination)\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EEnds the current session\u003C/li\u003E\n\u003Cli\u003EKeeps the device (chain) active\u003C/li\u003E\n\u003Cli\u003EAllows re-login without resetting device context\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cpre\u003E\u003Ccode\u003ESession \u2192 Invalidated\nChain \u2192 Still Active\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 User can log in again and continue on the same device chain\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022revoke-hard-invalidation\u0022\u003E\uD83D\uDD25 Revoke (Hard Invalidation)\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EInvalidates session, chain, or root\u003C/li\u003E\n\u003Cli\u003ECannot be undone\u003C/li\u003E\n\u003Cli\u003EForces a completely new authentication path\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cpre\u003E\u003Ccode\u003EChain \u2192 Revoked\nSessions \u2192 Revoked\nNext login \u2192 New chain\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Revoke resets trust for that scope\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022levels-of-termination\u0022\u003E\uD83E\uDDE9 Levels of Termination\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth supports multiple logout scopes:\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022session-level-logout\u0022\u003E\uD83D\uDD39 Session-Level Logout\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ETerminates a single session\u003C/li\u003E\n\u003Cli\u003EOther sessions on the same device may remain\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022device-level-chain\u0022\u003E\uD83D\uDCF1 Device-Level (Chain)\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ETerminates all sessions on a device\u003C/li\u003E\n\u003Cli\u003EDevice chain is invalidated or reset\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022global-logout-all-devices\u0022\u003E\uD83C\uDF10 Global Logout (All Devices)\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ETerminates all sessions across all devices\u003C/li\u003E\n\u003Cli\u003EKeeps root (user identity) intact\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022root-revoke\u0022\u003E\uD83D\uDD25 Root Revoke\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EInvalidates entire authentication state\u003C/li\u003E\n\u003Cli\u003EAll chains and sessions are revoked\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This is the strongest possible action\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022step-by-step-execution\u0022\u003E\uD83D\uDD04 Step-by-Step Execution\u003C/h2\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022flow-context-resolution\u0022\u003E1\uFE0F\u20E3 Flow Context Resolution\u003C/h3\u003E\n\u003Cp\u003EThe system resolves:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ECurrent session\u003C/li\u003E\n\u003Cli\u003EUser identity\u003C/li\u003E\n\u003Cli\u003ETenant\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022authority-decision\u0022\u003E2\uFE0F\u20E3 Authority Decision\u003C/h3\u003E\n\u003Cp\u003ELogout operations are validated:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EAuthorization checks\u003C/li\u003E\n\u003Cli\u003EAccess validation\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Logout is not blindly executed\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022scope-determination\u0022\u003E3\uFE0F\u20E3 Scope Determination\u003C/h3\u003E\n\u003Cp\u003EThe system determines what to terminate:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession\u003C/li\u003E\n\u003Cli\u003EChain\u003C/li\u003E\n\u003Cli\u003ERoot\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022execution\u0022\u003E4\uFE0F\u20E3 Execution\u003C/h3\u003E\n\u003Cp\u003EDepending on scope:\u003C/p\u003E\n\u003Ch4 id=\u0022session-logout\u0022\u003ESession Logout\u003C/h4\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession is revoked\u003C/li\u003E\n\u003Cli\u003EOther sessions unaffected\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch4 id=\u0022chain-revoke-logout\u0022\u003EChain Revoke / Logout\u003C/h4\u003E\n\u003Cul\u003E\n\u003Cli\u003EAll sessions in the chain are revoked\u003C/li\u003E\n\u003Cli\u003EDevice trust is reset\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch4 id=\u0022global-logout\u0022\u003EGlobal Logout\u003C/h4\u003E\n\u003Cul\u003E\n\u003Cli\u003EAll chains are revoked (optionally excluding current)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch4 id=\u0022root-revoke-1\u0022\u003ERoot Revoke\u003C/h4\u003E\n\u003Cul\u003E\n\u003Cli\u003EEntire identity state is invalidated\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022event-dispatch\u0022\u003E5\uFE0F\u20E3 Event Dispatch\u003C/h3\u003E\n\u003Cp\u003EThe system emits:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELogout events\u003C/li\u003E\n\u003Cli\u003EAudit logs\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022device-awareness\u0022\u003E\uD83D\uDCF1 Device Awareness\u003C/h2\u003E\n\u003Cp\u003ELogout behavior is device-aware:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EEach device is a chain\u003C/li\u003E\n\u003Cli\u003ELogout can target specific devices\u003C/li\u003E\n\u003Cli\u003ESessions are grouped by device\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This enables fine-grained control\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022security-model\u0022\u003E\uD83D\uDD10 Security Model\u003C/h2\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022controlled-termination\u0022\u003E\uD83D\uDD12 Controlled Termination\u003C/h3\u003E\n\u003Cp\u003EAll logout operations:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EPass through orchestrator\u003C/li\u003E\n\u003Cli\u003EAre validated by authority\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Prevents unauthorized session manipulation\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022irreversible-revocation\u0022\u003E\uD83D\uDD01 Irreversible Revocation\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ERevoked chains cannot be restored\u003C/li\u003E\n\u003Cli\u003ERevoked sessions remain invalid\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Ensures strong security guarantees\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022identity-boundaries\u0022\u003E\uD83D\uDD17 Identity Boundaries\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession \u2192 temporary identity proof\u003C/li\u003E\n\u003Cli\u003EChain \u2192 device trust boundary\u003C/li\u003E\n\u003Cli\u003ERoot \u2192 global identity state\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Logout operates within these boundaries\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Logout = ending a session\u003Cbr /\u003E\n\uD83D\uDC49 Revoke = resetting trust\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022key-takeaways\u0022\u003E\uD83D\uDCCC Key Takeaways\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003ELogout and revoke are different operations\u003C/li\u003E\n\u003Cli\u003ELogout is reversible (via re-login)\u003C/li\u003E\n\u003Cli\u003ERevoke is permanent and forces new authentication\u003C/li\u003E\n\u003Cli\u003EDevice (chain) is a first-class concept\u003C/li\u003E\n\u003Cli\u003ESecurity is enforced through authority and orchestrator\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003EContinue to \u003Cstrong\u003ESession Lifecycle\u003C/strong\u003E\u003C/p\u003E\n",
+ "Headings": [
+ {
+ "Id": "what-is-logout",
+ "Text": "\uD83E\uDDE0 What is Logout?",
+ "Level": 0
+ },
+ {
+ "Id": "logout-vs-revoke",
+ "Text": "\uD83D\uDD00 Logout vs Revoke",
+ "Level": 0
+ },
+ {
+ "Id": "logout-soft-termination",
+ "Text": "\uD83D\uDD39 Logout (Soft Termination)",
+ "Level": 1
+ },
+ {
+ "Id": "revoke-hard-invalidation",
+ "Text": "\uD83D\uDD25 Revoke (Hard Invalidation)",
+ "Level": 1
+ },
+ {
+ "Id": "levels-of-termination",
+ "Text": "\uD83E\uDDE9 Levels of Termination",
+ "Level": 0
+ },
+ {
+ "Id": "session-level-logout",
+ "Text": "\uD83D\uDD39 Session-Level Logout",
+ "Level": 1
+ },
+ {
+ "Id": "device-level-chain",
+ "Text": "\uD83D\uDCF1 Device-Level (Chain)",
+ "Level": 1
+ },
+ {
+ "Id": "global-logout-all-devices",
+ "Text": "\uD83C\uDF10 Global Logout (All Devices)",
+ "Level": 1
+ },
+ {
+ "Id": "root-revoke",
+ "Text": "\uD83D\uDD25 Root Revoke",
+ "Level": 1
+ },
+ {
+ "Id": "step-by-step-execution",
+ "Text": "\uD83D\uDD04 Step-by-Step Execution",
+ "Level": 0
+ },
+ {
+ "Id": "flow-context-resolution",
+ "Text": "1\uFE0F\u20E3 Flow Context Resolution",
+ "Level": 1
+ },
+ {
+ "Id": "authority-decision",
+ "Text": "2\uFE0F\u20E3 Authority Decision",
+ "Level": 1
+ },
+ {
+ "Id": "scope-determination",
+ "Text": "3\uFE0F\u20E3 Scope Determination",
+ "Level": 1
+ },
+ {
+ "Id": "execution",
+ "Text": "4\uFE0F\u20E3 Execution",
+ "Level": 1
+ },
+ {
+ "Id": "event-dispatch",
+ "Text": "5\uFE0F\u20E3 Event Dispatch",
+ "Level": 1
+ },
+ {
+ "Id": "device-awareness",
+ "Text": "\uD83D\uDCF1 Device Awareness",
+ "Level": 0
+ },
+ {
+ "Id": "security-model",
+ "Text": "\uD83D\uDD10 Security Model",
+ "Level": 0
+ },
+ {
+ "Id": "controlled-termination",
+ "Text": "\uD83D\uDD12 Controlled Termination",
+ "Level": 1
+ },
+ {
+ "Id": "irreversible-revocation",
+ "Text": "\uD83D\uDD01 Irreversible Revocation",
+ "Level": 1
+ },
+ {
+ "Id": "identity-boundaries",
+ "Text": "\uD83D\uDD17 Identity Boundaries",
+ "Level": 1
+ },
+ {
+ "Id": "mental-model",
+ "Text": "\uD83E\uDDE0 Mental Model",
+ "Level": 0
+ },
+ {
+ "Id": "key-takeaways",
+ "Text": "\uD83D\uDCCC Key Takeaways",
+ "Level": 0
+ },
+ {
+ "Id": "next-step",
+ "Text": "\u27A1\uFE0F Next Step",
+ "Level": 0
+ }
+ ]
+}
\ No newline at end of file
diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/refresh-flow.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/refresh-flow.json
new file mode 100644
index 00000000..f0c9d34e
--- /dev/null
+++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/refresh-flow.json
@@ -0,0 +1,107 @@
+{
+ "Slug": "auth-flows/refresh-flow",
+ "Title": "Refresh",
+ "Html": "\n\u003Cp\u003EThe refresh flow in UltimateAuth is not a single fixed operation.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 It is a \u003Cstrong\u003Emode-dependent continuation strategy\u003C/strong\u003E.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022what-is-refresh\u0022\u003E\uD83E\uDDE0 What is Refresh?\u003C/h2\u003E\n\u003Cp\u003EIn traditional systems:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERefresh = get a new access token\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EIn UltimateAuth:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Refresh continues an existing authentication state\u003C/p\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003ERefresh is not re-authentication\u003Cbr /\u003E\n\u2192 It is session continuation\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022two-refresh-strategies\u0022\u003E\uD83D\uDD00 Two Refresh Strategies\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth supports two fundamentally different refresh behaviors:\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022session-touch-stateful\u0022\u003E\uD83D\uDD39 Session Touch (Stateful)\u003C/h3\u003E\n\u003Cp\u003EUsed in \u003Cstrong\u003EPureOpaque mode\u003C/strong\u003E\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ENo tokens involved\u003C/li\u003E\n\u003Cli\u003ENo new session created\u003C/li\u003E\n\u003Cli\u003EExisting session is extended\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cpre\u003E\u003Ccode\u003ESession \u2192 Validate \u2192 Touch \u2192 Continue\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 This updates activity without changing identity\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022token-rotation-stateless-hybrid\u0022\u003E\uD83D\uDD39 Token Rotation (Stateless / Hybrid)\u003C/h3\u003E\n\u003Cp\u003EUsed in:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u003Cp\u003EHybrid\u003C/p\u003E\n\u003C/li\u003E\n\u003Cli\u003E\u003Cp\u003ESemiHybrid\u003C/p\u003E\n\u003C/li\u003E\n\u003Cli\u003E\u003Cp\u003EPureJwt\u003C/p\u003E\n\u003C/li\u003E\n\u003Cli\u003E\u003Cp\u003ERefresh token is validated\u003C/p\u003E\n\u003C/li\u003E\n\u003Cli\u003E\u003Cp\u003EOld token is revoked\u003C/p\u003E\n\u003C/li\u003E\n\u003Cli\u003E\u003Cp\u003ENew tokens are issued\u003C/p\u003E\n\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cpre\u003E\u003Ccode\u003ERefreshToken \u2192 Validate \u2192 Revoke \u2192 Issue New Tokens\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 This ensures forward security\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022mode-based-behavior\u0022\u003E\u2696\uFE0F Mode-Based Behavior\u003C/h2\u003E\n\u003Cp\u003ERefresh behavior is determined by the authentication mode:\u003C/p\u003E\n\u003Ctable\u003E\n\u003Cthead\u003E\n\u003Ctr\u003E\n\u003Cth\u003EMode\u003C/th\u003E\n\u003Cth\u003EBehavior\u003C/th\u003E\n\u003C/tr\u003E\n\u003C/thead\u003E\n\u003Ctbody\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EPureOpaque\u003C/td\u003E\n\u003Ctd\u003ESession Touch\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EHybrid\u003C/td\u003E\n\u003Ctd\u003ERotation \u002B Touch\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003ESemiHybrid\u003C/td\u003E\n\u003Ctd\u003ERotation\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EPureJwt\u003C/td\u003E\n\u003Ctd\u003ERotation\u003C/td\u003E\n\u003C/tr\u003E\n\u003C/tbody\u003E\n\u003C/table\u003E\n\u003Cp\u003E\uD83D\uDC49 UltimateAuth automatically selects the correct strategy.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022step-by-step-execution\u0022\u003E\uD83D\uDD04 Step-by-Step Execution\u003C/h2\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022input-resolution\u0022\u003E1\uFE0F\u20E3 Input Resolution\u003C/h3\u003E\n\u003Cp\u003EThe system resolves:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESessionId (if present)\u003C/li\u003E\n\u003Cli\u003ERefreshToken (if present)\u003C/li\u003E\n\u003Cli\u003EDevice context\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022mode-based-branching\u0022\u003E2\uFE0F\u20E3 Mode-Based Branching\u003C/h3\u003E\n\u003Cp\u003EThe system determines the refresh strategy:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession-based \u2192 Touch\u003C/li\u003E\n\u003Cli\u003EToken-based \u2192 Rotation\u003C/li\u003E\n\u003Cli\u003EHybrid \u2192 Both\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022session-validation\u0022\u003E3\uFE0F\u20E3 Session Validation\u003C/h3\u003E\n\u003Cp\u003EIf session is involved:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession is validated\u003C/li\u003E\n\u003Cli\u003EDevice binding is checked\u003C/li\u003E\n\u003Cli\u003EExpiration is evaluated\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022token-validation-if-applicable\u0022\u003E4\uFE0F\u20E3 Token Validation (if applicable)\u003C/h3\u003E\n\u003Cp\u003EIf refresh token is used:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EToken is validated\u003C/li\u003E\n\u003Cli\u003ESession and chain are verified\u003C/li\u003E\n\u003Cli\u003EDevice consistency is checked\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022security-checks\u0022\u003E5\uFE0F\u20E3 Security Checks\u003C/h3\u003E\n\u003Cp\u003EThe system enforces:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EToken reuse detection\u003C/li\u003E\n\u003Cli\u003ESession validity\u003C/li\u003E\n\u003Cli\u003EChain integrity\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 If validation fails \u2192 reauthentication required\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022execution\u0022\u003E6\uFE0F\u20E3 Execution\u003C/h3\u003E\n\u003Cp\u003EDepending on the strategy:\u003C/p\u003E\n\u003Ch4 id=\u0022session-touch\u0022\u003ESession Touch\u003C/h4\u003E\n\u003Cul\u003E\n\u003Cli\u003EUpdates \u003Ccode\u003ELastSeenAt\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003EApplies sliding expiration\u003C/li\u003E\n\u003Cli\u003ENo new tokens issued\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch4 id=\u0022token-rotation\u0022\u003EToken Rotation\u003C/h4\u003E\n\u003Cul\u003E\n\u003Cli\u003ERevokes old refresh token\u003C/li\u003E\n\u003Cli\u003EIssues new access token\u003C/li\u003E\n\u003Cli\u003EIssues new refresh token\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch4 id=\u0022hybrid-mode\u0022\u003EHybrid Mode\u003C/h4\u003E\n\u003Cul\u003E\n\u003Cli\u003EValidates session\u003C/li\u003E\n\u003Cli\u003ERotates tokens\u003C/li\u003E\n\u003Cli\u003EUpdates session activity\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022response-generation\u0022\u003E7\uFE0F\u20E3 Response Generation\u003C/h3\u003E\n\u003Cp\u003EThe response may include:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESessionId\u003C/li\u003E\n\u003Cli\u003EAccess token\u003C/li\u003E\n\u003Cli\u003ERefresh token\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Output depends on mode and client\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022security-model\u0022\u003E\uD83D\uDD10 Security Model\u003C/h2\u003E\n\u003Cp\u003EThe refresh flow includes strong protections:\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022token-reuse-detection\u0022\u003E\uD83D\uDD01 Token Reuse Detection\u003C/h3\u003E\n\u003Cp\u003EIf a refresh token is reused:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EChain may be revoked\u003C/li\u003E\n\u003Cli\u003ESession may be revoked\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This prevents replay attacks\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022session-binding\u0022\u003E\uD83D\uDD17 Session Binding\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ETokens are bound to session\u003C/li\u003E\n\u003Cli\u003ESession is bound to device\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Prevents token misuse across devices\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022chain-integrity\u0022\u003E\uD83E\uDDEC Chain Integrity\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ERefresh operates within a chain\u003C/li\u003E\n\u003Cli\u003ECross-device usage is rejected\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Refresh = continuation\u003Cbr /\u003E\n\uD83D\uDC49 Not new authentication\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022key-takeaways\u0022\u003E\uD83D\uDCCC Key Takeaways\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003ERefresh behavior depends on auth mode\u003C/li\u003E\n\u003Cli\u003EStateful systems use session touch\u003C/li\u003E\n\u003Cli\u003EStateless systems use token rotation\u003C/li\u003E\n\u003Cli\u003EHybrid systems combine both\u003C/li\u003E\n\u003Cli\u003ESecurity is enforced at every step\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003EContinue to \u003Cstrong\u003ELogout Flow\u003C/strong\u003E\u003C/p\u003E\n",
+ "Headings": [
+ {
+ "Id": "what-is-refresh",
+ "Text": "\uD83E\uDDE0 What is Refresh?",
+ "Level": 0
+ },
+ {
+ "Id": "two-refresh-strategies",
+ "Text": "\uD83D\uDD00 Two Refresh Strategies",
+ "Level": 0
+ },
+ {
+ "Id": "session-touch-stateful",
+ "Text": "\uD83D\uDD39 Session Touch (Stateful)",
+ "Level": 1
+ },
+ {
+ "Id": "token-rotation-stateless-hybrid",
+ "Text": "\uD83D\uDD39 Token Rotation (Stateless / Hybrid)",
+ "Level": 1
+ },
+ {
+ "Id": "mode-based-behavior",
+ "Text": "\u2696\uFE0F Mode-Based Behavior",
+ "Level": 0
+ },
+ {
+ "Id": "step-by-step-execution",
+ "Text": "\uD83D\uDD04 Step-by-Step Execution",
+ "Level": 0
+ },
+ {
+ "Id": "input-resolution",
+ "Text": "1\uFE0F\u20E3 Input Resolution",
+ "Level": 1
+ },
+ {
+ "Id": "mode-based-branching",
+ "Text": "2\uFE0F\u20E3 Mode-Based Branching",
+ "Level": 1
+ },
+ {
+ "Id": "session-validation",
+ "Text": "3\uFE0F\u20E3 Session Validation",
+ "Level": 1
+ },
+ {
+ "Id": "token-validation-if-applicable",
+ "Text": "4\uFE0F\u20E3 Token Validation (if applicable)",
+ "Level": 1
+ },
+ {
+ "Id": "security-checks",
+ "Text": "5\uFE0F\u20E3 Security Checks",
+ "Level": 1
+ },
+ {
+ "Id": "execution",
+ "Text": "6\uFE0F\u20E3 Execution",
+ "Level": 1
+ },
+ {
+ "Id": "response-generation",
+ "Text": "7\uFE0F\u20E3 Response Generation",
+ "Level": 1
+ },
+ {
+ "Id": "security-model",
+ "Text": "\uD83D\uDD10 Security Model",
+ "Level": 0
+ },
+ {
+ "Id": "token-reuse-detection",
+ "Text": "\uD83D\uDD01 Token Reuse Detection",
+ "Level": 1
+ },
+ {
+ "Id": "session-binding",
+ "Text": "\uD83D\uDD17 Session Binding",
+ "Level": 1
+ },
+ {
+ "Id": "chain-integrity",
+ "Text": "\uD83E\uDDEC Chain Integrity",
+ "Level": 1
+ },
+ {
+ "Id": "mental-model",
+ "Text": "\uD83E\uDDE0 Mental Model",
+ "Level": 0
+ },
+ {
+ "Id": "key-takeaways",
+ "Text": "\uD83D\uDCCC Key Takeaways",
+ "Level": 0
+ },
+ {
+ "Id": "next-step",
+ "Text": "\u27A1\uFE0F Next Step",
+ "Level": 0
+ }
+ ]
+}
\ No newline at end of file
diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/session-lifecycle.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/session-lifecycle.json
new file mode 100644
index 00000000..96fc0cfb
--- /dev/null
+++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/session-lifecycle.json
@@ -0,0 +1,102 @@
+{
+ "Slug": "auth-flows/session-lifecycle",
+ "Title": "Session Lifecycle",
+ "Html": "\n\u003Cp\u003EUltimateAuth is built around a structured session model.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Authentication is not a token\u003Cbr /\u003E\n\uD83D\uDC49 It is a \u003Cstrong\u003Ehierarchical session system\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022core-model\u0022\u003E\uD83E\uDDE0 Core Model\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth defines three core entities:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003ERoot \u2192 Chain \u2192 Session\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022root-identity-authority\u0022\u003E\uD83D\uDD39 Root (Identity Authority)\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EOne per user\u003C/li\u003E\n\u003Cli\u003ERepresents global authentication state\u003C/li\u003E\n\u003Cli\u003EHolds security version\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Root defines \u003Cstrong\u003Ewho the user is\u003C/strong\u003E\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022chain-device-context\u0022\u003E\uD83D\uDCF1 Chain (Device Context)\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EOne per device\u003C/li\u003E\n\u003Cli\u003ERepresents a device-bound identity context\u003C/li\u003E\n\u003Cli\u003ETracks activity (LastSeenAt)\u003C/li\u003E\n\u003Cli\u003EManages session rotation and touch\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 A chain is a \u003Cstrong\u003Etrusted device boundary\u003C/strong\u003E\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022session-authentication-instance\u0022\u003E\uD83D\uDD11 Session (Authentication Instance)\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ECreated on login\u003C/li\u003E\n\u003Cli\u003ERepresents a single authentication event\u003C/li\u003E\n\u003Cli\u003EHas expiration and revocation state\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 A session is a \u003Cstrong\u003Eproof of authentication\u003C/strong\u003E\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022relationship\u0022\u003E\uD83D\uDD17 Relationship\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode\u003EUser\n\u2514\u2500\u2500 Root\n\u2514\u2500\u2500 Chain (Device)\n\u2514\u2500\u2500 Session (Instance)\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Each level adds more specificity:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERoot \u2192 identity\u003C/li\u003E\n\u003Cli\u003EChain \u2192 device\u003C/li\u003E\n\u003Cli\u003ESession \u2192 login instance\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022lifecycle-overview\u0022\u003E\uD83D\uDD04 Lifecycle Overview\u003C/h2\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022creation-login\u0022\u003E1\uFE0F\u20E3 Creation (Login)\u003C/h3\u003E\n\u003Cp\u003EWhen a user logs in:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERoot is created (if not exists)\u003C/li\u003E\n\u003Cli\u003EChain is resolved or created\u003C/li\u003E\n\u003Cli\u003ESession is issued\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022active-usage\u0022\u003E2\uFE0F\u20E3 Active Usage\u003C/h3\u003E\n\u003Cp\u003EDuring normal operation:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession is validated\u003C/li\u003E\n\u003Cli\u003EChain \u003Ccode\u003ELastSeenAt\u003C/code\u003E is updated (touch)\u003C/li\u003E\n\u003Cli\u003ESliding expiration may apply\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Activity updates the \u003Cstrong\u003Echain\u003C/strong\u003E, not just the session\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022refresh\u0022\u003E3\uFE0F\u20E3 Refresh\u003C/h3\u003E\n\u003Cp\u003EDepending on mode:\u003C/p\u003E\n\u003Ch4 id=\u0022session-based-pureopaque\u0022\u003ESession-Based (PureOpaque)\u003C/h4\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession remains\u003C/li\u003E\n\u003Cli\u003EChain is touched\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch4 id=\u0022token-based-hybrid-jwt\u0022\u003EToken-Based (Hybrid / JWT)\u003C/h4\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession continues\u003C/li\u003E\n\u003Cli\u003ETokens are rotated\u003C/li\u003E\n\u003Cli\u003EChain rotation count increases\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Chain tracks behavior:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERotationCount\u003C/li\u003E\n\u003Cli\u003ETouchCount\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022expiration\u0022\u003E4\uFE0F\u20E3 Expiration\u003C/h3\u003E\n\u003Cp\u003EA session may expire due to:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELifetime expiration\u003C/li\u003E\n\u003Cli\u003EIdle timeout\u003C/li\u003E\n\u003Cli\u003EAbsolute expiration\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Expired \u2260 revoked\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022revocation\u0022\u003E5\uFE0F\u20E3 Revocation\u003C/h3\u003E\n\u003Cp\u003ERevocation can occur at multiple levels:\u003C/p\u003E\n\u003Ch4 id=\u0022session-revocation\u0022\u003ESession Revocation\u003C/h4\u003E\n\u003Cul\u003E\n\u003Cli\u003ESingle session invalidated\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch4 id=\u0022chain-revocation\u0022\u003EChain Revocation\u003C/h4\u003E\n\u003Cul\u003E\n\u003Cli\u003EAll sessions on device invalidated\u003C/li\u003E\n\u003Cli\u003EDevice trust reset\u003C/li\u003E\n\u003C/ul\u003E\n\u003Chr /\u003E\n\u003Ch4 id=\u0022root-revocation\u0022\u003ERoot Revocation\u003C/h4\u003E\n\u003Cul\u003E\n\u003Cli\u003EAll chains and sessions invalidated\u003C/li\u003E\n\u003Cli\u003ESecurity version increased\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Revocation is irreversible\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022security-model\u0022\u003E\uD83D\uDD10 Security Model\u003C/h2\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022security-versioning\u0022\u003E\uD83D\uDD12 Security Versioning\u003C/h3\u003E\n\u003Cp\u003EEach root has:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u003Ccode\u003ESecurityVersion\u003C/code\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EEach session stores:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u003Ccode\u003ESecurityVersionAtCreation\u003C/code\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Chr /\u003E\n\u003Cp\u003E\uD83D\uDC49 If mismatch:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003ESession becomes invalid\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022device-binding\u0022\u003E\uD83D\uDD17 Device Binding\u003C/h3\u003E\n\u003Cp\u003EEach chain is tied to:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EDeviceId\u003C/li\u003E\n\u003Cli\u003EPlatform\u003C/li\u003E\n\u003Cli\u003EOS\u003C/li\u003E\n\u003Cli\u003EBrowser\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Prevents cross-device misuse\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022rotation-tracking\u0022\u003E\uD83D\uDD01 Rotation Tracking\u003C/h3\u003E\n\u003Cp\u003EChains track:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERotationCount\u003C/li\u003E\n\u003Cli\u003ETouchCount\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Enables:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Ereplay detection\u003C/li\u003E\n\u003Cli\u003Eanomaly tracking\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022lifecycle-configuration\u0022\u003E\u2699\uFE0F Lifecycle Configuration\u003C/h3\u003E\n\u003Cp\u003ESession behavior is configurable:\u003C/p\u003E\n\u003Cp\u003E\u23F1 Lifetime\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EDefault session duration\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDD04 Sliding Expiration\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EExtends session on activity\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDCA4 Idle Timeout\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EInvalidates inactive sessions\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDCF1 Device Limits\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EMax chains per user\u003C/li\u003E\n\u003Cli\u003EMax sessions per chain\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 These are defined via UAuthSessionOptions\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Authentication is a living structure\n\uD83D\uDC49 Not a static token\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022key-takeaways\u0022\u003E\uD83D\uDCCC Key Takeaways\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003ESessions are part of a hierarchy\u003C/li\u003E\n\u003Cli\u003EDevice (chain) is a first-class concept\u003C/li\u003E\n\u003Cli\u003ERoot controls global security\u003C/li\u003E\n\u003Cli\u003ESessions are short-lived proofs\u003C/li\u003E\n\u003Cli\u003EChains manage lifecycle and activity\u003C/li\u003E\n\u003Cli\u003ERevocation operates at multiple levels\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003EContinue to Token Behavior\u003C/p\u003E\n",
+ "Headings": [
+ {
+ "Id": "core-model",
+ "Text": "\uD83E\uDDE0 Core Model",
+ "Level": 0
+ },
+ {
+ "Id": "root-identity-authority",
+ "Text": "\uD83D\uDD39 Root (Identity Authority)",
+ "Level": 1
+ },
+ {
+ "Id": "chain-device-context",
+ "Text": "\uD83D\uDCF1 Chain (Device Context)",
+ "Level": 1
+ },
+ {
+ "Id": "session-authentication-instance",
+ "Text": "\uD83D\uDD11 Session (Authentication Instance)",
+ "Level": 1
+ },
+ {
+ "Id": "relationship",
+ "Text": "\uD83D\uDD17 Relationship",
+ "Level": 0
+ },
+ {
+ "Id": "lifecycle-overview",
+ "Text": "\uD83D\uDD04 Lifecycle Overview",
+ "Level": 0
+ },
+ {
+ "Id": "creation-login",
+ "Text": "1\uFE0F\u20E3 Creation (Login)",
+ "Level": 1
+ },
+ {
+ "Id": "active-usage",
+ "Text": "2\uFE0F\u20E3 Active Usage",
+ "Level": 1
+ },
+ {
+ "Id": "refresh",
+ "Text": "3\uFE0F\u20E3 Refresh",
+ "Level": 1
+ },
+ {
+ "Id": "expiration",
+ "Text": "4\uFE0F\u20E3 Expiration",
+ "Level": 1
+ },
+ {
+ "Id": "revocation",
+ "Text": "5\uFE0F\u20E3 Revocation",
+ "Level": 1
+ },
+ {
+ "Id": "security-model",
+ "Text": "\uD83D\uDD10 Security Model",
+ "Level": 0
+ },
+ {
+ "Id": "security-versioning",
+ "Text": "\uD83D\uDD12 Security Versioning",
+ "Level": 1
+ },
+ {
+ "Id": "device-binding",
+ "Text": "\uD83D\uDD17 Device Binding",
+ "Level": 1
+ },
+ {
+ "Id": "rotation-tracking",
+ "Text": "\uD83D\uDD01 Rotation Tracking",
+ "Level": 1
+ },
+ {
+ "Id": "lifecycle-configuration",
+ "Text": "\u2699\uFE0F Lifecycle Configuration",
+ "Level": 1
+ },
+ {
+ "Id": "mental-model",
+ "Text": "\uD83E\uDDE0 Mental Model",
+ "Level": 0
+ },
+ {
+ "Id": "key-takeaways",
+ "Text": "\uD83D\uDCCC Key Takeaways",
+ "Level": 0
+ },
+ {
+ "Id": "next-step",
+ "Text": "\u27A1\uFE0F Next Step",
+ "Level": 0
+ }
+ ]
+}
\ No newline at end of file
diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/token-behavior.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/token-behavior.json
new file mode 100644
index 00000000..cc2615db
--- /dev/null
+++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/token-behavior.json
@@ -0,0 +1,117 @@
+{
+ "Slug": "auth-flows/token-behavior",
+ "Title": "Token Behavior",
+ "Html": "\n\u003Cp\u003EIn UltimateAuth, tokens are not the foundation of authentication.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 They are \u003Cstrong\u003Ederived artifacts of a session\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022rethinking-tokens\u0022\u003E\uD83E\uDDE0 Rethinking Tokens\u003C/h2\u003E\n\u003Cp\u003EIn traditional systems:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EToken = identity\u003C/li\u003E\n\u003Cli\u003EToken = authentication\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EIn UltimateAuth:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Session = identity\u003Cbr /\u003E\n\uD83D\uDC49 Token = transport mechanism\u003C/p\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003ETokens do not define identity\u003Cbr /\u003E\n\u2192 Sessions do\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022token-types\u0022\u003E\uD83E\uDDE9 Token Types\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth supports two main token types:\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022opaque-tokens\u0022\u003E\uD83D\uDD39 Opaque Tokens\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ERandom, non-decodable values\u003C/li\u003E\n\u003Cli\u003EStored and validated on the server\u003C/li\u003E\n\u003Cli\u003ETypically reference a session\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Used in:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EPureOpaque\u003C/li\u003E\n\u003Cli\u003EHybrid\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022jwt-json-web-tokens\u0022\u003E\uD83D\uDD39 JWT (JSON Web Tokens)\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ESelf-contained tokens\u003C/li\u003E\n\u003Cli\u003EInclude claims and metadata\u003C/li\u003E\n\u003Cli\u003ESigned and verifiable without server lookup\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Used in:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESemiHybrid\u003C/li\u003E\n\u003Cli\u003EPureJwt\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022mode-based-behavior\u0022\u003E\u2696\uFE0F Mode-Based Behavior\u003C/h2\u003E\n\u003Cp\u003EToken behavior depends on the authentication mode:\u003C/p\u003E\n\u003Ctable\u003E\n\u003Cthead\u003E\n\u003Ctr\u003E\n\u003Cth\u003EMode\u003C/th\u003E\n\u003Cth\u003EAccess Token\u003C/th\u003E\n\u003Cth\u003ERefresh Token\u003C/th\u003E\n\u003Cth\u003EBehavior\u003C/th\u003E\n\u003C/tr\u003E\n\u003C/thead\u003E\n\u003Ctbody\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EPureOpaque\u003C/td\u003E\n\u003Ctd\u003E\u274C\u003C/td\u003E\n\u003Ctd\u003E\u274C\u003C/td\u003E\n\u003Ctd\u003ESession-only\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EHybrid\u003C/td\u003E\n\u003Ctd\u003E\u2714 (opaque/JWT)\u003C/td\u003E\n\u003Ctd\u003E\u2714\u003C/td\u003E\n\u003Ctd\u003ESession \u002B token\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003ESemiHybrid\u003C/td\u003E\n\u003Ctd\u003E\u2714 (JWT)\u003C/td\u003E\n\u003Ctd\u003E\u2714\u003C/td\u003E\n\u003Ctd\u003EJWT \u002B session metadata\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EPureJwt\u003C/td\u003E\n\u003Ctd\u003E\u2714 (JWT)\u003C/td\u003E\n\u003Ctd\u003E\u2714\u003C/td\u003E\n\u003Ctd\u003EFully stateless\u003C/td\u003E\n\u003C/tr\u003E\n\u003C/tbody\u003E\n\u003C/table\u003E\n\u003Cp\u003E\uD83D\uDC49 UltimateAuth selects behavior automatically\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022access-tokens\u0022\u003E\uD83D\uDD11 Access Tokens\u003C/h2\u003E\n\u003Cp\u003EAccess tokens represent:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 A \u003Cstrong\u003Etemporary access grant\u003C/strong\u003E\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022characteristics\u0022\u003ECharacteristics\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EShort-lived\u003C/li\u003E\n\u003Cli\u003EMode-dependent format\u003C/li\u003E\n\u003Cli\u003EMay contain session reference (\u003Ccode\u003Esid\u003C/code\u003E)\u003C/li\u003E\n\u003Cli\u003EMay include claims\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022important\u0022\u003EImportant\u003C/h3\u003E\n\u003Cp\u003EAccess token is NOT the source of truth.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 It reflects session state, not replaces it\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022refresh-tokens\u0022\u003E\uD83D\uDD04 Refresh Tokens\u003C/h2\u003E\n\u003Cp\u003ERefresh tokens represent:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 A \u003Cstrong\u003Econtinuation mechanism\u003C/strong\u003E\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022characteristics-1\u0022\u003ECharacteristics\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ELong-lived\u003C/li\u003E\n\u003Cli\u003EStored as hashed values\u003C/li\u003E\n\u003Cli\u003EBound to session and optionally chain\u003C/li\u003E\n\u003Cli\u003ERotated on use\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022lifecycle\u0022\u003ELifecycle\u003C/h3\u003E\n\u003Cp\u003EIssued \u2192 Used \u2192 Replaced \u2192 Revoked\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Old tokens are invalidated on rotation\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022security-model\u0022\u003E\uD83D\uDD10 Security Model\u003C/h2\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022rotation\u0022\u003E\uD83D\uDD01 Rotation\u003C/h3\u003E\n\u003Cp\u003EEach refresh:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EInvalidates previous token\u003C/li\u003E\n\u003Cli\u003EIssues a new token\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Prevents replay attacks\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022reuse-detection\u0022\u003E\uD83D\uDEA8 Reuse Detection\u003C/h3\u003E\n\u003Cp\u003EIf a token is reused:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EChain may be revoked\u003C/li\u003E\n\u003Cli\u003ESession may be revoked\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Strong forward security\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022session-binding\u0022\u003E\uD83D\uDD17 Session Binding\u003C/h3\u003E\n\u003Cp\u003ERefresh tokens include:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESessionId\u003C/li\u003E\n\u003Cli\u003EChainId (optional)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Prevents cross-context usage\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022hashed-storage\u0022\u003E\uD83D\uDD12 Hashed Storage\u003C/h3\u003E\n\u003Cp\u003ETokens are:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ENever stored as plaintext\u003C/li\u003E\n\u003Cli\u003EHashed using secure algorithms\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Reduces breach impact\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022token-issuance\u0022\u003E\uD83D\uDD04 Token Issuance\u003C/h2\u003E\n\u003Cp\u003ETokens are issued during:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELogin\u003C/li\u003E\n\u003Cli\u003ERefresh\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022access-token\u0022\u003EAccess Token\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EMay be opaque or JWT\u003C/li\u003E\n\u003Cli\u003EIncludes identity and optional session reference\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022refresh-token\u0022\u003ERefresh Token\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EAlways opaque\u003C/li\u003E\n\u003Cli\u003EPersisted in secure store\u003C/li\u003E\n\u003Cli\u003EUsed only for rotation\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Tokens are not identity\u003Cbr /\u003E\n\uD83D\uDC49 They are projections of a session\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022key-takeaways\u0022\u003E\uD83D\uDCCC Key Takeaways\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession is the source of truth\u003C/li\u003E\n\u003Cli\u003ETokens are optional and mode-dependent\u003C/li\u003E\n\u003Cli\u003EOpaque tokens require server validation\u003C/li\u003E\n\u003Cli\u003EJWT tokens allow stateless access\u003C/li\u003E\n\u003Cli\u003ERefresh tokens enable secure continuation\u003C/li\u003E\n\u003Cli\u003EToken rotation ensures forward security\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003EContinue to Device Management\u003C/p\u003E\n",
+ "Headings": [
+ {
+ "Id": "rethinking-tokens",
+ "Text": "\uD83E\uDDE0 Rethinking Tokens",
+ "Level": 0
+ },
+ {
+ "Id": "token-types",
+ "Text": "\uD83E\uDDE9 Token Types",
+ "Level": 0
+ },
+ {
+ "Id": "opaque-tokens",
+ "Text": "\uD83D\uDD39 Opaque Tokens",
+ "Level": 1
+ },
+ {
+ "Id": "jwt-json-web-tokens",
+ "Text": "\uD83D\uDD39 JWT (JSON Web Tokens)",
+ "Level": 1
+ },
+ {
+ "Id": "mode-based-behavior",
+ "Text": "\u2696\uFE0F Mode-Based Behavior",
+ "Level": 0
+ },
+ {
+ "Id": "access-tokens",
+ "Text": "\uD83D\uDD11 Access Tokens",
+ "Level": 0
+ },
+ {
+ "Id": "characteristics",
+ "Text": "Characteristics",
+ "Level": 1
+ },
+ {
+ "Id": "important",
+ "Text": "Important",
+ "Level": 1
+ },
+ {
+ "Id": "refresh-tokens",
+ "Text": "\uD83D\uDD04 Refresh Tokens",
+ "Level": 0
+ },
+ {
+ "Id": "characteristics-1",
+ "Text": "Characteristics",
+ "Level": 1
+ },
+ {
+ "Id": "lifecycle",
+ "Text": "Lifecycle",
+ "Level": 1
+ },
+ {
+ "Id": "security-model",
+ "Text": "\uD83D\uDD10 Security Model",
+ "Level": 0
+ },
+ {
+ "Id": "rotation",
+ "Text": "\uD83D\uDD01 Rotation",
+ "Level": 1
+ },
+ {
+ "Id": "reuse-detection",
+ "Text": "\uD83D\uDEA8 Reuse Detection",
+ "Level": 1
+ },
+ {
+ "Id": "session-binding",
+ "Text": "\uD83D\uDD17 Session Binding",
+ "Level": 1
+ },
+ {
+ "Id": "hashed-storage",
+ "Text": "\uD83D\uDD12 Hashed Storage",
+ "Level": 1
+ },
+ {
+ "Id": "token-issuance",
+ "Text": "\uD83D\uDD04 Token Issuance",
+ "Level": 0
+ },
+ {
+ "Id": "access-token",
+ "Text": "Access Token",
+ "Level": 1
+ },
+ {
+ "Id": "refresh-token",
+ "Text": "Refresh Token",
+ "Level": 1
+ },
+ {
+ "Id": "mental-model",
+ "Text": "\uD83E\uDDE0 Mental Model",
+ "Level": 0
+ },
+ {
+ "Id": "key-takeaways",
+ "Text": "\uD83D\uDCCC Key Takeaways",
+ "Level": 0
+ },
+ {
+ "Id": "next-step",
+ "Text": "\u27A1\uFE0F Next Step",
+ "Level": 0
+ }
+ ]
+}
\ No newline at end of file
diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/authentication.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/authentication.json
new file mode 100644
index 00000000..861594bd
--- /dev/null
+++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/authentication.json
@@ -0,0 +1,72 @@
+{
+ "Slug": "client/authentication",
+ "Title": "Authentication",
+ "Html": "\n\u003Cp\u003EThis section explains how to use the UltimateAuth client for authentication flows.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022overview\u0022\u003E\uD83E\uDDE0 Overview\u003C/h2\u003E\n\u003Cp\u003EAuthentication in UltimateAuth is \u003Cstrong\u003Eflow-based\u003C/strong\u003E, not endpoint-based.\u003C/p\u003E\n\u003Cp\u003EYou interact with:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Ccode\u003EFlowClient\u003C/code\u003E\u003C/p\u003E\n\u003Chr /\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022login\u0022\u003E\uD83D\uDD11 Login\u003C/h2\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022basic-login\u0022\u003EBasic Login\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Flows.LoginAsync(new LoginRequest\n{\n Identifier = \u0026quot;user@ultimateauth.com\u0026quot;,\n Secret = \u0026quot;password\u0026quot;\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 This triggers a full login flow:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESends credentials\u003C/li\u003E\n\u003Cli\u003EHandles redirect\u003C/li\u003E\n\u003Cli\u003EEstablishes session\u003C/li\u003E\n\u003C/ul\u003E\n\u003Chr /\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022try-login-pre-check\u0022\u003E\u26A1 Try Login (Pre-check)\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Evar result = await UAuthClient.Flows.TryLoginAsync(\n new LoginRequest\n {\n Identifier = \u0026quot;user@mail.com\u0026quot;,\n Secret = \u0026quot;password\u0026quot;\n },\n UAuthSubmitMode.TryOnly\n);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022modes\u0022\u003EModes\u003C/h3\u003E\n\u003Ctable\u003E\n\u003Cthead\u003E\n\u003Ctr\u003E\n\u003Cth\u003EMode\u003C/th\u003E\n\u003Cth\u003EBehavior\u003C/th\u003E\n\u003C/tr\u003E\n\u003C/thead\u003E\n\u003Ctbody\u003E\n\u003Ctr\u003E\n\u003Ctd\u003ETryOnly\u003C/td\u003E\n\u003Ctd\u003EValidate only\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EDirectCommit\u003C/td\u003E\n\u003Ctd\u003ESkip validation\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003ETryAndCommit\u003C/td\u003E\n\u003Ctd\u003EValidate then login if success\u003C/td\u003E\n\u003C/tr\u003E\n\u003C/tbody\u003E\n\u003C/table\u003E\n\u003Cp\u003E\uD83D\uDC49 Use \u003Ccode\u003EDirectCommit\u003C/code\u003E when:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EYou need maximum performance while sacrificing interactive SPA capabilities.\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Use \u003Ccode\u003ETryOnly\u003C/code\u003E when:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EYou need validation feedback\u003C/li\u003E\n\u003Cli\u003EYou want custom UI flows\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Use \u003Ccode\u003ETryAndCommit\u003C/code\u003E when:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EYou need completely interactive SPA experience.\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Ccode\u003ETryAndCommit\u003C/code\u003E is the recommended mode for most applications.\u003C/p\u003E\n\u003Cp\u003EIt provides:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EValidation feedback\u003C/li\u003E\n\u003Cli\u003EAutomatic redirect on success\u003C/li\u003E\n\u003Cli\u003ESmooth SPA experience\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022refresh\u0022\u003E\uD83D\uDD04 Refresh\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Evar result = await UAuthClient.Flows.RefreshAsync();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022possible-outcomes\u0022\u003EPossible Outcomes\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ESuccess \u2192 new tokens/session updated\u003C/li\u003E\n\u003Cli\u003ETouched \u2192 session extended\u003C/li\u003E\n\u003Cli\u003ERotated \u2192 refresh token rotated\u003C/li\u003E\n\u003Cli\u003ENoOp \u2192 nothing changed\u003C/li\u003E\n\u003Cli\u003EReauthRequired \u2192 login required\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Refresh behavior depends on auth mode:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EPureOpaque \u2192 session touch\u003C/li\u003E\n\u003Cli\u003EHybrid/JWT \u2192 token rotation\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EIn 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:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Ebuilder.Services.AddUltimateAuthServer(o =\u0026gt;\n{\n o.Diagnostics.EnableRefreshDetails = true;\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022logout\u0022\u003E\uD83D\uDEAA Logout\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Flows.LogoutAsync();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 This:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EEnds current session\u003C/li\u003E\n\u003Cli\u003EClears authentication state\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022device-logout-variants\u0022\u003E\uD83D\uDCF1 Device Logout Variants\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Flows.LogoutMyDeviceAsync(sessionId);\nawait UAuthClient.Flows.LogoutMyOtherDevicesAsync();\nawait UAuthClient.Flows.LogoutAllMyDevicesAsync();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 These operate on \u003Cstrong\u003Esession chains (devices)\u003C/strong\u003E\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022pkce-flow-public-clients\u0022\u003E\uD83D\uDD10 PKCE Flow (Public Clients)\u003C/h2\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022start-pkce\u0022\u003EStart PKCE\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Flows.BeginPkceAsync();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022complete-pkce\u0022\u003EComplete PKCE\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Flows.CompletePkceLoginAsync(request);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003EComplete PKCE also has try semantics the same as login flow. UltimateAuth suggests to use \u003Ccode\u003ETryCompletePkceLoginAsync\u003C/code\u003E for complete interactive experience.\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Cp\u003E\uD83D\uDC49 Required for:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESPA\u003C/li\u003E\n\u003Cli\u003EBlazor WASM\u003C/li\u003E\n\u003Cli\u003EMobile apps\u003C/li\u003E\n\u003C/ul\u003E\n\u003Chr /\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022security-note\u0022\u003E\uD83D\uDEA8 Security Note\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EPublic clients MUST use PKCE\u003C/li\u003E\n\u003Cli\u003EServer clients MAY allow direct login\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EDirect 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.\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Ebuilder.Services.AddUltimateAuthClientBlazor(o =\u0026gt;\n{\n o.Login.AllowCredentialPost = true;\n});\n---\n\n## \uD83C\uDFAF Summary\n\nAuthentication in UltimateAuth:\n\n- is flow-driven\n- adapts to client type\n- enforces security by design\n\n---\n\n\uD83D\uDC49 Always use \u0060FlowClient\u0060 for authentication operations\n\u003C/code\u003E\u003C/pre\u003E\n",
+ "Headings": [
+ {
+ "Id": "overview",
+ "Text": "\uD83E\uDDE0 Overview",
+ "Level": 0
+ },
+ {
+ "Id": "login",
+ "Text": "\uD83D\uDD11 Login",
+ "Level": 0
+ },
+ {
+ "Id": "basic-login",
+ "Text": "Basic Login",
+ "Level": 1
+ },
+ {
+ "Id": "try-login-pre-check",
+ "Text": "\u26A1 Try Login (Pre-check)",
+ "Level": 0
+ },
+ {
+ "Id": "modes",
+ "Text": "Modes",
+ "Level": 1
+ },
+ {
+ "Id": "refresh",
+ "Text": "\uD83D\uDD04 Refresh",
+ "Level": 0
+ },
+ {
+ "Id": "possible-outcomes",
+ "Text": "Possible Outcomes",
+ "Level": 1
+ },
+ {
+ "Id": "logout",
+ "Text": "\uD83D\uDEAA Logout",
+ "Level": 0
+ },
+ {
+ "Id": "device-logout-variants",
+ "Text": "\uD83D\uDCF1 Device Logout Variants",
+ "Level": 0
+ },
+ {
+ "Id": "pkce-flow-public-clients",
+ "Text": "\uD83D\uDD10 PKCE Flow (Public Clients)",
+ "Level": 0
+ },
+ {
+ "Id": "start-pkce",
+ "Text": "Start PKCE",
+ "Level": 1
+ },
+ {
+ "Id": "complete-pkce",
+ "Text": "Complete PKCE",
+ "Level": 1
+ },
+ {
+ "Id": "security-note",
+ "Text": "\uD83D\uDEA8 Security Note",
+ "Level": 0
+ }
+ ]
+}
\ No newline at end of file
diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/authorization.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/authorization.json
new file mode 100644
index 00000000..9d9d909b
--- /dev/null
+++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/authorization.json
@@ -0,0 +1,72 @@
+{
+ "Slug": "client/authorization",
+ "Title": "Authorization",
+ "Html": "\n\u003Cp\u003EThis section explains how to manage roles, permissions, and access control using the UltimateAuth client.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022overview\u0022\u003E\uD83E\uDDE0 Overview\u003C/h2\u003E\n\u003Cp\u003EAuthorization in UltimateAuth is policy-driven and role-based.\u003C/p\u003E\n\u003Cp\u003EOn the client, you interact with:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003EUAuthClient.Authorization\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022core-concepts\u0022\u003E\uD83D\uDD11 Core Concepts\u003C/h2\u003E\n\u003Cp\u003ERoles\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ENamed groups of permissions\u003C/li\u003E\n\u003Cli\u003EAssigned to users\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EPermissions\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EFine-grained access definitions\u003C/li\u003E\n\u003Cli\u003EExample: users.create.anonymous, users.delete.self, authorization.roles.admin\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EPolicies\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERuntime decision rules\u003C/li\u003E\n\u003Cli\u003EEnforced automatically on server\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022query-roles\u0022\u003E\uD83D\uDCCB Query Roles\u003C/h3\u003E\n\u003Cp\u003Evar result = await UAuthClient.Authorization.QueryRolesAsync(new RoleQuery\n);\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022create-role\u0022\u003E\u2795 Create Role\u003C/h3\u003E\n\u003Cp\u003Eawait UAuthClient.Authorization.CreateRoleAsync(new CreateRoleRequest\n);\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022rename-role\u0022\u003E\u270F\uFE0F Rename Role\u003C/h3\u003E\n\u003Cp\u003Eawait UAuthClient.Authorization.RenameRoleAsync(new RenameRoleRequest\n);\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022set-permissions\u0022\u003E\uD83E\uDDE9 Set Permissions\u003C/h3\u003E\n\u003Cp\u003Eawait UAuthClient.Authorization.SetRolePermissionsAsync(new SetRolePermissionsRequest\n{\nRoleId = roleId,\nPermissions = new[]\n{\nPermission.From(\u0026quot;users.read\u0026quot;),\nPermission.From(\u0026quot;users.update\u0026quot;)\n}\n});\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Permissions support:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EExact match \u2192 users.create\u003C/li\u003E\n\u003Cli\u003EPrefix \u2192 users.*\u003C/li\u003E\n\u003Cli\u003EWildcard \u2192 *\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022delete-role\u0022\u003E\u274C Delete Role\u003C/h3\u003E\n\u003Cp\u003Eawait UAuthClient.Authorization.DeleteRoleAsync(new DeleteRoleRequest\n);\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Automatically removes role assignments from users\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022assign-role-to-user\u0022\u003E\uD83D\uDC64 Assign Role to User\u003C/h3\u003E\n\u003Cp\u003Eawait UAuthClient.Authorization.AssignRoleToUserAsync(new AssignRoleRequest\n);\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022remove-role\u0022\u003E\u2796 Remove Role\u003C/h3\u003E\n\u003Cp\u003Eawait UAuthClient.Authorization.RemoveRoleFromUserAsync(new RemoveRoleRequest\n);\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022get-user-roles\u0022\u003E\uD83D\uDCCB Get User Roles\u003C/h3\u003E\n\u003Cp\u003Evar roles = await UAuthClient.Authorization.GetUserRolesAsync(userKey);\n\uD83D\uDD0D Check Authorization\nvar result = await UAuthClient.Authorization.CheckAsync(new AuthorizationCheckRequest\n);\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Returns:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EAllow / Deny\u003C/li\u003E\n\u003Cli\u003EReason (if denied)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022permission-model\u0022\u003E\uD83E\uDDE0 Permission Model\u003C/h2\u003E\n\u003Cp\u003EPermissions are normalized and optimized:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EFull access \u2192 *\u003C/li\u003E\n\u003Cli\u003EGroup access \u2192 users.*\u003C/li\u003E\n\u003Cli\u003ESpecific \u2192 users.create\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Internally compiled for fast evaluation\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022security-notes\u0022\u003E\uD83D\uDD10 Security Notes\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EAuthorization is enforced server-side\u003C/li\u003E\n\u003Cli\u003EClient only requests actions\u003C/li\u003E\n\u003Cli\u003EPolicies may override permissions\u003C/li\u003E\n\u003Cli\u003ECross-tenant access is denied by default\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022summary\u0022\u003E\uD83C\uDFAF Summary\u003C/h2\u003E\n\u003Cp\u003EAuthorization in UltimateAuth:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Ecombines roles \u002B permissions \u002B policies\u003C/li\u003E\n\u003Cli\u003Eis evaluated through a decision pipeline\u003C/li\u003E\n\u003Cli\u003Esupports fine-grained and scalable access control\u003C/li\u003E\n\u003C/ul\u003E\n",
+ "Headings": [
+ {
+ "Id": "overview",
+ "Text": "\uD83E\uDDE0 Overview",
+ "Level": 0
+ },
+ {
+ "Id": "core-concepts",
+ "Text": "\uD83D\uDD11 Core Concepts",
+ "Level": 0
+ },
+ {
+ "Id": "query-roles",
+ "Text": "\uD83D\uDCCB Query Roles",
+ "Level": 1
+ },
+ {
+ "Id": "create-role",
+ "Text": "\u2795 Create Role",
+ "Level": 1
+ },
+ {
+ "Id": "rename-role",
+ "Text": "\u270F\uFE0F Rename Role",
+ "Level": 1
+ },
+ {
+ "Id": "set-permissions",
+ "Text": "\uD83E\uDDE9 Set Permissions",
+ "Level": 1
+ },
+ {
+ "Id": "delete-role",
+ "Text": "\u274C Delete Role",
+ "Level": 1
+ },
+ {
+ "Id": "assign-role-to-user",
+ "Text": "\uD83D\uDC64 Assign Role to User",
+ "Level": 1
+ },
+ {
+ "Id": "remove-role",
+ "Text": "\u2796 Remove Role",
+ "Level": 1
+ },
+ {
+ "Id": "get-user-roles",
+ "Text": "\uD83D\uDCCB Get User Roles",
+ "Level": 1
+ },
+ {
+ "Id": "permission-model",
+ "Text": "\uD83E\uDDE0 Permission Model",
+ "Level": 0
+ },
+ {
+ "Id": "security-notes",
+ "Text": "\uD83D\uDD10 Security Notes",
+ "Level": 0
+ },
+ {
+ "Id": "summary",
+ "Text": "\uD83C\uDFAF Summary",
+ "Level": 0
+ }
+ ]
+}
\ No newline at end of file
diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/credentials.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/credentials.json
new file mode 100644
index 00000000..429aa532
--- /dev/null
+++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/credentials.json
@@ -0,0 +1,92 @@
+{
+ "Slug": "client/credentials",
+ "Title": "Credentials",
+ "Html": "\n\u003Cp\u003EThis section explains how to manage user credentials (such as passwords) using the UltimateAuth client.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022overview\u0022\u003E\uD83E\uDDE0 Overview\u003C/h2\u003E\n\u003Cp\u003ECredential operations are handled via:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003EUAuthClient.Credentials...\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EThis includes:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Epassword management\u003C/li\u003E\n\u003Cli\u003Ecredential reset flows\u003C/li\u003E\n\u003Cli\u003Eadmin credential operations\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022change-password-self\u0022\u003E\uD83D\uDD10 Change Password (Self)\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Credentials.ChangeMyAsync(new ChangeCredentialRequest\n{\n CurrentSecret = \u0026quot;old-password\u0026quot;,\n NewSecret = \u0026quot;new-password\u0026quot;\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Requires current password\u003Cbr /\u003E\n\uD83D\uDC49 Triggers \u003Ccode\u003ECredentialsChangedSelf\u003C/code\u003E event\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022reset-password-self\u0022\u003E\uD83D\uDD01 Reset Password (Self)\u003C/h2\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022begin-reset\u0022\u003EBegin Reset\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Evar begin = await UAuthClient.Credentials.BeginResetMyAsync(\n new BeginResetCredentialRequest\n {\n Identifier = \u0026quot;user@mail.com\u0026quot;\n });\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022complete-reset\u0022\u003EComplete Reset\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Credentials.CompleteResetMyAsync(\n new CompleteResetCredentialRequest\n {\n Token = \u0026quot;reset-token\u0026quot;,\n NewSecret = \u0026quot;new-password\u0026quot;\n });\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Typically used in:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eforgot password flows\u003C/li\u003E\n\u003Cli\u003Eemail-based reset flows\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022add-credential-self\u0022\u003E\u2795 Add Credential (Self)\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Credentials.AddMyAsync(new AddCredentialRequest\n{\n Secret = \u0026quot;password\u0026quot;\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022revoke-credential-self\u0022\u003E\u274C Revoke Credential (Self)\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Credentials.RevokeMyAsync(new RevokeCredentialRequest\n{\n CredentialId = credentialId\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Useful for:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eremoving login methods\u003C/li\u003E\n\u003Cli\u003Einvalidating compromised credentials\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022admin-change-user-credential\u0022\u003E\uD83D\uDC51 Admin: Change User Credential\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Credentials.ChangeUserAsync(userKey, new ChangeCredentialRequest\n{\n NewSecret = \u0026quot;new-password\u0026quot;\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Does NOT require current password\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022admin-add-credential\u0022\u003E\uD83D\uDC51 Admin: Add Credential\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Credentials.AddUserAsync(userKey, new AddCredentialRequest\n{\n Secret = \u0026quot;password\u0026quot;\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022admin-revoke-credential\u0022\u003E\uD83D\uDC51 Admin: Revoke Credential\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Credentials.RevokeUserAsync(userKey, new RevokeCredentialRequest\n{\n CredentialId = credentialId\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022admin-reset-credential\u0022\u003E\uD83D\uDC51 Admin: Reset Credential\u003C/h2\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022begin\u0022\u003EBegin\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Credentials.BeginResetUserAsync(userKey, request);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022complete\u0022\u003EComplete\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Credentials.CompleteResetUserAsync(userKey, request);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022delete-credential-admin\u0022\u003E\u274C Delete Credential (Admin)\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Credentials.DeleteUserAsync(userKey, new DeleteCredentialRequest\n{\n CredentialId = credentialId\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022credential-model\u0022\u003E\uD83E\uDDE0 Credential Model\u003C/h2\u003E\n\u003Cp\u003ECredentials are:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Euser-bound\u003C/li\u003E\n\u003Cli\u003Esecurity-version aware\u003C/li\u003E\n\u003Cli\u003Elifecycle-managed\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Changing credentials may:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Einvalidate sessions\u003C/li\u003E\n\u003Cli\u003Etrigger security updates\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022security-notes\u0022\u003E\uD83D\uDD10 Security Notes\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EPasswords are never sent back to client\u003C/li\u003E\n\u003Cli\u003EAll secrets are hashed server-side\u003C/li\u003E\n\u003Cli\u003EReset flows should be protected (email, OTP, etc.)\u003C/li\u003E\n\u003Cli\u003EAdmin operations are policy-protected\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022summary\u0022\u003E\uD83C\uDFAF Summary\u003C/h2\u003E\n\u003Cp\u003ECredential management in UltimateAuth:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Esupports self-service and admin flows\u003C/li\u003E\n\u003Cli\u003Eintegrates with security lifecycle\u003C/li\u003E\n\u003Cli\u003Eenables safe credential rotation\u003C/li\u003E\n\u003C/ul\u003E\n",
+ "Headings": [
+ {
+ "Id": "overview",
+ "Text": "\uD83E\uDDE0 Overview",
+ "Level": 0
+ },
+ {
+ "Id": "change-password-self",
+ "Text": "\uD83D\uDD10 Change Password (Self)",
+ "Level": 0
+ },
+ {
+ "Id": "reset-password-self",
+ "Text": "\uD83D\uDD01 Reset Password (Self)",
+ "Level": 0
+ },
+ {
+ "Id": "begin-reset",
+ "Text": "Begin Reset",
+ "Level": 1
+ },
+ {
+ "Id": "complete-reset",
+ "Text": "Complete Reset",
+ "Level": 1
+ },
+ {
+ "Id": "add-credential-self",
+ "Text": "\u2795 Add Credential (Self)",
+ "Level": 0
+ },
+ {
+ "Id": "revoke-credential-self",
+ "Text": "\u274C Revoke Credential (Self)",
+ "Level": 0
+ },
+ {
+ "Id": "admin-change-user-credential",
+ "Text": "\uD83D\uDC51 Admin: Change User Credential",
+ "Level": 0
+ },
+ {
+ "Id": "admin-add-credential",
+ "Text": "\uD83D\uDC51 Admin: Add Credential",
+ "Level": 0
+ },
+ {
+ "Id": "admin-revoke-credential",
+ "Text": "\uD83D\uDC51 Admin: Revoke Credential",
+ "Level": 0
+ },
+ {
+ "Id": "admin-reset-credential",
+ "Text": "\uD83D\uDC51 Admin: Reset Credential",
+ "Level": 0
+ },
+ {
+ "Id": "begin",
+ "Text": "Begin",
+ "Level": 1
+ },
+ {
+ "Id": "complete",
+ "Text": "Complete",
+ "Level": 1
+ },
+ {
+ "Id": "delete-credential-admin",
+ "Text": "\u274C Delete Credential (Admin)",
+ "Level": 0
+ },
+ {
+ "Id": "credential-model",
+ "Text": "\uD83E\uDDE0 Credential Model",
+ "Level": 0
+ },
+ {
+ "Id": "security-notes",
+ "Text": "\uD83D\uDD10 Security Notes",
+ "Level": 0
+ },
+ {
+ "Id": "summary",
+ "Text": "\uD83C\uDFAF Summary",
+ "Level": 0
+ }
+ ]
+}
\ No newline at end of file
diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/identifiers.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/identifiers.json
new file mode 100644
index 00000000..73b092d4
--- /dev/null
+++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/identifiers.json
@@ -0,0 +1,92 @@
+{
+ "Slug": "client/identifiers",
+ "Title": "Identifiers",
+ "Html": "\n\u003Cp\u003EThis section explains how UltimateAuth manages user identifiers such as email, username, and phone.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022overview\u0022\u003E\uD83E\uDDE0 Overview\u003C/h2\u003E\n\u003Cp\u003EIn UltimateAuth, identifiers are \u003Cstrong\u003Efirst-class entities\u003C/strong\u003E.\u003C/p\u003E\n\u003Cp\u003EThey are NOT just fields on the user.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 On the client, you interact with:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003EUAuthClient.Identifiers...\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022what-is-an-identifier\u0022\u003E\uD83D\uDD11 What is an Identifier?\u003C/h2\u003E\n\u003Cp\u003EAn identifier represents a way to identify a user:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EEmail\u003C/li\u003E\n\u003Cli\u003EUsername\u003C/li\u003E\n\u003Cli\u003EPhone number\u003C/li\u003E\n\u003Cli\u003ECustom identifiers\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Each identifier has:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EValue (e.g. user@ultimateauth.com)\u003C/li\u003E\n\u003Cli\u003EType (email, username, etc.)\u003C/li\u003E\n\u003Cli\u003EVerification state\u003C/li\u003E\n\u003Cli\u003EPrimary flag\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022primary-identifier\u0022\u003E\u2B50 Primary Identifier\u003C/h2\u003E\n\u003Cp\u003EA user can have multiple identifiers, but only one can be \u003Cstrong\u003Eprimary\u003C/strong\u003E. Setting an identifier to primary automatically unset the current primary identifier if exists.\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Identifiers.SetMyPrimaryAsync(new SetPrimaryUserIdentifierRequest\n{\n Id = identifierId\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Primary identifier is typically:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EUsed for display\u003C/li\u003E\n\u003Cli\u003EPreferred for communication\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022login-identifiers\u0022\u003E\uD83D\uDD10 Login Identifiers\u003C/h2\u003E\n\u003Cp\u003ENot all identifiers are used for login.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Cstrong\u003ELogin identifiers are a subset of identifiers\u003C/strong\u003E\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 This is configurable:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EEnable/disable per type\u003C/li\u003E\n\u003Cli\u003ECustom logic can be applied\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022get-my-identifiers\u0022\u003E\uD83D\uDCCB Get My Identifiers\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Evar result = await UAuthClient.Identifiers.GetMyAsync();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022add-identifier\u0022\u003E\u2795 Add Identifier\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Identifiers.AddMyAsync(new AddUserIdentifierRequest\n{\n Identifier = \u0026quot;new@ultimateauth.com\u0026quot;,\n Type = UserIdentifierType.Email,\n IsPrimary = true\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022update-identifier\u0022\u003E\u270F\uFE0F Update Identifier\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003EUpdateUserIdentifierRequest updateRequest = new()\n{\n Id = item.Id,\n NewValue = item.Value\n};\n\nawait UAuthClient.Identifiers.UpdateMyAsync(updateRequest);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022verify-identifier\u0022\u003E\u2705 Verify Identifier\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Identifiers.VerifyMyAsync(new VerifyUserIdentifierRequest\n{\n Id = identifierId\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022delete-identifier\u0022\u003E\u274C Delete Identifier\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Identifiers.DeleteMyAsync(new DeleteUserIdentifierRequest\n{\n Id = identifierId\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022admin-operations\u0022\u003E\uD83D\uDC64 Admin Operations\u003C/h2\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022get-user-identifiers\u0022\u003EGet User Identifiers\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Identifiers.GetUserAsync(userKey);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022add-identifier-to-user\u0022\u003EAdd Identifier to User\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Identifiers.AddUserAsync(userKey, request);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022update-identifier-1\u0022\u003EUpdate Identifier\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Identifiers.UpdateUserAsync(userKey, request);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022delete-identifier-1\u0022\u003EDelete Identifier\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Identifiers.DeleteUserAsync(userKey, request);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022state-events\u0022\u003E\uD83D\uDD04 State Events\u003C/h2\u003E\n\u003Cp\u003EIdentifier changes trigger events:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EIdentifiersChanged\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Useful for:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EUI updates\u003C/li\u003E\n\u003Cli\u003ECache invalidation\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022security-considerations\u0022\u003E\uD83D\uDD10 Security Considerations\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EIdentifiers may require verification\u003C/li\u003E\n\u003Cli\u003ELogin identifiers can be restricted\u003C/li\u003E\n\u003Cli\u003EPrimary identifier can be controlled\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022summary\u0022\u003E\uD83C\uDFAF Summary\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth identifiers:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eare independent entities\u003C/li\u003E\n\u003Cli\u003Esupport multiple types\u003C/li\u003E\n\u003Cli\u003Eseparate login vs non-login identifiers\u003C/li\u003E\n\u003Cli\u003Eare fully manageable via client\u003C/li\u003E\n\u003C/ul\u003E\n",
+ "Headings": [
+ {
+ "Id": "overview",
+ "Text": "\uD83E\uDDE0 Overview",
+ "Level": 0
+ },
+ {
+ "Id": "what-is-an-identifier",
+ "Text": "\uD83D\uDD11 What is an Identifier?",
+ "Level": 0
+ },
+ {
+ "Id": "primary-identifier",
+ "Text": "\u2B50 Primary Identifier",
+ "Level": 0
+ },
+ {
+ "Id": "login-identifiers",
+ "Text": "\uD83D\uDD10 Login Identifiers",
+ "Level": 0
+ },
+ {
+ "Id": "get-my-identifiers",
+ "Text": "\uD83D\uDCCB Get My Identifiers",
+ "Level": 0
+ },
+ {
+ "Id": "add-identifier",
+ "Text": "\u2795 Add Identifier",
+ "Level": 0
+ },
+ {
+ "Id": "update-identifier",
+ "Text": "\u270F\uFE0F Update Identifier",
+ "Level": 0
+ },
+ {
+ "Id": "verify-identifier",
+ "Text": "\u2705 Verify Identifier",
+ "Level": 0
+ },
+ {
+ "Id": "delete-identifier",
+ "Text": "\u274C Delete Identifier",
+ "Level": 0
+ },
+ {
+ "Id": "admin-operations",
+ "Text": "\uD83D\uDC64 Admin Operations",
+ "Level": 0
+ },
+ {
+ "Id": "get-user-identifiers",
+ "Text": "Get User Identifiers",
+ "Level": 1
+ },
+ {
+ "Id": "add-identifier-to-user",
+ "Text": "Add Identifier to User",
+ "Level": 1
+ },
+ {
+ "Id": "update-identifier-1",
+ "Text": "Update Identifier",
+ "Level": 1
+ },
+ {
+ "Id": "delete-identifier-1",
+ "Text": "Delete Identifier",
+ "Level": 1
+ },
+ {
+ "Id": "state-events",
+ "Text": "\uD83D\uDD04 State Events",
+ "Level": 0
+ },
+ {
+ "Id": "security-considerations",
+ "Text": "\uD83D\uDD10 Security Considerations",
+ "Level": 0
+ },
+ {
+ "Id": "summary",
+ "Text": "\uD83C\uDFAF Summary",
+ "Level": 0
+ }
+ ]
+}
\ No newline at end of file
diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/index.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/index.json
new file mode 100644
index 00000000..bcd0a44e
--- /dev/null
+++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/index.json
@@ -0,0 +1,47 @@
+{
+ "Slug": "client/index",
+ "Title": "Client \u0026 API",
+ "Html": "\n\u003Cp\u003EUltimateAuth Client is a \u003Cstrong\u003Ehigh-level SDK\u003C/strong\u003E designed to simplify authentication flows.\u003C/p\u003E\n\u003Cp\u003EIt is NOT just an HTTP wrapper.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022what-makes-the-client-different\u0022\u003E\uD83E\uDDE0 What Makes the Client Different?\u003C/h2\u003E\n\u003Cp\u003EThe client:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EHandles full authentication flows (login, PKCE, refresh)\u003C/li\u003E\n\u003Cli\u003EManages redirects automatically\u003C/li\u003E\n\u003Cli\u003EPublishes state events\u003C/li\u003E\n\u003Cli\u003EProvides structured results\u003C/li\u003E\n\u003Cli\u003EWorks across multiple client types (SPA, server, hybrid)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 You SHOULD use the client instead of calling endpoints manually.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022client-architecture\u0022\u003E\uD83E\uDDF1 Client Architecture\u003C/h2\u003E\n\u003Cp\u003EThe client is split into multiple specialized services:\u003C/p\u003E\n\u003Ctable\u003E\n\u003Cthead\u003E\n\u003Ctr\u003E\n\u003Cth\u003EService\u003C/th\u003E\n\u003Cth\u003EResponsibility\u003C/th\u003E\n\u003C/tr\u003E\n\u003C/thead\u003E\n\u003Ctbody\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EFlowClient\u003C/td\u003E\n\u003Ctd\u003ELogin, logout, refresh, PKCE\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003ESessionClient\u003C/td\u003E\n\u003Ctd\u003ESession \u0026amp; device management\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EUserClient\u003C/td\u003E\n\u003Ctd\u003EUser profile \u0026amp; lifecycle\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EIdentifierClient\u003C/td\u003E\n\u003Ctd\u003EEmail / username / phone management\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003ECredentialClient\u003C/td\u003E\n\u003Ctd\u003EPassword management\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EAuthorizationClient\u003C/td\u003E\n\u003Ctd\u003ERoles \u0026amp; permissions\u003C/td\u003E\n\u003C/tr\u003E\n\u003C/tbody\u003E\n\u003C/table\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022client-entry-point\u0022\u003E\uD83E\uDDE9 Client Entry Point\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth exposes a single entry point:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Ccode\u003EUAuthClient\u003C/code\u003E\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003E[Inject] IUAuthClient UAuthClient { get; set; } = null!;\n\nawait UAuthClient.Flows.LoginAsync(...);\nawait UAuthClient.Users.GetMeAsync();\nawait UAuthClient.Sessions.GetMyChainsAsync();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022core-concept-flow-based-design\u0022\u003E\uD83D\uDD11 Core Concept: Flow-Based Design\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth is \u003Cstrong\u003Eflow-oriented\u003C/strong\u003E, not endpoint-oriented.\u003C/p\u003E\n\u003Cp\u003EInstead of calling endpoints:\u003C/p\u003E\n\u003Cp\u003E\u274C POST /auth/login\u003Cbr /\u003E\n\u2714 flowClient.LoginAsync()\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022example\u0022\u003E\u26A1 Example\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Flows.LoginAsync(new LoginRequest\n{\n Identifier = \u0026quot;user@ultimateauth.com\u0026quot;,\n Secret = \u0026quot;password\u0026quot;\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 This automatically:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EBuilds request payload\u003C/li\u003E\n\u003Cli\u003EHandles redirect\u003C/li\u003E\n\u003Cli\u003EIntegrates with configured endpoints\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022state-events\u0022\u003E\uD83D\uDD04 State Events\u003C/h2\u003E\n\u003Cp\u003EClient automatically publishes events:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESessionRevoked\u003C/li\u003E\n\u003Cli\u003EProfileChanged\u003C/li\u003E\n\u003Cli\u003EAuthorizationChanged\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022how-to-use-this-section\u0022\u003E\uD83E\uDDED How to Use This Section\u003C/h2\u003E\n\u003Cp\u003EFollow these guides:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eauthentication.md \u2192 login, refresh, logout\u003C/li\u003E\n\u003Cli\u003Esession-management.md \u2192 sessions \u0026amp; devices\u003C/li\u003E\n\u003Cli\u003Euser-management.md \u2192 user operations\u003C/li\u003E\n\u003Cli\u003Eidentifiers.md \u2192 login identifiers\u003C/li\u003E\n\u003Cli\u003Eauthorization.md \u2192 roles \u0026amp; permissions\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022summary\u0022\u003E\uD83C\uDFAF Summary\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth Client:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eabstracts complexity\u003C/li\u003E\n\u003Cli\u003Eenforces correct flows\u003C/li\u003E\n\u003Cli\u003Ereduces security mistakes\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Think of it as:\u003C/p\u003E\n\u003Cp\u003E\u003Cstrong\u003E\u201CAuthentication runtime for your frontend / app\u201D\u003C/strong\u003E\u003C/p\u003E\n",
+ "Headings": [
+ {
+ "Id": "what-makes-the-client-different",
+ "Text": "\uD83E\uDDE0 What Makes the Client Different?",
+ "Level": 0
+ },
+ {
+ "Id": "client-architecture",
+ "Text": "\uD83E\uDDF1 Client Architecture",
+ "Level": 0
+ },
+ {
+ "Id": "client-entry-point",
+ "Text": "\uD83E\uDDE9 Client Entry Point",
+ "Level": 0
+ },
+ {
+ "Id": "core-concept-flow-based-design",
+ "Text": "\uD83D\uDD11 Core Concept: Flow-Based Design",
+ "Level": 0
+ },
+ {
+ "Id": "example",
+ "Text": "\u26A1 Example",
+ "Level": 0
+ },
+ {
+ "Id": "state-events",
+ "Text": "\uD83D\uDD04 State Events",
+ "Level": 0
+ },
+ {
+ "Id": "how-to-use-this-section",
+ "Text": "\uD83E\uDDED How to Use This Section",
+ "Level": 0
+ },
+ {
+ "Id": "summary",
+ "Text": "\uD83C\uDFAF Summary",
+ "Level": 0
+ }
+ ]
+}
\ No newline at end of file
diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/session-management.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/session-management.json
new file mode 100644
index 00000000..b6189d48
--- /dev/null
+++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/session-management.json
@@ -0,0 +1,87 @@
+{
+ "Slug": "client/session-management",
+ "Title": "Session Management",
+ "Html": "\n\u003Cp\u003EThis section explains how to manage sessions and devices using the UltimateAuth client.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022overview\u0022\u003E\uD83E\uDDE0 Overview\u003C/h2\u003E\n\u003Cp\u003EIn UltimateAuth, sessions are \u003Cstrong\u003Enot just tokens\u003C/strong\u003E.\u003C/p\u003E\n\u003Cp\u003EThey are structured as:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERoot \u2192 user-level security\u003C/li\u003E\n\u003Cli\u003EChain \u2192 device (browser / mobile / app)\u003C/li\u003E\n\u003Cli\u003ESession \u2192 individual authentication instance\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 On the client, you interact with:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003E[Inject] IUAuthClient UAuthClient { get; set; } = null!;\n\nawait UAuthClient.Sessions...\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022get-active-sessions-devices\u0022\u003E\uD83D\uDCCB Get Active Sessions (Devices)\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Evar result = await UAuthClient.Sessions.GetMyChainsAsync();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Returns:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EActive devices\u003C/li\u003E\n\u003Cli\u003ESession chains\u003C/li\u003E\n\u003Cli\u003EMetadata (device, timestamps)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022get-session-detail\u0022\u003E\uD83D\uDD0D Get Session Detail\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Evar detail = await UAuthClient.Sessions.GetMyChainDetailAsync(chainId);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Use this to:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EInspect a specific device\u003C/li\u003E\n\u003Cli\u003EView session history\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022logout-vs-revoke-important\u0022\u003E\uD83D\uDEAA Logout vs Revoke (Important)\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth distinguishes between:\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022logout\u0022\u003ELogout\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Flows.LogoutAsync();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cul\u003E\n\u003Cli\u003EEnds \u003Cstrong\u003Ecurrent session\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003EUser can login again normally\u003C/li\u003E\n\u003Cli\u003EDoes NOT affect other devices\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022revoke-session-control\u0022\u003ERevoke (Session Control)\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Sessions.RevokeMyChainAsync(chainId);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cul\u003E\n\u003Cli\u003EInvalidates entire \u003Cstrong\u003Edevice chain\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003EAll sessions under that device are revoked\u003C/li\u003E\n\u003Cli\u003ECannot be restored\u003C/li\u003E\n\u003Cli\u003ENew login creates a new chain\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Key difference:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELogout = end current session\u003C/li\u003E\n\u003Cli\u003ERevoke = destroy device identity\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EFor standard auth process, use \u003Ccode\u003EUAuthClient.Flows.LogoutMyDeviceAsync(chainId)\u003C/code\u003E instead of \u003Ccode\u003EUAuthClient.Sessions.RevokeMyChainAsync(chainId)\u003C/code\u003E\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022revoke-other-devices\u0022\u003E\uD83D\uDCF1 Revoke Other Devices\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Sessions.RevokeMyOtherChainsAsync();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 This:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EKeeps current device active\u003C/li\u003E\n\u003Cli\u003ELogs out all other devices\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022revoke-all-sessions\u0022\u003E\uD83D\uDCA5 Revoke All Sessions\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Sessions.RevokeAllMyChainsAsync();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 This:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELogs out ALL devices (including current)\u003C/li\u003E\n\u003Cli\u003EForces full reauthentication everywhere\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022admin-session-management\u0022\u003E\uD83D\uDC64 Admin Session Management\u003C/h2\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022get-user-devices\u0022\u003EGet User Devices\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Sessions.GetUserChainsAsync(userKey);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022revoke-specific-session\u0022\u003ERevoke Specific Session\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Sessions.RevokeUserSessionAsync(userKey, sessionId);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022revoke-device-chain\u0022\u003ERevoke Device (Chain)\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Sessions.RevokeUserChainAsync(userKey, chainId);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022revoke-all-user-sessions\u0022\u003ERevoke All User Sessions\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Sessions.RevokeAllUserChainsAsync(userKey);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022device-model\u0022\u003E\uD83E\uDDE0 Device Model\u003C/h2\u003E\n\u003Cp\u003EEach chain represents a \u003Cstrong\u003Edevice identity\u003C/strong\u003E:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EBrowser instance\u003C/li\u003E\n\u003Cli\u003EMobile device\u003C/li\u003E\n\u003Cli\u003EApplication instance\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Sessions are grouped under chains.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022security-implications\u0022\u003E\uD83D\uDD10 Security Implications\u003C/h2\u003E\n\u003Cp\u003ESession operations are security-critical:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERevoke is irreversible\u003C/li\u003E\n\u003Cli\u003EDevice isolation is enforced\u003C/li\u003E\n\u003Cli\u003ECross-device attacks are contained\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022summary\u0022\u003E\uD83C\uDFAF Summary\u003C/h2\u003E\n\u003Cp\u003ESession management in UltimateAuth:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eis device-aware\u003C/li\u003E\n\u003Cli\u003Eseparates logout vs revoke\u003C/li\u003E\n\u003Cli\u003Egives full control over user sessions\u003C/li\u003E\n\u003C/ul\u003E\n",
+ "Headings": [
+ {
+ "Id": "overview",
+ "Text": "\uD83E\uDDE0 Overview",
+ "Level": 0
+ },
+ {
+ "Id": "get-active-sessions-devices",
+ "Text": "\uD83D\uDCCB Get Active Sessions (Devices)",
+ "Level": 0
+ },
+ {
+ "Id": "get-session-detail",
+ "Text": "\uD83D\uDD0D Get Session Detail",
+ "Level": 0
+ },
+ {
+ "Id": "logout-vs-revoke-important",
+ "Text": "\uD83D\uDEAA Logout vs Revoke (Important)",
+ "Level": 0
+ },
+ {
+ "Id": "logout",
+ "Text": "Logout",
+ "Level": 1
+ },
+ {
+ "Id": "revoke-session-control",
+ "Text": "Revoke (Session Control)",
+ "Level": 1
+ },
+ {
+ "Id": "revoke-other-devices",
+ "Text": "\uD83D\uDCF1 Revoke Other Devices",
+ "Level": 0
+ },
+ {
+ "Id": "revoke-all-sessions",
+ "Text": "\uD83D\uDCA5 Revoke All Sessions",
+ "Level": 0
+ },
+ {
+ "Id": "admin-session-management",
+ "Text": "\uD83D\uDC64 Admin Session Management",
+ "Level": 0
+ },
+ {
+ "Id": "get-user-devices",
+ "Text": "Get User Devices",
+ "Level": 1
+ },
+ {
+ "Id": "revoke-specific-session",
+ "Text": "Revoke Specific Session",
+ "Level": 1
+ },
+ {
+ "Id": "revoke-device-chain",
+ "Text": "Revoke Device (Chain)",
+ "Level": 1
+ },
+ {
+ "Id": "revoke-all-user-sessions",
+ "Text": "Revoke All User Sessions",
+ "Level": 1
+ },
+ {
+ "Id": "device-model",
+ "Text": "\uD83E\uDDE0 Device Model",
+ "Level": 0
+ },
+ {
+ "Id": "security-implications",
+ "Text": "\uD83D\uDD10 Security Implications",
+ "Level": 0
+ },
+ {
+ "Id": "summary",
+ "Text": "\uD83C\uDFAF Summary",
+ "Level": 0
+ }
+ ]
+}
\ No newline at end of file
diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/user-management.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/user-management.json
new file mode 100644
index 00000000..55d8be3e
--- /dev/null
+++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/user-management.json
@@ -0,0 +1,87 @@
+{
+ "Slug": "client/user-management",
+ "Title": "User Management",
+ "Html": "\n\u003Cp\u003EThis section explains how to manage users using the UltimateAuth client.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022overview\u0022\u003E\uD83E\uDDE0 Overview\u003C/h2\u003E\n\u003Cp\u003EUser operations are handled via:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003EUAuthClient.Users...\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EThis includes:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EProfile management\u003C/li\u003E\n\u003Cli\u003EUser lifecycle\u003C/li\u003E\n\u003Cli\u003EAdmin operations\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022get-current-user\u0022\u003E\uD83D\uDE4B Get Current User\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Evar me = await UAuthClient.Users.GetMeAsync();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Returns:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EUser profile\u003C/li\u003E\n\u003Cli\u003EStatus\u003C/li\u003E\n\u003Cli\u003EBasic identity data\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022update-profile\u0022\u003E\u270F\uFE0F Update Profile\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Users.UpdateMeAsync(new UpdateProfileRequest\n{\n DisplayName = \u0026quot;John Doe\u0026quot;\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Triggers:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EProfile update\u003C/li\u003E\n\u003Cli\u003EState event (ProfileChanged)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022delete-current-user\u0022\u003E\u274C Delete Current User\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Users.DeleteMeAsync();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 This:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EDeletes user (based on configured mode)\u003C/li\u003E\n\u003Cli\u003EEnds session\u003C/li\u003E\n\u003Cli\u003ETriggers state update\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022admin-query-users\u0022\u003E\uD83D\uDC51 Admin: Query Users\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Evar result = await UAuthClient.Users.QueryAsync(new UserQuery\n{\n Search = \u0026quot;john\u0026quot;,\n PageNumber = 1,\n PageSize = 10\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Supports:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Esearch\u003C/li\u003E\n\u003Cli\u003Epagination\u003C/li\u003E\n\u003Cli\u003Efiltering (status, etc.)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022create-user\u0022\u003E\u2795 Create User\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Users.CreateAsync(new CreateUserRequest\n{\n UserName = \u0026quot;john\u0026quot;,\n Email = \u0026quot;john@mail.com\u0026quot;,\n Password = \u0026quot;123456\u0026quot;\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022admin-create\u0022\u003E\uD83D\uDEE0 Admin Create\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Users.CreateAsAdminAsync(request);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022change-status\u0022\u003E\uD83D\uDD04 Change Status\u003C/h2\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022self\u0022\u003ESelf\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Users.ChangeMyStatusAsync(new ChangeUserStatusSelfRequest\n{\n Status = UserStatus.SelfSuspended\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022admin\u0022\u003EAdmin\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Users.ChangeUserStatusAsync(userKey, new ChangeUserStatusAdminRequest\n{\n Status = UserStatus.Suspended\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022delete-user-admin\u0022\u003E\u274C Delete User (Admin)\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Users.DeleteUserAsync(userKey, new DeleteUserRequest\n{\n Mode = DeleteMode.Soft\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022get-user\u0022\u003E\uD83D\uDD0D Get User\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Evar user = await UAuthClient.Users.GetUserAsync(userKey);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022update-user-admin\u0022\u003E\u270F\uFE0F Update User (Admin)\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Users.UpdateUserAsync(userKey, request);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022lifecycle-model\u0022\u003E\uD83E\uDDE0 Lifecycle Model\u003C/h2\u003E\n\u003Cp\u003EUsers have a lifecycle:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EActive\u003C/li\u003E\n\u003Cli\u003ESuspended\u003C/li\u003E\n\u003Cli\u003EDisabled\u003C/li\u003E\n\u003Cli\u003EDeleted (soft/hard)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Status impacts:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Elogin ability\u003C/li\u003E\n\u003Cli\u003Esession validity\u003C/li\u003E\n\u003Cli\u003Eauthorization\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022security-notes\u0022\u003E\uD83D\uDD10 Security Notes\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EStatus changes may invalidate sessions\u003C/li\u003E\n\u003Cli\u003EDelete may trigger cleanup across domains\u003C/li\u003E\n\u003Cli\u003EAdmin actions are policy-protected\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022summary\u0022\u003E\uD83C\uDFAF Summary\u003C/h2\u003E\n\u003Cp\u003EUser management in UltimateAuth:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eis lifecycle-aware\u003C/li\u003E\n\u003Cli\u003Esupports self \u002B admin flows\u003C/li\u003E\n\u003Cli\u003Eintegrates with session \u0026amp; security model\u003C/li\u003E\n\u003C/ul\u003E\n",
+ "Headings": [
+ {
+ "Id": "overview",
+ "Text": "\uD83E\uDDE0 Overview",
+ "Level": 0
+ },
+ {
+ "Id": "get-current-user",
+ "Text": "\uD83D\uDE4B Get Current User",
+ "Level": 0
+ },
+ {
+ "Id": "update-profile",
+ "Text": "\u270F\uFE0F Update Profile",
+ "Level": 0
+ },
+ {
+ "Id": "delete-current-user",
+ "Text": "\u274C Delete Current User",
+ "Level": 0
+ },
+ {
+ "Id": "admin-query-users",
+ "Text": "\uD83D\uDC51 Admin: Query Users",
+ "Level": 0
+ },
+ {
+ "Id": "create-user",
+ "Text": "\u2795 Create User",
+ "Level": 0
+ },
+ {
+ "Id": "admin-create",
+ "Text": "\uD83D\uDEE0 Admin Create",
+ "Level": 0
+ },
+ {
+ "Id": "change-status",
+ "Text": "\uD83D\uDD04 Change Status",
+ "Level": 0
+ },
+ {
+ "Id": "self",
+ "Text": "Self",
+ "Level": 1
+ },
+ {
+ "Id": "admin",
+ "Text": "Admin",
+ "Level": 1
+ },
+ {
+ "Id": "delete-user-admin",
+ "Text": "\u274C Delete User (Admin)",
+ "Level": 0
+ },
+ {
+ "Id": "get-user",
+ "Text": "\uD83D\uDD0D Get User",
+ "Level": 0
+ },
+ {
+ "Id": "update-user-admin",
+ "Text": "\u270F\uFE0F Update User (Admin)",
+ "Level": 0
+ },
+ {
+ "Id": "lifecycle-model",
+ "Text": "\uD83E\uDDE0 Lifecycle Model",
+ "Level": 0
+ },
+ {
+ "Id": "security-notes",
+ "Text": "\uD83D\uDD10 Security Notes",
+ "Level": 0
+ },
+ {
+ "Id": "summary",
+ "Text": "\uD83C\uDFAF Summary",
+ "Level": 0
+ }
+ ]
+}
\ No newline at end of file
diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/configuration/advanced-configuration.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/configuration/advanced-configuration.json
new file mode 100644
index 00000000..051d3cbc
--- /dev/null
+++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/configuration/advanced-configuration.json
@@ -0,0 +1,72 @@
+{
+ "Slug": "configuration/advanced-configuration",
+ "Title": "Advanced Configuration",
+ "Html": "\n\u003Cp\u003EUltimateAuth is designed to be flexible \u2014 but not fragile.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 You can customize almost every part of the system\u003Cbr /\u003E\n\uD83D\uDC49 Without breaking its security guarantees\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022philosophy\u0022\u003E\u26A0\uFE0F Philosophy\u003C/h2\u003E\n\u003Cp\u003ECustomization in UltimateAuth follows one rule:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 You can extend behavior\u003Cbr /\u003E\n\uD83D\uDC49 You should not bypass security\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022extension-points\u0022\u003E\uD83E\uDDE9 Extension Points\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth exposes multiple extension points:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EResolvers\u003C/li\u003E\n\u003Cli\u003EValidators\u003C/li\u003E\n\u003Cli\u003EAuthorities\u003C/li\u003E\n\u003Cli\u003EOrchestrators\u003C/li\u003E\n\u003Cli\u003EStores\u003C/li\u003E\n\u003Cli\u003EEvents\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 You don\u2019t replace the system\u003Cbr /\u003E\n\uD83D\uDC49 You plug into it\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022replacing-services\u0022\u003E\uD83D\uDD0C Replacing Services\u003C/h2\u003E\n\u003Cp\u003EAll core services can be overridden using DI:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eservices.AddScoped\u0026lt;ISessionValidator, CustomSessionValidator\u0026gt;();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 This allows deep customization\u003Cbr /\u003E\n\uD83D\uDC49 While preserving the pipeline\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022authorities-decisions\u0022\u003E\uD83E\uDDE0 Authorities \u0026amp; Decisions\u003C/h2\u003E\n\u003Cp\u003EAuthorities are responsible for decisions:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELoginAuthority\u003C/li\u003E\n\u003Cli\u003EAccessAuthority\u003C/li\u003E\n\u003C/ul\u003E\n\u003Chr /\u003E\n\u003Cp\u003EYou can override them:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eservices.AddScoped\u0026lt;ILoginAuthority, CustomLoginAuthority\u0026gt;();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 This changes decision logic\u003Cbr /\u003E\n\uD83D\uDC49 Without touching flows\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022orchestrators\u0022\u003E\uD83D\uDD04 Orchestrators\u003C/h2\u003E\n\u003Cp\u003EOrchestrators coordinate execution:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EValidate\u003C/li\u003E\n\u003Cli\u003EAuthorize\u003C/li\u003E\n\u003Cli\u003EExecute command\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 They enforce invariants\u003Cbr /\u003E\n\uD83D\uDC49 Do not bypass them, unless you exact know what you are doing\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022store-customization\u0022\u003E\uD83D\uDDC4 Store Customization\u003C/h2\u003E\n\u003Cp\u003EYou can provide custom stores:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession store\u003C/li\u003E\n\u003Cli\u003ERefresh token store\u003C/li\u003E\n\u003Cli\u003EUser store\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Supports EF Core, in-memory, or custom implementations\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022events\u0022\u003E\uD83D\uDCE1 Events\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth provides event hooks:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELogin\u003C/li\u003E\n\u003Cli\u003ELogout\u003C/li\u003E\n\u003Cli\u003ERefresh\u003C/li\u003E\n\u003Cli\u003ERevoke\u003C/li\u003E\n\u003C/ul\u003E\n\u003Chr /\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eo.Events.OnUserLoggedIn = ctx =\u0026gt;\n{\n // custom logic\n return Task.CompletedTask;\n};\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Use events for side-effects\u003Cbr /\u003E\n\uD83D\uDC49 Not for core logic\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022mode-configuration-overrides\u0022\u003E\u2699\uFE0F Mode Configuration Overrides\u003C/h2\u003E\n\u003Cp\u003EYou can customize behavior per mode:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eo.ModeConfigurations[UAuthMode.Hybrid] = options =\u0026gt;\n{\n options.Token.AccessTokenLifetime = TimeSpan.FromMinutes(5);\n};\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Runs after defaults\u003Cbr /\u003E\n\uD83D\uDC49 Allows fine-grained control\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022custom-resolvers\u0022\u003E\uD83D\uDD10 Custom Resolvers\u003C/h2\u003E\n\u003Cp\u003EYou can override how data is resolved:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ETenant resolver\u003C/li\u003E\n\u003Cli\u003ESession resolver\u003C/li\u003E\n\u003Cli\u003EDevice resolver\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Enables full control over request interpretation\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022safety-boundaries\u0022\u003E\uD83D\uDEE1 Safety Boundaries\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth enforces:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EInvariants\u003C/li\u003E\n\u003Cli\u003EValidation\u003C/li\u003E\n\u003Cli\u003EFail-fast behavior\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Unsafe overrides will fail early\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Extend the system\u003Cbr /\u003E\n\uD83D\uDC49 Don\u2019t fight the system\u003C/p\u003E\n\u003Chr /\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022key-takeaways\u0022\u003E\uD83D\uDCCC Key Takeaways\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EEverything is replaceable via DI\u003C/li\u003E\n\u003Cli\u003EAuthorities control decisions\u003C/li\u003E\n\u003Cli\u003EOrchestrators enforce flow\u003C/li\u003E\n\u003Cli\u003EEvents are for side-effects\u003C/li\u003E\n\u003Cli\u003ESecurity boundaries are protected\u003C/li\u003E\n\u003C/ul\u003E\n\u003Chr /\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003EReturn to \u003Cstrong\u003EAuth Flows\u003C/strong\u003E or explore \u003Cstrong\u003EPlugin Domains\u003C/strong\u003E\u003C/p\u003E\n",
+ "Headings": [
+ {
+ "Id": "philosophy",
+ "Text": "\u26A0\uFE0F Philosophy",
+ "Level": 0
+ },
+ {
+ "Id": "extension-points",
+ "Text": "\uD83E\uDDE9 Extension Points",
+ "Level": 0
+ },
+ {
+ "Id": "replacing-services",
+ "Text": "\uD83D\uDD0C Replacing Services",
+ "Level": 0
+ },
+ {
+ "Id": "authorities-decisions",
+ "Text": "\uD83E\uDDE0 Authorities \u0026 Decisions",
+ "Level": 0
+ },
+ {
+ "Id": "orchestrators",
+ "Text": "\uD83D\uDD04 Orchestrators",
+ "Level": 0
+ },
+ {
+ "Id": "store-customization",
+ "Text": "\uD83D\uDDC4 Store Customization",
+ "Level": 0
+ },
+ {
+ "Id": "events",
+ "Text": "\uD83D\uDCE1 Events",
+ "Level": 0
+ },
+ {
+ "Id": "mode-configuration-overrides",
+ "Text": "\u2699\uFE0F Mode Configuration Overrides",
+ "Level": 0
+ },
+ {
+ "Id": "custom-resolvers",
+ "Text": "\uD83D\uDD10 Custom Resolvers",
+ "Level": 0
+ },
+ {
+ "Id": "safety-boundaries",
+ "Text": "\uD83D\uDEE1 Safety Boundaries",
+ "Level": 0
+ },
+ {
+ "Id": "mental-model",
+ "Text": "\uD83E\uDDE0 Mental Model",
+ "Level": 0
+ },
+ {
+ "Id": "key-takeaways",
+ "Text": "\uD83D\uDCCC Key Takeaways",
+ "Level": 0
+ },
+ {
+ "Id": "next-step",
+ "Text": "\u27A1\uFE0F Next Step",
+ "Level": 0
+ }
+ ]
+}
\ No newline at end of file
diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/configuration/client-options.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/configuration/client-options.json
new file mode 100644
index 00000000..90b12565
--- /dev/null
+++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/configuration/client-options.json
@@ -0,0 +1,57 @@
+{
+ "Slug": "configuration/client-options",
+ "Title": "Client Options",
+ "Html": "\n\u003Cp\u003EClient Options define how UltimateAuth behaves on the \u003Cstrong\u003Eclient side\u003C/strong\u003E.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 While Server controls the system,\u003Cbr /\u003E\n\uD83D\uDC49 Client controls how it is \u003Cstrong\u003Eused\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022what-are-client-options\u0022\u003E\uD83E\uDDE0 What Are Client Options?\u003C/h2\u003E\n\u003Cp\u003EClient options configure:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EClient profile (WASM, Server, MAUI, API)\u003C/li\u003E\n\u003Cli\u003EEndpoint communication\u003C/li\u003E\n\u003Cli\u003EPKCE behavior\u003C/li\u003E\n\u003Cli\u003EToken refresh\u003C/li\u003E\n\u003Cli\u003ERe-authentication behavior\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022basic-usage\u0022\u003E\u2699\uFE0F Basic Usage\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Ebuilder.Services.AddUltimateAuthClientBlazor(o =\u0026gt;\n{\n o.AutoDetectClientProfile = false;\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022client-profile\u0022\u003E\uD83E\uDDE9 Client Profile\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth automatically detects client type by default:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EBlazor WASM\u003C/li\u003E\n\u003Cli\u003EBlazor Server\u003C/li\u003E\n\u003Cli\u003EMAUI\u003C/li\u003E\n\u003Cli\u003EWebServer\u003C/li\u003E\n\u003Cli\u003EAPI\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EYou can override manually:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eo.ClientProfile = UAuthClientProfile.BlazorWasm;\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Manual override is useful for testing or special scenarios\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022endpoints\u0022\u003E\uD83C\uDF10 Endpoints\u003C/h2\u003E\n\u003Cp\u003EDefines where requests are sent:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eo.Endpoints.BasePath = \u0026quot;https://localhost:5001/auth\u0026quot;;\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Required for WASM / remote clients\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022pkce-configuration\u0022\u003E\uD83D\uDD10 PKCE Configuration\u003C/h2\u003E\n\u003Cp\u003EUsed for browser-based login flows:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eo.Pkce.ReturnUrl = \u0026quot;https://localhost:5002/home\u0026quot;;\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Required for WASM scenarios\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022auto-refresh\u0022\u003E\uD83D\uDD01 Auto Refresh\u003C/h2\u003E\n\u003Cp\u003EControls token/session refresh behavior:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eo.AutoRefresh.Interval = TimeSpan.FromMinutes(1);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Keeps authentication alive automatically\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022re-authentication\u0022\u003E\uD83D\uDD04 Re-authentication\u003C/h2\u003E\n\u003Cp\u003EControls behavior when session expires:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eo.Reauth.Behavior = ReauthBehavior.RaiseEvent;\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Allows silent or interactive re-login\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Server decides\u003Cbr /\u003E\n\uD83D\uDC49 Client adapts\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022key-takeaways\u0022\u003E\uD83D\uDCCC Key Takeaways\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EClient options control runtime behavior\u003C/li\u003E\n\u003Cli\u003EProfile detection is automatic\u003C/li\u003E\n\u003Cli\u003EPKCE is required for public clients\u003C/li\u003E\n\u003Cli\u003ERefresh and re-auth are configurable\u003C/li\u003E\n\u003Cli\u003EWorks together with Server options\u003C/li\u003E\n\u003C/ul\u003E\n\u003Chr /\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003EContinue to \u003Cstrong\u003EConfiguration Sources\u003C/strong\u003E\u003C/p\u003E\n",
+ "Headings": [
+ {
+ "Id": "what-are-client-options",
+ "Text": "\uD83E\uDDE0 What Are Client Options?",
+ "Level": 0
+ },
+ {
+ "Id": "basic-usage",
+ "Text": "\u2699\uFE0F Basic Usage",
+ "Level": 0
+ },
+ {
+ "Id": "client-profile",
+ "Text": "\uD83E\uDDE9 Client Profile",
+ "Level": 0
+ },
+ {
+ "Id": "endpoints",
+ "Text": "\uD83C\uDF10 Endpoints",
+ "Level": 0
+ },
+ {
+ "Id": "pkce-configuration",
+ "Text": "\uD83D\uDD10 PKCE Configuration",
+ "Level": 0
+ },
+ {
+ "Id": "auto-refresh",
+ "Text": "\uD83D\uDD01 Auto Refresh",
+ "Level": 0
+ },
+ {
+ "Id": "re-authentication",
+ "Text": "\uD83D\uDD04 Re-authentication",
+ "Level": 0
+ },
+ {
+ "Id": "mental-model",
+ "Text": "\uD83E\uDDE0 Mental Model",
+ "Level": 0
+ },
+ {
+ "Id": "key-takeaways",
+ "Text": "\uD83D\uDCCC Key Takeaways",
+ "Level": 0
+ },
+ {
+ "Id": "next-step",
+ "Text": "\u27A1\uFE0F Next Step",
+ "Level": 0
+ }
+ ]
+}
\ No newline at end of file
diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/configuration/configuration-overview.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/configuration/configuration-overview.json
new file mode 100644
index 00000000..328f7601
--- /dev/null
+++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/configuration/configuration-overview.json
@@ -0,0 +1,72 @@
+{
+ "Slug": "configuration/configuration-overview",
+ "Title": "Configuration Overview",
+ "Html": "\n\u003Cp\u003EUltimateAuth is not configured as a static system.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 It is configured as a \u003Cstrong\u003Eruntime-adaptive system\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022a-common-misunderstanding\u0022\u003E\u26A0\uFE0F A Common Misunderstanding\u003C/h2\u003E\n\u003Cp\u003EMany frameworks expect you to configure authentication once:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EChoose JWT or cookies\u003C/li\u003E\n\u003Cli\u003ESet token lifetimes\u003C/li\u003E\n\u003Cli\u003EConfigure behavior globally\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 And that configuration applies everywhere\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 UltimateAuth does NOT work like this\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022layered-configuration-model\u0022\u003E\uD83E\uDDE9 Layered Configuration Model\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth separates configuration into distinct layers:\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022core-behavior-definition\u0022\u003E\uD83D\uDD39 Core (Behavior Definition)\u003C/h3\u003E\n\u003Cp\u003ECore defines \u003Cstrong\u003Ewhat authentication means\u003C/strong\u003E:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession lifecycle\u003C/li\u003E\n\u003Cli\u003EToken behavior\u003C/li\u003E\n\u003Cli\u003EPKCE rules\u003C/li\u003E\n\u003Cli\u003EMulti-tenant handling\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Core is the foundation\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 But you typically do NOT configure it directly\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022server-application-configuration\u0022\u003E\uD83D\uDD39 Server (Application Configuration)\u003C/h3\u003E\n\u003Cp\u003EServer is where you configure the system:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Ebuilder.Services.AddUltimateAuthServer(o =\u0026gt;\n{\n o.Login.MaxFailedAttempts = 5;\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EThis layer controls:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EAllowed authentication modes\u003C/li\u003E\n\u003Cli\u003EEndpoint exposure\u003C/li\u003E\n\u003Cli\u003ECookie behavior\u003C/li\u003E\n\u003Cli\u003ESecurity policies\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This is your main configuration surface\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022client-runtime-behavior\u0022\u003E\uD83D\uDD39 Client (Runtime Behavior)\u003C/h3\u003E\n\u003Cp\u003EClient configuration controls:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EClient profile (WASM, Server, MAUI, API)\u003C/li\u003E\n\u003Cli\u003EPKCE behavior\u003C/li\u003E\n\u003Cli\u003EAuto-refresh\u003C/li\u003E\n\u003Cli\u003ERe-authentication\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Client influences how flows are executed\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022runtime-configuration-the-key-idea\u0022\u003E\u26A1 Runtime Configuration (The Key Idea)\u003C/h2\u003E\n\u003Cp\u003EHere is the most important concept:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 UltimateAuth does NOT use configuration as-is\u003C/p\u003E\n\u003Cp\u003EInstead, it computes \u003Cstrong\u003EEffective Configuration per request\u003C/strong\u003E\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022how-it-works\u0022\u003E\uD83E\uDDE0 How It Works\u003C/h3\u003E\n\u003Cp\u003EAt runtime:\u003C/p\u003E\n\u003Col\u003E\n\u003Cli\u003EClient profile is detected\u003C/li\u003E\n\u003Cli\u003EFlow type is determined (Login, Refresh, etc.)\u003C/li\u003E\n\u003Cli\u003EAuth mode is resolved\u003C/li\u003E\n\u003Cli\u003EDefaults are applied\u003C/li\u003E\n\u003Cli\u003EMode-specific overrides are applied\u003C/li\u003E\n\u003C/ol\u003E\n\u003Cp\u003E\uD83D\uDC49 This produces:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003EEffectiveOptions\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022from-static-to-dynamic\u0022\u003E\uD83D\uDD04 From Static to Dynamic\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode\u003EUAuthServerOptions (startup)\n \u2193\nMode Resolver\n \u2193\nApply Defaults\n \u2193\nMode Overrides\n \u2193\nEffectiveUAuthServerOptions (runtime)\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Every request can have a different effective configuration\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022why-this-matters\u0022\u003E\uD83C\uDFAF Why This Matters\u003C/h2\u003E\n\u003Cp\u003EThis allows UltimateAuth to:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EUse different auth modes per client\u003C/li\u003E\n\u003Cli\u003EAdapt behavior per flow\u003C/li\u003E\n\u003Cli\u003EEnforce security dynamically\u003C/li\u003E\n\u003Cli\u003EAvoid global misconfiguration\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 You don\u2019t configure \u201Cone system\u201D\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 You configure a \u003Cstrong\u003Edecision engine\u003C/strong\u003E\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022safety-by-design\u0022\u003E\uD83D\uDEE1 Safety by Design\u003C/h2\u003E\n\u003Cp\u003EEven with dynamic behavior:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EInvalid combinations fail early\u003C/li\u003E\n\u003Cli\u003EDisallowed modes are rejected\u003C/li\u003E\n\u003Cli\u003ESecurity invariants are enforced\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Flexibility does not reduce safety\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022core-vs-effective\u0022\u003E\u2699\uFE0F Core vs Effective\u003C/h2\u003E\n\u003Ctable\u003E\n\u003Cthead\u003E\n\u003Ctr\u003E\n\u003Cth\u003EConcept\u003C/th\u003E\n\u003Cth\u003EMeaning\u003C/th\u003E\n\u003C/tr\u003E\n\u003C/thead\u003E\n\u003Ctbody\u003E\n\u003Ctr\u003E\n\u003Ctd\u003ECore Options\u003C/td\u003E\n\u003Ctd\u003EBase behavior definitions\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EServer Options\u003C/td\u003E\n\u003Ctd\u003EApplication-level configuration\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EEffective Options\u003C/td\u003E\n\u003Ctd\u003ERuntime-resolved behavior\u003C/td\u003E\n\u003C/tr\u003E\n\u003C/tbody\u003E\n\u003C/table\u003E\n\u003Cp\u003E\uD83D\uDC49 Effective options are what actually run\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 You don\u2019t configure authentication\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 You configure how it is \u003Cstrong\u003Eresolved\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EControl runtime \u2192 Server Options\u003C/li\u003E\n\u003Cli\u003EConfigure clients \u2192 Client Options\u003C/li\u003E\n\u003C/ul\u003E\n",
+ "Headings": [
+ {
+ "Id": "a-common-misunderstanding",
+ "Text": "\u26A0\uFE0F A Common Misunderstanding",
+ "Level": 0
+ },
+ {
+ "Id": "layered-configuration-model",
+ "Text": "\uD83E\uDDE9 Layered Configuration Model",
+ "Level": 0
+ },
+ {
+ "Id": "core-behavior-definition",
+ "Text": "\uD83D\uDD39 Core (Behavior Definition)",
+ "Level": 1
+ },
+ {
+ "Id": "server-application-configuration",
+ "Text": "\uD83D\uDD39 Server (Application Configuration)",
+ "Level": 1
+ },
+ {
+ "Id": "client-runtime-behavior",
+ "Text": "\uD83D\uDD39 Client (Runtime Behavior)",
+ "Level": 1
+ },
+ {
+ "Id": "runtime-configuration-the-key-idea",
+ "Text": "\u26A1 Runtime Configuration (The Key Idea)",
+ "Level": 0
+ },
+ {
+ "Id": "how-it-works",
+ "Text": "\uD83E\uDDE0 How It Works",
+ "Level": 1
+ },
+ {
+ "Id": "from-static-to-dynamic",
+ "Text": "\uD83D\uDD04 From Static to Dynamic",
+ "Level": 0
+ },
+ {
+ "Id": "why-this-matters",
+ "Text": "\uD83C\uDFAF Why This Matters",
+ "Level": 0
+ },
+ {
+ "Id": "safety-by-design",
+ "Text": "\uD83D\uDEE1 Safety by Design",
+ "Level": 0
+ },
+ {
+ "Id": "core-vs-effective",
+ "Text": "\u2699\uFE0F Core vs Effective",
+ "Level": 0
+ },
+ {
+ "Id": "mental-model",
+ "Text": "\uD83E\uDDE0 Mental Model",
+ "Level": 0
+ },
+ {
+ "Id": "next-step",
+ "Text": "\u27A1\uFE0F Next Step",
+ "Level": 0
+ }
+ ]
+}
\ No newline at end of file
diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/configuration/configuration-sources.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/configuration/configuration-sources.json
new file mode 100644
index 00000000..1ebbf96d
--- /dev/null
+++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/configuration/configuration-sources.json
@@ -0,0 +1,87 @@
+{
+ "Slug": "configuration/configuration-sources",
+ "Title": "Configuration Sources",
+ "Html": "\n\u003Cp\u003EUltimateAuth supports multiple configuration sources.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 But more importantly, it defines a \u003Cstrong\u003Eclear and predictable precedence model\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022two-ways-to-configure\u0022\u003E\uD83E\uDDE0 Two Ways to Configure\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth can be configured using:\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022code-program.cs\u0022\u003E\uD83D\uDD39 Code (Program.cs)\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Ebuilder.Services.AddUltimateAuthServer(o =\u0026gt;\n{\n o.Login.MaxFailedAttempts = 5;\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022configuration-files-appsettings.json\u0022\u003E\uD83D\uDD39 Configuration Files (appsettings.json)\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-json\u0022\u003E{\n \u0026quot;UltimateAuth\u0026quot;: {\n \u0026quot;Server\u0026quot;: {\n \u0026quot;Login\u0026quot;: {\n \u0026quot;MaxFailedAttempts\u0026quot;: 5\n }\n }\n }\n}\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022precedence-rules\u0022\u003E\u2696\uFE0F Precedence Rules\u003C/h2\u003E\n\u003Cp\u003EThis is the most important rule:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Cstrong\u003EConfiguration files override code\u003C/strong\u003E\u003C/p\u003E\n\u003Cp\u003EExecution order:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003EProgram.cs configuration\n \u2193\nappsettings.json binding\n \u2193\nFinal options\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 This means:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EDefaults can be defined in code\u003C/li\u003E\n\u003Cli\u003EEnvironments can override them safely\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022environment-based-configuration\u0022\u003E\uD83C\uDF0D Environment-Based Configuration\u003C/h2\u003E\n\u003Cp\u003EASP.NET Core supports environment-specific configuration:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eappsettings.Development.json\u003C/li\u003E\n\u003Cli\u003Eappsettings.Staging.json\u003C/li\u003E\n\u003Cli\u003Eappsettings.Production.json\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EExample:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-json\u0022\u003E{\n \u0026quot;UltimateAuth\u0026quot;: {\n \u0026quot;Server\u0026quot;: {\n \u0026quot;Session\u0026quot;: {\n \u0026quot;IdleTimeout\u0026quot;: \u0026quot;7.00:00:00\u0026quot;\n }\n }\n }\n}\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 You can use different values per environment without changing code\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022recommended-strategy\u0022\u003E\uD83E\uDDE9 Recommended Strategy\u003C/h2\u003E\n\u003Cp\u003EFor real-world applications:\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022use-program.cs-for\u0022\u003E\u2714 Use Program.cs for:\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EDefaults\u003C/li\u003E\n\u003Cli\u003EDevelopment setup\u003C/li\u003E\n\u003Cli\u003ELocal testing\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022use-appsettings-for\u0022\u003E\u2714 Use appsettings for:\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EEnvironment-specific overrides\u003C/li\u003E\n\u003Cli\u003EProduction tuning\u003C/li\u003E\n\u003Cli\u003EDeployment configuration\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This keeps your system flexible and maintainable\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022safety-validation\u0022\u003E\uD83D\uDEE1 Safety \u0026amp; Validation\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth validates configuration at startup:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EInvalid combinations are rejected\u003C/li\u003E\n\u003Cli\u003EMissing required values fail fast\u003C/li\u003E\n\u003Cli\u003EUnsafe configurations are blocked\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 You will not run with a broken configuration\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022common-pitfalls\u0022\u003E\u26A0\uFE0F Common Pitfalls\u003C/h2\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022assuming-code-overrides-config\u0022\u003E\u274C Assuming code overrides config\u003C/h3\u003E\n\u003Cp\u003EIt does not.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 appsettings.json always wins\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022hardcoding-production-values\u0022\u003E\u274C Hardcoding production values\u003C/h3\u003E\n\u003Cp\u003EAvoid:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eo.Token.AccessTokenLifetime = TimeSpan.FromHours(1);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Use environment config instead for maximum flexibility\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022mixing-environments-unintentionally\u0022\u003E\u274C Mixing environments unintentionally\u003C/h3\u003E\n\u003Cp\u003EEnsure correct environment is set:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003EASPNETCORE_ENVIRONMENT=Production\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Code defines defaults\u003Cbr /\u003E\n\uD83D\uDC49 Configuration defines reality\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022key-takeaways\u0022\u003E\uD83D\uDCCC Key Takeaways\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EUltimateAuth supports code \u002B configuration\u003C/li\u003E\n\u003Cli\u003Eappsettings.json overrides Program.cs\u003C/li\u003E\n\u003Cli\u003EEnvironment-based configuration is first-class\u003C/li\u003E\n\u003Cli\u003EValidation prevents unsafe setups\u003C/li\u003E\n\u003Cli\u003EDesigned for real-world deployment\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003EContinue to \u003Cstrong\u003EAdvanced Configuration\u003C/strong\u003E\u003C/p\u003E\n",
+ "Headings": [
+ {
+ "Id": "two-ways-to-configure",
+ "Text": "\uD83E\uDDE0 Two Ways to Configure",
+ "Level": 0
+ },
+ {
+ "Id": "code-program.cs",
+ "Text": "\uD83D\uDD39 Code (Program.cs)",
+ "Level": 1
+ },
+ {
+ "Id": "configuration-files-appsettings.json",
+ "Text": "\uD83D\uDD39 Configuration Files (appsettings.json)",
+ "Level": 1
+ },
+ {
+ "Id": "precedence-rules",
+ "Text": "\u2696\uFE0F Precedence Rules",
+ "Level": 0
+ },
+ {
+ "Id": "environment-based-configuration",
+ "Text": "\uD83C\uDF0D Environment-Based Configuration",
+ "Level": 0
+ },
+ {
+ "Id": "recommended-strategy",
+ "Text": "\uD83E\uDDE9 Recommended Strategy",
+ "Level": 0
+ },
+ {
+ "Id": "use-program.cs-for",
+ "Text": "\u2714 Use Program.cs for:",
+ "Level": 1
+ },
+ {
+ "Id": "use-appsettings-for",
+ "Text": "\u2714 Use appsettings for:",
+ "Level": 1
+ },
+ {
+ "Id": "safety-validation",
+ "Text": "\uD83D\uDEE1 Safety \u0026 Validation",
+ "Level": 0
+ },
+ {
+ "Id": "common-pitfalls",
+ "Text": "\u26A0\uFE0F Common Pitfalls",
+ "Level": 0
+ },
+ {
+ "Id": "assuming-code-overrides-config",
+ "Text": "\u274C Assuming code overrides config",
+ "Level": 1
+ },
+ {
+ "Id": "hardcoding-production-values",
+ "Text": "\u274C Hardcoding production values",
+ "Level": 1
+ },
+ {
+ "Id": "mixing-environments-unintentionally",
+ "Text": "\u274C Mixing environments unintentionally",
+ "Level": 1
+ },
+ {
+ "Id": "mental-model",
+ "Text": "\uD83E\uDDE0 Mental Model",
+ "Level": 0
+ },
+ {
+ "Id": "key-takeaways",
+ "Text": "\uD83D\uDCCC Key Takeaways",
+ "Level": 0
+ },
+ {
+ "Id": "next-step",
+ "Text": "\u27A1\uFE0F Next Step",
+ "Level": 0
+ }
+ ]
+}
\ No newline at end of file
diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/configuration/index.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/configuration/index.json
new file mode 100644
index 00000000..fb345771
--- /dev/null
+++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/configuration/index.json
@@ -0,0 +1,57 @@
+{
+ "Slug": "configuration/index",
+ "Title": "Configuration",
+ "Html": "\n\u003Cp\u003EUltimateAuth is designed to be flexible.\u003C/p\u003E\n\u003Cp\u003EBut flexibility without structure leads to chaos.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Configuration in UltimateAuth is structured, layered, and safe by default.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022what-you-configure\u0022\u003E\uD83E\uDDE0 What You Configure\u003C/h2\u003E\n\u003Cp\u003EIn UltimateAuth, you don\u2019t just configure values.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 You configure behavior.\u003C/p\u003E\n\u003Cp\u003EThis includes:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EHow sessions are created and managed\u003C/li\u003E\n\u003Cli\u003EHow tokens are issued and refreshed\u003C/li\u003E\n\u003Cli\u003EHow tenants are resolved\u003C/li\u003E\n\u003Cli\u003EHow clients interact with the system\u003C/li\u003E\n\u003Cli\u003EWhich features are enabled or restricted\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022configuration-layers\u0022\u003E\uD83E\uDDE9 Configuration Layers\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth separates configuration into three layers:\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022core\u0022\u003E\uD83D\uDD39 Core\u003C/h3\u003E\n\u003Cp\u003EDefines authentication behavior:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession lifecycle\u003C/li\u003E\n\u003Cli\u003EToken policies\u003C/li\u003E\n\u003Cli\u003EPKCE flows\u003C/li\u003E\n\u003Cli\u003EMulti-tenancy\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022server\u0022\u003E\uD83D\uDD39 Server\u003C/h3\u003E\n\u003Cp\u003EDefines runtime behavior:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EAllowed authentication modes\u003C/li\u003E\n\u003Cli\u003EEndpoint exposure\u003C/li\u003E\n\u003Cli\u003ECookie and transport behavior\u003C/li\u003E\n\u003Cli\u003EHub deployment\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022client\u0022\u003E\uD83D\uDD39 Client\u003C/h3\u003E\n\u003Cp\u003EDefines client-side behavior:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EClient profile\u003C/li\u003E\n\u003Cli\u003EPKCE configuration\u003C/li\u003E\n\u003Cli\u003EAuto-refresh and re-authentication\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 These layers are independent but work together.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022configuration-sources\u0022\u003E\u2699\uFE0F Configuration Sources\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth supports two configuration styles:\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022code-based-program.cs\u0022\u003ECode-based (Program.cs)\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Ebuilder.Services.AddUltimateAuthServer(o =\u0026gt;\n{\n o.Login.MaxFailedAttempts = 5;\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022configuration-based-appsettings.json\u0022\u003EConfiguration-based (appsettings.json)\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003E{\n \u0026quot;UltimateAuth\u0026quot;: {\n \u0026quot;Server\u0026quot;: {\n \u0026quot;Login\u0026quot;: {\n \u0026quot;MaxFailedAttempts\u0026quot;: 5\n }\n }\n }\n}\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 appsettings.json overrides Program.cs\u003C/p\u003E\n\u003Cp\u003EThis allows:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EEnvironment-based configuration\u003C/li\u003E\n\u003Cli\u003ECentralized management\u003C/li\u003E\n\u003Cli\u003EProduction-safe overrides\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022safety-by-design\u0022\u003E\uD83D\uDEE1 Safety by Design\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth does not allow unsafe configurations silently.\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EInvalid combinations fail at startup\u003C/li\u003E\n\u003Cli\u003EUnsupported modes are rejected\u003C/li\u003E\n\u003Cli\u003ESecurity invariants are enforced\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Flexibility is allowed\n\uD83D\uDC49 Unsafe behavior is not\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022whats-next\u0022\u003E\uD83C\uDFAF What\u2019s Next?\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EUnderstand configuration layers \u2192 Configuration Overview\u003C/li\u003E\n\u003Cli\u003ELearn Core behavior \u2192 Core Options\u003C/li\u003E\n\u003Cli\u003ECustomize server \u2192 Server Options\u003C/li\u003E\n\u003Cli\u003EControl clients \u2192 Client Options\u003C/li\u003E\n\u003Cli\u003EGo deeper \u2192 Advanced Configuration\u003C/li\u003E\n\u003C/ul\u003E\n",
+ "Headings": [
+ {
+ "Id": "what-you-configure",
+ "Text": "\uD83E\uDDE0 What You Configure",
+ "Level": 0
+ },
+ {
+ "Id": "configuration-layers",
+ "Text": "\uD83E\uDDE9 Configuration Layers",
+ "Level": 0
+ },
+ {
+ "Id": "core",
+ "Text": "\uD83D\uDD39 Core",
+ "Level": 1
+ },
+ {
+ "Id": "server",
+ "Text": "\uD83D\uDD39 Server",
+ "Level": 1
+ },
+ {
+ "Id": "client",
+ "Text": "\uD83D\uDD39 Client",
+ "Level": 1
+ },
+ {
+ "Id": "configuration-sources",
+ "Text": "\u2699\uFE0F Configuration Sources",
+ "Level": 0
+ },
+ {
+ "Id": "code-based-program.cs",
+ "Text": "Code-based (Program.cs)",
+ "Level": 1
+ },
+ {
+ "Id": "configuration-based-appsettings.json",
+ "Text": "Configuration-based (appsettings.json)",
+ "Level": 1
+ },
+ {
+ "Id": "safety-by-design",
+ "Text": "\uD83D\uDEE1 Safety by Design",
+ "Level": 0
+ },
+ {
+ "Id": "whats-next",
+ "Text": "\uD83C\uDFAF What\u2019s Next?",
+ "Level": 0
+ }
+ ]
+}
\ No newline at end of file
diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/configuration/server-options.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/configuration/server-options.json
new file mode 100644
index 00000000..8804d48a
--- /dev/null
+++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/configuration/server-options.json
@@ -0,0 +1,77 @@
+{
+ "Slug": "configuration/server-options",
+ "Title": "Server Options",
+ "Html": "\n\u003Cp\u003EUltimateAuth is configured primarily through \u003Cstrong\u003EServer Options\u003C/strong\u003E.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 This is the main entry point for configuring authentication behavior.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022what-are-server-options\u0022\u003E\uD83E\uDDE0 What Are Server Options?\u003C/h2\u003E\n\u003Cp\u003EServer options define how UltimateAuth behaves \u003Cstrong\u003Einside your application\u003C/strong\u003E.\u003C/p\u003E\n\u003Cp\u003EThey control:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EAuthentication behavior\u003C/li\u003E\n\u003Cli\u003ESecurity policies\u003C/li\u003E\n\u003Cli\u003EToken issuance\u003C/li\u003E\n\u003Cli\u003ESession lifecycle\u003C/li\u003E\n\u003Cli\u003EEndpoint exposure\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022basic-usage\u0022\u003E\u2699\uFE0F Basic Usage\u003C/h2\u003E\n\u003Cp\u003EYou configure server options in \u003Ccode\u003EProgram.cs\u003C/code\u003E:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Ebuilder.Services.AddUltimateAuthServer(o =\u0026gt;\n{\n o.Login.MaxFailedAttempts = 5;\n o.Session.IdleTimeout = TimeSpan.FromDays(7);\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Chr /\u003E\n\u003Cp\u003EYou can also use \u003Ccode\u003Eappsettings.json\u003C/code\u003E:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-json\u0022\u003E{\n \u0026quot;UltimateAuth\u0026quot;: {\n \u0026quot;Server\u0026quot;: {\n \u0026quot;Login\u0026quot;: {\n \u0026quot;MaxFailedAttempts\u0026quot;: 5\n },\n \u0026quot;Session\u0026quot;: {\n \u0026quot;IdleTimeout\u0026quot;: \u0026quot;07.00.00.00\u0026quot;\n }\n }\n }\n}\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Ccode\u003Eappsettings.json\u003C/code\u003E overrides \u003Ccode\u003EProgram.cs\u003C/code\u003E\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022core-composition\u0022\u003E\uD83E\uDDE9 Core Composition\u003C/h2\u003E\n\u003Cp\u003EServer options include Core behavior:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELogin\u003C/li\u003E\n\u003Cli\u003ESession\u003C/li\u003E\n\u003Cli\u003EToken\u003C/li\u003E\n\u003Cli\u003EPKCE\u003C/li\u003E\n\u003Cli\u003EMulti-tenancy\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 These are defined in Core\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 But configured via Server\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022important-you-dont-configure-modes-directly\u0022\u003E\u26A0\uFE0F Important: You Don\u2019t Configure Modes Directly\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth does NOT expect you to select a single auth mode.\u003C/p\u003E\n\u003Cp\u003EInstead:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Mode is resolved at runtime\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022allowed-modes-guardrail\u0022\u003E\uD83D\uDEE1 Allowed Modes (Guardrail)\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eo.AllowedModes = new[]\n{\n UAuthMode.Hybrid,\n UAuthMode.PureOpaque\n};\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 This does NOT select a mode\u003Cbr /\u003E\n\uD83D\uDC49 It restricts which modes are allowed\u003C/p\u003E\n\u003Cp\u003EIf a resolved mode is not allowed:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Request fails early\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022runtime-behavior-effective-options\u0022\u003E\u26A1 Runtime Behavior (Effective Options)\u003C/h2\u003E\n\u003Cp\u003EServer options are not used directly.\u003C/p\u003E\n\u003Cp\u003EThey are transformed into:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Ccode\u003EEffectiveUAuthServerOptions\u003C/code\u003E\u003C/p\u003E\n\u003Cp\u003EThis happens per request:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EMode is resolved\u003C/li\u003E\n\u003Cli\u003EDefaults are applied\u003C/li\u003E\n\u003Cli\u003EOverrides are applied\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 What actually runs is \u003Cstrong\u003EEffective Options\u003C/strong\u003E\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022mode-based-defaults\u0022\u003E\uD83D\uDD04 Mode-Based Defaults\u003C/h2\u003E\n\u003Cp\u003EEach auth mode applies different defaults automatically:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EPureOpaque \u2192 session-heavy\u003C/li\u003E\n\u003Cli\u003EHybrid \u2192 session \u002B token\u003C/li\u003E\n\u003Cli\u003EPureJwt \u2192 token-only\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 You don\u2019t need to manually configure everything\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022endpoint-control\u0022\u003E\uD83C\uDF9B Endpoint Control\u003C/h2\u003E\n\u003Cp\u003EYou can control which features are enabled:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eo.Endpoints.Authentication = true;\no.Endpoints.Session = true;\no.Endpoints.Authorization = true;\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EYou can also disable specific actions:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eo.Endpoints.DisabledActions.Add(\u0026quot;UAuthActions.Users.Create.Anonymous\u0026quot;);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Useful for API hardening\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022cookie-transport-behavior\u0022\u003E\uD83C\uDF6A Cookie \u0026amp; Transport Behavior\u003C/h2\u003E\n\u003Cp\u003EServer options define how credentials are transported:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ECookies\u003C/li\u003E\n\u003Cli\u003EHeaders\u003C/li\u003E\n\u003Cli\u003ETokens\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Unsafe combinations are rejected at startup\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022hub-configuration\u0022\u003E\uD83C\uDF10 Hub Configuration\u003C/h2\u003E\n\u003Cp\u003EIf using UAuthHub:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eo.HubDeploymentMode = UAuthHubDeploymentMode.Integrated;\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Defines how auth server is deployed\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022session-resolution\u0022\u003E\uD83D\uDD01 Session Resolution\u003C/h2\u003E\n\u003Cp\u003EControls how session IDs are extracted:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ECookie\u003C/li\u003E\n\u003Cli\u003EHeader\u003C/li\u003E\n\u003Cli\u003EBearer\u003C/li\u003E\n\u003Cli\u003EQuery\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Fully configurable\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Server options define \u003Cstrong\u003Ewhat is allowed\u003C/strong\u003E\u003Cbr /\u003E\n\uD83D\uDC49 Runtime determines \u003Cstrong\u003Ewhat is used\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022key-takeaways\u0022\u003E\uD83D\uDCCC Key Takeaways\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EServer options are the main configuration entry\u003C/li\u003E\n\u003Cli\u003ECore behavior is configured via server\u003C/li\u003E\n\u003Cli\u003EModes are not selected manually\u003C/li\u003E\n\u003Cli\u003EEffective options are computed per request\u003C/li\u003E\n\u003Cli\u003ESecurity is enforced by design\u003C/li\u003E\n\u003C/ul\u003E\n\u003Chr /\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003EContinue to \u003Cstrong\u003EClient Options\u003C/strong\u003E\u003C/p\u003E\n",
+ "Headings": [
+ {
+ "Id": "what-are-server-options",
+ "Text": "\uD83E\uDDE0 What Are Server Options?",
+ "Level": 0
+ },
+ {
+ "Id": "basic-usage",
+ "Text": "\u2699\uFE0F Basic Usage",
+ "Level": 0
+ },
+ {
+ "Id": "core-composition",
+ "Text": "\uD83E\uDDE9 Core Composition",
+ "Level": 0
+ },
+ {
+ "Id": "important-you-dont-configure-modes-directly",
+ "Text": "\u26A0\uFE0F Important: You Don\u2019t Configure Modes Directly",
+ "Level": 0
+ },
+ {
+ "Id": "allowed-modes-guardrail",
+ "Text": "\uD83D\uDEE1 Allowed Modes (Guardrail)",
+ "Level": 0
+ },
+ {
+ "Id": "runtime-behavior-effective-options",
+ "Text": "\u26A1 Runtime Behavior (Effective Options)",
+ "Level": 0
+ },
+ {
+ "Id": "mode-based-defaults",
+ "Text": "\uD83D\uDD04 Mode-Based Defaults",
+ "Level": 0
+ },
+ {
+ "Id": "endpoint-control",
+ "Text": "\uD83C\uDF9B Endpoint Control",
+ "Level": 0
+ },
+ {
+ "Id": "cookie-transport-behavior",
+ "Text": "\uD83C\uDF6A Cookie \u0026 Transport Behavior",
+ "Level": 0
+ },
+ {
+ "Id": "hub-configuration",
+ "Text": "\uD83C\uDF10 Hub Configuration",
+ "Level": 0
+ },
+ {
+ "Id": "session-resolution",
+ "Text": "\uD83D\uDD01 Session Resolution",
+ "Level": 0
+ },
+ {
+ "Id": "mental-model",
+ "Text": "\uD83E\uDDE0 Mental Model",
+ "Level": 0
+ },
+ {
+ "Id": "key-takeaways",
+ "Text": "\uD83D\uDCCC Key Takeaways",
+ "Level": 0
+ },
+ {
+ "Id": "next-step",
+ "Text": "\u27A1\uFE0F Next Step",
+ "Level": 0
+ }
+ ]
+}
\ No newline at end of file
diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/docs-index.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/docs-index.json
new file mode 100644
index 00000000..ae4a7783
--- /dev/null
+++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/docs-index.json
@@ -0,0 +1,282 @@
+[
+ {
+ "Title": "Getting Started",
+ "Slug": "getting-started/index",
+ "Order": 1,
+ "Group": "getting-started",
+ "GroupOrder": 1
+ },
+ {
+ "Title": "QuickStart",
+ "Slug": "getting-started/quickstart",
+ "Order": 2,
+ "Group": "getting-started",
+ "GroupOrder": 1
+ },
+ {
+ "Title": "Real World Setup",
+ "Slug": "getting-started/real-world-setup",
+ "Order": 3,
+ "Group": "getting-started",
+ "GroupOrder": 1
+ },
+ {
+ "Title": "Fundamental Overview",
+ "Slug": "fundamentals/index",
+ "Order": 1,
+ "Group": "fundamentals",
+ "GroupOrder": 2
+ },
+ {
+ "Title": "Authentication Model",
+ "Slug": "fundamentals/auth-model",
+ "Order": 2,
+ "Group": "fundamentals",
+ "GroupOrder": 2
+ },
+ {
+ "Title": "Flow Based Authentication",
+ "Slug": "fundamentals/flow-based-auth",
+ "Order": 3,
+ "Group": "fundamentals",
+ "GroupOrder": 2
+ },
+ {
+ "Title": "Authentication Modes",
+ "Slug": "fundamentals/auth-modes",
+ "Order": 4,
+ "Group": "fundamentals",
+ "GroupOrder": 2
+ },
+ {
+ "Title": "Client Profiles",
+ "Slug": "fundamentals/client-profiles",
+ "Order": 5,
+ "Group": "fundamentals",
+ "GroupOrder": 2
+ },
+ {
+ "Title": "Runtime Architecture",
+ "Slug": "fundamentals/runtime-architecture",
+ "Order": 6,
+ "Group": "fundamentals",
+ "GroupOrder": 2
+ },
+ {
+ "Title": "Request Lifecycle",
+ "Slug": "fundamentals/request-lifecycle",
+ "Order": 7,
+ "Group": "fundamentals",
+ "GroupOrder": 2
+ },
+ {
+ "Title": "Auth Flows",
+ "Slug": "auth-flows/index",
+ "Order": 1,
+ "Group": "auth-flows",
+ "GroupOrder": 3
+ },
+ {
+ "Title": "Login",
+ "Slug": "auth-flows/login-flow",
+ "Order": 2,
+ "Group": "auth-flows",
+ "GroupOrder": 3
+ },
+ {
+ "Title": "Refresh",
+ "Slug": "auth-flows/refresh-flow",
+ "Order": 3,
+ "Group": "auth-flows",
+ "GroupOrder": 3
+ },
+ {
+ "Title": "Logout",
+ "Slug": "auth-flows/logout-flow",
+ "Order": 4,
+ "Group": "auth-flows",
+ "GroupOrder": 3
+ },
+ {
+ "Title": "Session Lifecycle",
+ "Slug": "auth-flows/session-lifecycle",
+ "Order": 5,
+ "Group": "auth-flows",
+ "GroupOrder": 3
+ },
+ {
+ "Title": "Token Behavior",
+ "Slug": "auth-flows/token-behavior",
+ "Order": 6,
+ "Group": "auth-flows",
+ "GroupOrder": 3
+ },
+ {
+ "Title": "Device Management",
+ "Slug": "auth-flows/device-management",
+ "Order": 7,
+ "Group": "auth-flows",
+ "GroupOrder": 3
+ },
+ {
+ "Title": "Plugin Domains",
+ "Slug": "plugin-domains/index",
+ "Order": 1,
+ "Group": "plugin-domains",
+ "GroupOrder": 4
+ },
+ {
+ "Title": "Users",
+ "Slug": "plugin-domains/users-domain",
+ "Order": 2,
+ "Group": "plugin-domains",
+ "GroupOrder": 4
+ },
+ {
+ "Title": "Credentials",
+ "Slug": "plugin-domains/credential-domain",
+ "Order": 3,
+ "Group": "plugin-domains",
+ "GroupOrder": 4
+ },
+ {
+ "Title": "Authorization",
+ "Slug": "plugin-domains/authorization-domain",
+ "Order": 4,
+ "Group": "plugin-domains",
+ "GroupOrder": 4
+ },
+ {
+ "Title": "Policies",
+ "Slug": "plugin-domains/policies",
+ "Order": 5,
+ "Group": "plugin-domains",
+ "GroupOrder": 4
+ },
+ {
+ "Title": "Client \u0026 API",
+ "Slug": "client/index",
+ "Order": 1,
+ "Group": "client",
+ "GroupOrder": 5
+ },
+ {
+ "Title": "Authentication",
+ "Slug": "client/authentication",
+ "Order": 2,
+ "Group": "client",
+ "GroupOrder": 5
+ },
+ {
+ "Title": "Session Management",
+ "Slug": "client/session-management",
+ "Order": 3,
+ "Group": "client",
+ "GroupOrder": 5
+ },
+ {
+ "Title": "User Management",
+ "Slug": "client/user-management",
+ "Order": 4,
+ "Group": "client",
+ "GroupOrder": 5
+ },
+ {
+ "Title": "Identifiers",
+ "Slug": "client/identifiers",
+ "Order": 5,
+ "Group": "client",
+ "GroupOrder": 5
+ },
+ {
+ "Title": "Credentials",
+ "Slug": "client/credentials",
+ "Order": 6,
+ "Group": "client",
+ "GroupOrder": 5
+ },
+ {
+ "Title": "Authorization",
+ "Slug": "client/authorization",
+ "Order": 7,
+ "Group": "client",
+ "GroupOrder": 5
+ },
+ {
+ "Title": "Configuration",
+ "Slug": "configuration/index",
+ "Order": 1,
+ "Group": "configuration",
+ "GroupOrder": 6
+ },
+ {
+ "Title": "Configuration Overview",
+ "Slug": "configuration/configuration-overview",
+ "Order": 2,
+ "Group": "configuration",
+ "GroupOrder": 6
+ },
+ {
+ "Title": "Server Options",
+ "Slug": "configuration/server-options",
+ "Order": 3,
+ "Group": "configuration",
+ "GroupOrder": 6
+ },
+ {
+ "Title": "Client Options",
+ "Slug": "configuration/client-options",
+ "Order": 4,
+ "Group": "configuration",
+ "GroupOrder": 6
+ },
+ {
+ "Title": "Configuration Sources",
+ "Slug": "configuration/configuration-sources",
+ "Order": 5,
+ "Group": "configuration",
+ "GroupOrder": 6
+ },
+ {
+ "Title": "Advanced Configuration",
+ "Slug": "configuration/advanced-configuration",
+ "Order": 6,
+ "Group": "configuration",
+ "GroupOrder": 6
+ },
+ {
+ "Title": "Session Security Model",
+ "Slug": "security/session-security-model",
+ "Order": 1,
+ "Group": "security",
+ "GroupOrder": 7
+ },
+ {
+ "Title": "Access Token Behavior",
+ "Slug": "security/access-token-behavior",
+ "Order": 2,
+ "Group": "security",
+ "GroupOrder": 7
+ },
+ {
+ "Title": "Refresh Rotation",
+ "Slug": "security/refresh-rotation",
+ "Order": 3,
+ "Group": "security",
+ "GroupOrder": 7
+ },
+ {
+ "Title": "Policy Pipeline",
+ "Slug": "security/policy-pipeline",
+ "Order": 4,
+ "Group": "security",
+ "GroupOrder": 7
+ },
+ {
+ "Title": "\uD83D\uDD10 UltimateAuth Docs",
+ "Slug": "readme",
+ "Order": 999,
+ "Group": "",
+ "GroupOrder": 999
+ }
+]
\ No newline at end of file
diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/auth-model.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/auth-model.json
new file mode 100644
index 00000000..0e9e9033
--- /dev/null
+++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/auth-model.json
@@ -0,0 +1,92 @@
+{
+ "Slug": "fundamentals/auth-model",
+ "Title": "Authentication Model",
+ "Html": "\n\u003Cp\u003EUltimateAuth is built around a simple but powerful idea:\u003C/p\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003EAuthentication is not just a token.\u003Cbr /\u003E\nIt is a \u003Cstrong\u003Estructured, server-controlled session model\u003C/strong\u003E.\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Cp\u003EAt the core of this model are three concepts:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u003Cstrong\u003ERoot\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003E\u003Cstrong\u003EChain\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003E\u003Cstrong\u003ESession\u003C/strong\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003ETogether, they form what we call:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Cstrong\u003EAuthentication Lineage\u003C/strong\u003E\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022the-big-picture\u0022\u003E\uD83D\uDD11 The Big Picture\u003C/h2\u003E\n\u003Cp\u003EInstead of treating authentication as a single token or cookie, UltimateAuth models it as a hierarchy:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003ERoot (user authority)\n\u251C\u2500\u2500 Chain (device context)\n\u2502 \u251C\u2500\u2500 Session (login instance)\n\u2502 \u251C\u2500\u2500 Session\n\u2502\n\u251C\u2500\u2500 Chain\n\u2502 \u251C\u2500\u2500 Session (login instance)\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EEach level has a distinct responsibility.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022root-the-authority\u0022\u003E\uD83E\uDDE9 Root \u2014 The Authority\u003C/h2\u003E\n\u003Cp\u003E\u003Cstrong\u003ERoot\u003C/strong\u003E represents the authentication authority of a user within a tenant.\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EThere is \u003Cstrong\u003Eonly one active Root per user per tenant\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003EIt defines the \u003Cstrong\u003Eglobal security state\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003EIt controls all chains and sessions\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022what-root-does\u0022\u003EWhat Root Does\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ETracks security version (security epoch)\u003C/li\u003E\n\u003Cli\u003EInvalidates all sessions when needed\u003C/li\u003E\n\u003Cli\u003EActs as the \u003Cstrong\u003Esource of truth\u003C/strong\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022example\u0022\u003EExample\u003C/h3\u003E\n\u003Cp\u003EIf a user changes their password:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Root is updated\u003Cbr /\u003E\n\uD83D\uDC49 All existing sessions can become invalid\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022chain-the-device-context\u0022\u003E\uD83D\uDD17 Chain \u2014 The Device Context\u003C/h2\u003E\n\u003Cp\u003E\u003Cstrong\u003EChain\u003C/strong\u003E represents a device or client context.\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EEach device typically has its own chain\u003C/li\u003E\n\u003Cli\u003EMultiple logins from the same device belong to the same chain\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Think of Chain as:\u003C/p\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003E\u201CWhere is the user logged in from?\u201D\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022what-chain-does\u0022\u003EWhat Chain Does\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u003Cp\u003EGroups sessions by device\u003C/p\u003E\n\u003C/li\u003E\n\u003Cli\u003E\u003Cp\u003EEnables \u003Cstrong\u003Edevice-level control\u003C/strong\u003E\u003C/p\u003E\n\u003C/li\u003E\n\u003Cli\u003E\u003Cp\u003EAllows actions like:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELogout from one device\u003C/li\u003E\n\u003Cli\u003ERevoke a specific device\u003C/li\u003E\n\u003C/ul\u003E\n\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022session-the-authentication-instance\u0022\u003E\uD83E\uDDFE Session \u2014 The Authentication Instance\u003C/h2\u003E\n\u003Cp\u003E\u003Cstrong\u003ESession\u003C/strong\u003E is the actual authentication instance.\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ECreated when the user logs in\u003C/li\u003E\n\u003Cli\u003ERepresents a single authenticated state\u003C/li\u003E\n\u003Cli\u003ECarries a snapshot of the Root security version\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This is what gets validated on each request.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022what-session-does\u0022\u003EWhat Session Does\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EProves the user is authenticated\u003C/li\u003E\n\u003Cli\u003ECan be refreshed, revoked, or expired\u003C/li\u003E\n\u003Cli\u003EIs tied to a specific chain\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022how-they-work-together\u0022\u003E\uD83D\uDD04 How They Work Together\u003C/h2\u003E\n\u003Cp\u003EWhen a user logs in:\u003C/p\u003E\n\u003Col\u003E\n\u003Cli\u003ERoot is resolved (or created)\u003C/li\u003E\n\u003Cli\u003EA Chain is identified (device context)\u003C/li\u003E\n\u003Cli\u003EA new Session is created\u003C/li\u003E\n\u003C/ol\u003E\n\u003Cp\u003E\u003Ccode\u003ELogin \u2192 Root \u2192 Chain \u2192 Session\u003C/code\u003E\u003C/p\u003E\n\u003Cp\u003EOn each request:\u003C/p\u003E\n\u003Col\u003E\n\u003Cli\u003ESession is validated\u003C/li\u003E\n\u003Cli\u003EChain context is checked\u003C/li\u003E\n\u003Cli\u003ERoot security version is verified\u003C/li\u003E\n\u003C/ol\u003E\n\u003Cp\u003E\uD83D\uDC49 If any level is invalid \u2192 authentication fails\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022why-this-model-matters\u0022\u003E\uD83D\uDEE1 Why This Model Matters\u003C/h2\u003E\n\u003Cp\u003ETraditional systems rely on:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ECookies\u003C/li\u003E\n\u003Cli\u003EJWT tokens\u003C/li\u003E\n\u003Cli\u003EStateless validation\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EThese approaches have limitations:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ENo real session control\u003C/li\u003E\n\u003Cli\u003EWeak revocation\u003C/li\u003E\n\u003Cli\u003ENo device awareness\u003C/li\u003E\n\u003C/ul\u003E\n\u003Chr /\u003E\n\u003Cp\u003EUltimateAuth solves this by:\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022server-controlled-authentication\u0022\u003E\u2714 Server-Controlled Authentication\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ESessions are always validated server-side\u003C/li\u003E\n\u003Cli\u003ENo blind trust in tokens\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022instant-revocation\u0022\u003E\u2714 Instant Revocation\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ERevoke a session \u2192 immediate effect\u003C/li\u003E\n\u003Cli\u003ERevoke a chain \u2192 device logged out\u003C/li\u003E\n\u003Cli\u003ERevoke root \u2192 disable user (global logout)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022device-awareness\u0022\u003E\u2714 Device Awareness\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EEach device has its own chain\u003C/li\u003E\n\u003Cli\u003ESessions are bound to context\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022strong-security-model\u0022\u003E\u2714 Strong Security Model\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession carries Root security version\u003C/li\u003E\n\u003Cli\u003EOld sessions automatically become invalid\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember only one thing, remember this:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Cstrong\u003ERoot = authority\u003C/strong\u003E\u003Cbr /\u003E\n\uD83D\uDC49 \u003Cstrong\u003EChain = device\u003C/strong\u003E\u003Cbr /\u003E\n\uD83D\uDC49 \u003Cstrong\u003ESession = login\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022key-takeaways\u0022\u003E\uD83D\uDCCC Key Takeaways\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EAuthentication is not just a token\u003C/li\u003E\n\u003Cli\u003ESessions are first-class citizens\u003C/li\u003E\n\u003Cli\u003EThe server always remains in control\u003C/li\u003E\n\u003Cli\u003EDevice and security context are built-in\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003ENow that you understand the core model:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Continue to \u003Cstrong\u003EFlow-Based Authentication\u003C/strong\u003E\u003C/p\u003E\n",
+ "Headings": [
+ {
+ "Id": "the-big-picture",
+ "Text": "\uD83D\uDD11 The Big Picture",
+ "Level": 0
+ },
+ {
+ "Id": "root-the-authority",
+ "Text": "\uD83E\uDDE9 Root \u2014 The Authority",
+ "Level": 0
+ },
+ {
+ "Id": "what-root-does",
+ "Text": "What Root Does",
+ "Level": 1
+ },
+ {
+ "Id": "example",
+ "Text": "Example",
+ "Level": 1
+ },
+ {
+ "Id": "chain-the-device-context",
+ "Text": "\uD83D\uDD17 Chain \u2014 The Device Context",
+ "Level": 0
+ },
+ {
+ "Id": "what-chain-does",
+ "Text": "What Chain Does",
+ "Level": 1
+ },
+ {
+ "Id": "session-the-authentication-instance",
+ "Text": "\uD83E\uDDFE Session \u2014 The Authentication Instance",
+ "Level": 0
+ },
+ {
+ "Id": "what-session-does",
+ "Text": "What Session Does",
+ "Level": 1
+ },
+ {
+ "Id": "how-they-work-together",
+ "Text": "\uD83D\uDD04 How They Work Together",
+ "Level": 0
+ },
+ {
+ "Id": "why-this-model-matters",
+ "Text": "\uD83D\uDEE1 Why This Model Matters",
+ "Level": 0
+ },
+ {
+ "Id": "server-controlled-authentication",
+ "Text": "\u2714 Server-Controlled Authentication",
+ "Level": 1
+ },
+ {
+ "Id": "instant-revocation",
+ "Text": "\u2714 Instant Revocation",
+ "Level": 1
+ },
+ {
+ "Id": "device-awareness",
+ "Text": "\u2714 Device Awareness",
+ "Level": 1
+ },
+ {
+ "Id": "strong-security-model",
+ "Text": "\u2714 Strong Security Model",
+ "Level": 1
+ },
+ {
+ "Id": "mental-model",
+ "Text": "\uD83E\uDDE0 Mental Model",
+ "Level": 0
+ },
+ {
+ "Id": "key-takeaways",
+ "Text": "\uD83D\uDCCC Key Takeaways",
+ "Level": 0
+ },
+ {
+ "Id": "next-step",
+ "Text": "\u27A1\uFE0F Next Step",
+ "Level": 0
+ }
+ ]
+}
\ No newline at end of file
diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/auth-modes.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/auth-modes.json
new file mode 100644
index 00000000..6904fede
--- /dev/null
+++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/auth-modes.json
@@ -0,0 +1,87 @@
+{
+ "Slug": "fundamentals/auth-modes",
+ "Title": "Authentication Modes",
+ "Html": "\u003Cblockquote\u003E\n\u003Cp\u003ENote: SemiHybrid and PureJwt modes will be available on future releases. For now you can safely use PureOpaque and Hybrid modes.\u003C/p\u003E\n\u003C/blockquote\u003E\n\n\u003Cp\u003EUltimateAuth supports multiple authentication modes.\u003C/p\u003E\n\u003Cp\u003EEach mode represents a different balance between:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESecurity\u003C/li\u003E\n\u003Cli\u003EPerformance\u003C/li\u003E\n\u003Cli\u003EControl\u003C/li\u003E\n\u003Cli\u003EClient capabilities\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 You don\u2019t always choose a single model.\u003Cbr /\u003E\nUltimateAuth can adapt based on context.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022available-modes\u0022\u003E\uD83E\uDDE9 Available Modes\u003C/h2\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022pureopaque\u0022\u003EPureOpaque\u003C/h3\u003E\n\u003Cp\u003EFully server-controlled session model.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022hybrid\u0022\u003EHybrid\u003C/h3\u003E\n\u003Cp\u003ECombines session control with token-based access.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022semihybrid\u0022\u003ESemiHybrid\u003C/h3\u003E\n\u003Cp\u003EJWT-based with server-side metadata awareness.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022purejwt\u0022\u003EPureJwt\u003C/h3\u003E\n\u003Cp\u003EFully stateless token-based authentication.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022mode-comparison\u0022\u003E\u2696\uFE0F Mode Comparison\u003C/h2\u003E\n\u003Ctable\u003E\n\u003Cthead\u003E\n\u003Ctr\u003E\n\u003Cth\u003EFeature\u003C/th\u003E\n\u003Cth\u003EPureOpaque\u003C/th\u003E\n\u003Cth\u003EHybrid\u003C/th\u003E\n\u003Cth\u003ESemiHybrid\u003C/th\u003E\n\u003Cth\u003EPureJwt\u003C/th\u003E\n\u003C/tr\u003E\n\u003C/thead\u003E\n\u003Ctbody\u003E\n\u003Ctr\u003E\n\u003Ctd\u003ESessionId\u003C/td\u003E\n\u003Ctd\u003ERequired\u003C/td\u003E\n\u003Ctd\u003ERequired\u003C/td\u003E\n\u003Ctd\u003EMetadata only\u003C/td\u003E\n\u003Ctd\u003ENone\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EAccess Token\u003C/td\u003E\n\u003Ctd\u003E\u274C\u003C/td\u003E\n\u003Ctd\u003E\u2714\u003C/td\u003E\n\u003Ctd\u003E\u2714\u003C/td\u003E\n\u003Ctd\u003E\u2714\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003ERefresh Token\u003C/td\u003E\n\u003Ctd\u003E\u274C\u003C/td\u003E\n\u003Ctd\u003E\u2714\u003C/td\u003E\n\u003Ctd\u003E\u2714\u003C/td\u003E\n\u003Ctd\u003E\u2714\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003ERevocation\u003C/td\u003E\n\u003Ctd\u003EImmediate\u003C/td\u003E\n\u003Ctd\u003EImmediate\u003C/td\u003E\n\u003Ctd\u003EMetadata-based\u003C/td\u003E\n\u003Ctd\u003ENot immediate\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EStatefulness\u003C/td\u003E\n\u003Ctd\u003EFull\u003C/td\u003E\n\u003Ctd\u003EHybrid\u003C/td\u003E\n\u003Ctd\u003ESemi\u003C/td\u003E\n\u003Ctd\u003EStateless\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EServer Control\u003C/td\u003E\n\u003Ctd\u003EFull\u003C/td\u003E\n\u003Ctd\u003EHigh\u003C/td\u003E\n\u003Ctd\u003EMedium\u003C/td\u003E\n\u003Ctd\u003ELow\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EPerformance*\u003C/td\u003E\n\u003Ctd\u003EMedium\u003C/td\u003E\n\u003Ctd\u003EMedium\u003C/td\u003E\n\u003Ctd\u003EHigh\u003C/td\u003E\n\u003Ctd\u003EHighest\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EOffline Support\u003C/td\u003E\n\u003Ctd\u003E\u274C\u003C/td\u003E\n\u003Ctd\u003EPartial\u003C/td\u003E\n\u003Ctd\u003E\u2714\u003C/td\u003E\n\u003Ctd\u003E\u2714\u003C/td\u003E\n\u003C/tr\u003E\n\u003C/tbody\u003E\n\u003C/table\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003E\u26A1 \u003Cstrong\u003EPerformance Note\u003C/strong\u003E\u003C/p\u003E\n\u003Cp\u003EAll modes in UltimateAuth are designed for production use and are highly optimized.\u003C/p\u003E\n\u003Cp\u003EThe differences here are about \u003Cstrong\u003Etrade-offs\u003C/strong\u003E, not absolute speed:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Even the most server-controlled mode is performant enough for real-world applications.\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022how-to-think-about-auth-modes\u0022\u003E\uD83E\uDDE0 How to Think About Auth Modes\u003C/h2\u003E\n\u003Cp\u003EIt\u2019s important to understand that authentication modes in UltimateAuth are not rigid system-wide choices.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 You are not expected to pick a single mode and use it everywhere.\u003C/p\u003E\n\u003Cp\u003EInstead:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EDifferent clients can use different modes on a single UAuthHub\u003C/li\u003E\n\u003Cli\u003EThe mode can change \u003Cstrong\u003Eper request\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003EUltimateAuth selects the most appropriate mode based on \u003Cstrong\u003EClient Profile and runtime context\u003C/strong\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022runtime-driven-behavior\u0022\u003E\uD83D\uDD04 Runtime-Driven Behavior\u003C/h3\u003E\n\u003Cp\u003EIn a typical application:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EBlazor Server \u2192 PureOpaque\u003C/li\u003E\n\u003Cli\u003EBlazor WASM \u2192 Hybrid\u003C/li\u003E\n\u003Cli\u003EAPI \u2192 PureJwt\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 All can coexist in the same system.\u003C/p\u003E\n\u003Cp\u003EYou don\u2019t split your architecture \u2014 UltimateAuth adapts automatically.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022you-can-override-everything\u0022\u003E\u2699\uFE0F You Can Override Everything\u003C/h3\u003E\n\u003Cp\u003EUltimateAuth provides \u003Cstrong\u003Esafe defaults\u003C/strong\u003E, but nothing is locked.\u003C/p\u003E\n\u003Cp\u003EYou can:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EForce a specific auth mode\u003C/li\u003E\n\u003Cli\u003ECustomize behavior per client\u003C/li\u003E\n\u003Cli\u003EReplace default strategies\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 The system is designed to be flexible without sacrificing safety.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022safe-by-default\u0022\u003E\uD83D\uDEE1 Safe by Default\u003C/h3\u003E\n\u003Cp\u003EThe comparison table shows trade-offs \u2014 not risks.\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EAll modes are \u003Cstrong\u003Evalid and supported\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003EChoosing a different mode will not \u201Cbreak\u201D security\u003C/li\u003E\n\u003Cli\u003EIncompatible configurations will \u003Cstrong\u003Efail fast\u003C/strong\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 You are always operating within a safe boundary.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022mental-model\u0022\u003E\uD83D\uDCA1 Mental Model\u003C/h3\u003E\n\u003Cp\u003EThink of auth modes as:\u003C/p\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003EDifferent execution strategies \u2014 not different systems.\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Cp\u003EUltimateAuth remains consistent.\u003Cbr /\u003E\nOnly the \u003Cstrong\u003Ebehavior adapts\u003C/strong\u003E.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022pureopaque-1\u0022\u003E\uD83D\uDD10 PureOpaque\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EFully session-based\u003C/li\u003E\n\u003Cli\u003EEvery request validated on server\u003C/li\u003E\n\u003Cli\u003EMaximum security\u003C/li\u003E\n\u003Cli\u003ETouch semantics instead of refresh rotation\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Best for:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EBlazor Server\u003C/li\u003E\n\u003Cli\u003EInternal apps\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022hybrid-1\u0022\u003E\u26A1 Hybrid\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EAccess token as opaque session id\u003C/li\u003E\n\u003Cli\u003ERefresh token with rotate semantics\u003C/li\u003E\n\u003Cli\u003EServer control with API performance\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Best for:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EBlazor WASM\u003C/li\u003E\n\u003Cli\u003EWeb \u002B API systems\u003C/li\u003E\n\u003Cli\u003EFull-stack apps\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022semihybrid-1\u0022\u003E\uD83D\uDE80 SemiHybrid\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EJWT-based access\u003C/li\u003E\n\u003Cli\u003EServer-side metadata control\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Best for:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EHigh-performance APIs\u003C/li\u003E\n\u003Cli\u003EZero-trust systems\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022purejwt-1\u0022\u003E\uD83C\uDF10 PureJwt\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EFully stateless\u003C/li\u003E\n\u003Cli\u003ENo server-side session control\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Best for:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EExternal APIs\u003C/li\u003E\n\u003Cli\u003EMicroservices\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022which-mode-should-you-use\u0022\u003E\uD83C\uDFAF Which Mode Should You Use?\u003C/h2\u003E\n\u003Ctable\u003E\n\u003Cthead\u003E\n\u003Ctr\u003E\n\u003Cth\u003EScenario\u003C/th\u003E\n\u003Cth\u003ERecommended Mode\u003C/th\u003E\n\u003C/tr\u003E\n\u003C/thead\u003E\n\u003Ctbody\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EBlazor Server\u003C/td\u003E\n\u003Ctd\u003EPureOpaque\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EWeb \u002B API\u003C/td\u003E\n\u003Ctd\u003EHybrid\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EHigh-scale API\u003C/td\u003E\n\u003Ctd\u003ESemiHybrid\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EExternal microservices\u003C/td\u003E\n\u003Ctd\u003EPureJwt\u003C/td\u003E\n\u003C/tr\u003E\n\u003C/tbody\u003E\n\u003C/table\u003E\n",
+ "Headings": [
+ {
+ "Id": "available-modes",
+ "Text": "\uD83E\uDDE9 Available Modes",
+ "Level": 0
+ },
+ {
+ "Id": "pureopaque",
+ "Text": "PureOpaque",
+ "Level": 1
+ },
+ {
+ "Id": "hybrid",
+ "Text": "Hybrid",
+ "Level": 1
+ },
+ {
+ "Id": "semihybrid",
+ "Text": "SemiHybrid",
+ "Level": 1
+ },
+ {
+ "Id": "purejwt",
+ "Text": "PureJwt",
+ "Level": 1
+ },
+ {
+ "Id": "mode-comparison",
+ "Text": "\u2696\uFE0F Mode Comparison",
+ "Level": 0
+ },
+ {
+ "Id": "how-to-think-about-auth-modes",
+ "Text": "\uD83E\uDDE0 How to Think About Auth Modes",
+ "Level": 0
+ },
+ {
+ "Id": "runtime-driven-behavior",
+ "Text": "\uD83D\uDD04 Runtime-Driven Behavior",
+ "Level": 1
+ },
+ {
+ "Id": "you-can-override-everything",
+ "Text": "\u2699\uFE0F You Can Override Everything",
+ "Level": 1
+ },
+ {
+ "Id": "safe-by-default",
+ "Text": "\uD83D\uDEE1 Safe by Default",
+ "Level": 1
+ },
+ {
+ "Id": "mental-model",
+ "Text": "\uD83D\uDCA1 Mental Model",
+ "Level": 1
+ },
+ {
+ "Id": "pureopaque-1",
+ "Text": "\uD83D\uDD10 PureOpaque",
+ "Level": 0
+ },
+ {
+ "Id": "hybrid-1",
+ "Text": "\u26A1 Hybrid",
+ "Level": 0
+ },
+ {
+ "Id": "semihybrid-1",
+ "Text": "\uD83D\uDE80 SemiHybrid",
+ "Level": 0
+ },
+ {
+ "Id": "purejwt-1",
+ "Text": "\uD83C\uDF10 PureJwt",
+ "Level": 0
+ },
+ {
+ "Id": "which-mode-should-you-use",
+ "Text": "\uD83C\uDFAF Which Mode Should You Use?",
+ "Level": 0
+ }
+ ]
+}
\ No newline at end of file
diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/client-profiles.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/client-profiles.json
new file mode 100644
index 00000000..101ce23b
--- /dev/null
+++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/client-profiles.json
@@ -0,0 +1,77 @@
+{
+ "Slug": "fundamentals/client-profiles",
+ "Title": "Client Profiles",
+ "Html": "\n\u003Cp\u003EUltimateAuth adapts its authentication behavior based on the client.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 This is powered by \u003Cstrong\u003EClient Profiles\u003C/strong\u003E.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022what-is-a-client-profile\u0022\u003E\uD83D\uDD11 What is a Client Profile?\u003C/h2\u003E\n\u003Cp\u003EA Client Profile defines how authentication behaves for a specific client type.\u003C/p\u003E\n\u003Cp\u003EIt affects:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EAuthentication mode\u003C/li\u003E\n\u003Cli\u003EFlow behavior\u003C/li\u003E\n\u003Cli\u003EToken usage\u003C/li\u003E\n\u003Cli\u003ESecurity constraints\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EUnlike traditional systems:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 You don\u2019t configure authentication globally\u003Cbr /\u003E\n\uD83D\uDC49 You let the system adapt per client\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022the-key-idea\u0022\u003E\uD83E\uDDE0 The Key Idea\u003C/h2\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003EAuthentication behavior is not static\u003Cbr /\u003E\nIt is determined \u003Cstrong\u003Eper client and per request\u003C/strong\u003E\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022runtime-detection\u0022\u003E\uD83D\uDD04 Runtime Detection\u003C/h2\u003E\n\u003Cp\u003EBy default, UltimateAuth automatically detects the client profile.\u003C/p\u003E\n\u003Cp\u003EThis is done using runtime signals such as:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELoaded assemblies\u003C/li\u003E\n\u003Cli\u003EHosting environment\u003C/li\u003E\n\u003Cli\u003ERegistered services\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022example-detection-logic\u0022\u003EExample Detection Logic\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EMAUI assemblies \u2192 \u003Ccode\u003EMaui\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003EWebAssembly runtime \u2192 \u003Ccode\u003EBlazorWasm\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003EServer components \u2192 \u003Ccode\u003EBlazorServer\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003EUAuthHub marker \u2192 \u003Ccode\u003EUAuthHub\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003EFallback \u2192 \u003Ccode\u003EWebServer\u003C/code\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Detection happens inside the client at startup.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022client-server-propagation\u0022\u003E\uD83D\uDCE1 Client \u2192 Server Propagation\u003C/h2\u003E\n\u003Cp\u003EThe detected client profile is sent to the server on each request.\u003C/p\u003E\n\u003Cp\u003EThis can happen via:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERequest headers\u003C/li\u003E\n\u003Cli\u003EForm fields (for flow-based operations)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EClient \u2192 (ClientProfile) \u2192 Server\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EOn the server:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EThe profile is read from the request\u003C/li\u003E\n\u003Cli\u003EIf not provided \u2192 NotSpecified\u003C/li\u003E\n\u003Cli\u003EServer applies defaults or resolves behavior\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022automatic-vs-manual-configuration\u0022\u003E\u2699\uFE0F Automatic vs Manual Configuration\u003C/h2\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022automatic-default\u0022\u003EAutomatic (Default)\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Ebuilder.Services.AddUltimateAuthClientBlazor();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EIt means:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EAutoDetectClientProfile = true\u003C/li\u003E\n\u003Cli\u003EProfile is resolved automatically\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022manual-override\u0022\u003EManual Override\u003C/h3\u003E\n\u003Cp\u003EYou can explicitly set the client profile:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Ebuilder.Services.AddUltimateAuthClientBlazor(o =\u0026gt;\n{\n o.ClientProfile = UAuthClientProfile.Maui;\n o.AutoDetectClientProfile = false; // optional\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 This is useful when:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERunning in custom hosting environments\u003C/li\u003E\n\u003Cli\u003EDetection is ambiguous\u003C/li\u003E\n\u003Cli\u003EYou want full control\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022built-in-profiles\u0022\u003E\uD83E\uDDE9 Built-in Profiles\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth includes predefined profiles:\u003C/p\u003E\n\u003Ctable\u003E\n\u003Cthead\u003E\n\u003Ctr\u003E\n\u003Cth\u003EProfile\u003C/th\u003E\n\u003Cth\u003EDescription\u003C/th\u003E\n\u003C/tr\u003E\n\u003C/thead\u003E\n\u003Ctbody\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EBlazorServer\u003C/td\u003E\n\u003Ctd\u003EServer-rendered apps\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EBlazorWasm\u003C/td\u003E\n\u003Ctd\u003EBrowser-based WASM apps\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EMaui\u003C/td\u003E\n\u003Ctd\u003ENative mobile apps\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EWebServer\u003C/td\u003E\n\u003Ctd\u003EMVC / Razor / generic server\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EApi\u003C/td\u003E\n\u003Ctd\u003EAPI-only backend\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EUAuthHub\u003C/td\u003E\n\u003Ctd\u003ECentral auth server\u003C/td\u003E\n\u003C/tr\u003E\n\u003C/tbody\u003E\n\u003C/table\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022safe-defaults\u0022\u003E\uD83D\uDEE1 Safe Defaults\u003C/h3\u003E\n\u003Cp\u003EIf no profile is specified (and auto detection is false):\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EClient \u2192 NotSpecified \u2192 Server resolves safely\u003C/li\u003E\n\u003Cli\u003EDefaults are applied\u003C/li\u003E\n\u003Cli\u003EUnsafe combinations are prevented\u003C/li\u003E\n\u003Cli\u003ESystem remains consistent\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022why-this-matters\u0022\u003E\uD83D\uDD10 Why This Matters\u003C/h3\u003E\n\u003Cp\u003EClient Profiles enable:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EMulti-client systems (Web \u002B Mobile \u002B API)\u003C/li\u003E\n\u003Cli\u003ERuntime adaptation\u003C/li\u003E\n\u003Cli\u003ESafe defaults without manual configuration\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EWithout Client Profiles You would need:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESeparate auth setups per client\u003C/li\u003E\n\u003Cli\u003EComplex branching logic\u003C/li\u003E\n\u003Cli\u003EManual security handling\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Client defines behavior\n\uD83D\uDC49 Server enforces rules\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022key-takeaways\u0022\u003E\uD83D\uDCCC Key Takeaways\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EClient Profiles are automatically detected\u003C/li\u003E\n\u003Cli\u003EThey are sent to the server on each request\u003C/li\u003E\n\u003Cli\u003EBehavior adapts per request\u003C/li\u003E\n\u003Cli\u003EYou can override everything when needed\u003C/li\u003E\n\u003Cli\u003ESafe defaults are always applied\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003ENow that you understand runtime behavior:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Continue to Runtime Architecture\u003C/p\u003E\n",
+ "Headings": [
+ {
+ "Id": "what-is-a-client-profile",
+ "Text": "\uD83D\uDD11 What is a Client Profile?",
+ "Level": 0
+ },
+ {
+ "Id": "the-key-idea",
+ "Text": "\uD83E\uDDE0 The Key Idea",
+ "Level": 0
+ },
+ {
+ "Id": "runtime-detection",
+ "Text": "\uD83D\uDD04 Runtime Detection",
+ "Level": 0
+ },
+ {
+ "Id": "example-detection-logic",
+ "Text": "Example Detection Logic",
+ "Level": 1
+ },
+ {
+ "Id": "client-server-propagation",
+ "Text": "\uD83D\uDCE1 Client \u2192 Server Propagation",
+ "Level": 0
+ },
+ {
+ "Id": "automatic-vs-manual-configuration",
+ "Text": "\u2699\uFE0F Automatic vs Manual Configuration",
+ "Level": 0
+ },
+ {
+ "Id": "automatic-default",
+ "Text": "Automatic (Default)",
+ "Level": 1
+ },
+ {
+ "Id": "manual-override",
+ "Text": "Manual Override",
+ "Level": 1
+ },
+ {
+ "Id": "built-in-profiles",
+ "Text": "\uD83E\uDDE9 Built-in Profiles",
+ "Level": 0
+ },
+ {
+ "Id": "safe-defaults",
+ "Text": "\uD83D\uDEE1 Safe Defaults",
+ "Level": 1
+ },
+ {
+ "Id": "why-this-matters",
+ "Text": "\uD83D\uDD10 Why This Matters",
+ "Level": 1
+ },
+ {
+ "Id": "mental-model",
+ "Text": "\uD83E\uDDE0 Mental Model",
+ "Level": 0
+ },
+ {
+ "Id": "key-takeaways",
+ "Text": "\uD83D\uDCCC Key Takeaways",
+ "Level": 0
+ },
+ {
+ "Id": "next-step",
+ "Text": "\u27A1\uFE0F Next Step",
+ "Level": 0
+ }
+ ]
+}
\ No newline at end of file
diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/flow-based-auth.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/flow-based-auth.json
new file mode 100644
index 00000000..1d44015a
--- /dev/null
+++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/flow-based-auth.json
@@ -0,0 +1,97 @@
+{
+ "Slug": "fundamentals/flow-based-auth",
+ "Title": "Flow Based Authentication",
+ "Html": "\n\u003Cp\u003EUltimateAuth is not cookie-based or token-based.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 It is \u003Cstrong\u003Eflow-based\u003C/strong\u003E.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022what-does-flow-based-mean\u0022\u003E\uD83D\uDD11 What Does \u201CFlow-Based\u201D Mean?\u003C/h2\u003E\n\u003Cp\u003EIn traditional systems, authentication is treated as:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EA cookie\u003C/li\u003E\n\u003Cli\u003EA JWT token\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EOnce issued, the system simply checks:\u003C/p\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003E\u201CIs this token valid?\u201D\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Cbr\u003E\n\u003Cp\u003EUltimateAuth takes a different approach:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Authentication is a \u003Cstrong\u003Eseries of controlled flows\u003C/strong\u003E, not a static artifact.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022authentication-as-flows\u0022\u003E\uD83E\uDDED Authentication as Flows\u003C/h2\u003E\n\u003Cp\u003EEvery authentication operation is an explicit flow:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u003Cstrong\u003ELogin\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003E\u003Cstrong\u003ELogout\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003E\u003Cstrong\u003EValidate\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003E\u003Cstrong\u003ERefresh\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003E\u003Cstrong\u003ERe-authentication\u003C/strong\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EEach flow:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EIs initiated intentionally\u003C/li\u003E\n\u003Cli\u003EIs processed on the server\u003C/li\u003E\n\u003Cli\u003EProduces a controlled result\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022example-login-flow\u0022\u003E\uD83D\uDD01 Example: Login Flow\u003C/h2\u003E\n\u003Cp\u003EInstead of:\u003C/p\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003E\u201CGenerate a token and store it\u201D\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Cp\u003EUltimateAuth does:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003ELogin Request\n\u2192 Validate credentials\n\u2192 Resolve Root\n\u2192 Resolve or create Chain\n\u2192 Create Session\n\u2192 Issue authentication grant\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Login is not a single step \u2014 it is a \u003Cstrong\u003Emanaged process\u003C/strong\u003E\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022example-refresh-flow\u0022\u003E\uD83D\uDD04 Example: Refresh Flow\u003C/h2\u003E\n\u003Cp\u003ETraditional systems:\u003C/p\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003ERefresh = issue new token\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Cp\u003EUltimateAuth:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003ERefresh Request\n\u2192 Validate session\n\u2192 Check security constraints\n\u2192 Apply policies (if any)\n\u2192 Optionally rotate tokens\n\u2192 Update session state (if needed)\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 The server decides what actually happens\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022example-validate-flow\u0022\u003E\uD83D\uDD0D Example: Validate Flow\u003C/h2\u003E\n\u003Cp\u003EOn each request:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003EIncoming Request\n\u2192 Extract session/token\n\u2192 Validate session\n\u2192 Check chain (device context)\n\u2192 Verify root security version\n\u2192 Build auth state\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Validation is not just \u201Ctoken valid?\u201D\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022why-token-based-thinking-falls-short\u0022\u003E\u26A0\uFE0F Why Token-Based Thinking Falls Short\u003C/h2\u003E\n\u003Cp\u003EToken-based systems assume:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EThe token contains truth\u003C/li\u003E\n\u003Cli\u003EThe server trusts the token\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EThis leads to:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EWeak revocation\u003C/li\u003E\n\u003Cli\u003ENo device awareness\u003C/li\u003E\n\u003Cli\u003ELimited control\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022ultimateauth-approach\u0022\u003E\u2705 UltimateAuth Approach\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth treats tokens as:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Cstrong\u003Etransport artifacts\u003C/strong\u003E, not sources of truth\u003C/p\u003E\n\u003Cp\u003EThe real authority is:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERoot\u003C/li\u003E\n\u003Cli\u003EChain\u003C/li\u003E\n\u003Cli\u003ESession\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022key-idea\u0022\u003E\uD83E\uDDE0 Key Idea\u003C/h2\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003ETokens carry data\u003Cbr /\u003E\nFlows enforce rules\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022server-controlled-by-design\u0022\u003E\uD83D\uDD10 Server-Controlled by Design\u003C/h2\u003E\n\u003Cp\u003EAll flows are:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EExecuted on the server\u003C/li\u003E\n\u003Cli\u003EEvaluated against policies\u003C/li\u003E\n\u003Cli\u003ESubject to security constraints\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 The client does not control authentication state\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022flow-examples-in-code\u0022\u003E\u2699\uFE0F Flow Examples in Code\u003C/h2\u003E\n\u003Cp\u003EUsing \u003Ccode\u003EIUAuthClient\u003C/code\u003E:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Flows.LoginAsync(request);\nawait UAuthClient.Flows.RefreshAsync();\nawait UAuthClient.Flows.LogoutAsync();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Each method represents a server-driven flow\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022how-this-changes-development\u0022\u003E\uD83E\uDDE9 How This Changes Development\u003C/h2\u003E\n\u003Cp\u003EInstead of thinking:\u003C/p\u003E\n\u003Cp\u003E\u274C \u201CI need to manage tokens\u201D\u003C/p\u003E\n\u003Cp\u003EYou think:\u003C/p\u003E\n\u003Cp\u003E\u2705 \u201CI need to trigger flows\u201D\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022benefits-of-flow-based-authentication\u0022\u003E\uD83D\uDCCC Benefits of Flow-Based Authentication\u003C/h2\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022predictable-behavior\u0022\u003E\u2714 Predictable Behavior\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EEvery action is explicit and controlled.\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022better-security\u0022\u003E\u2714 Better Security\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ENo blind token trust\u003C/li\u003E\n\u003Cli\u003EServer-side validation\u003C/li\u003E\n\u003Cli\u003EPolicy-driven decisions\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022extensibility\u0022\u003E\u2714 Extensibility\u003C/h3\u003E\n\u003Cp\u003EFlows can be extended with:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EMFA\u003C/li\u003E\n\u003Cli\u003ERisk-based checks\u003C/li\u003E\n\u003Cli\u003ECustom policies\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022consistent-across-clients\u0022\u003E\u2714 Consistent Across Clients\u003C/h3\u003E\n\u003Cp\u003ESame flows work for:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EBlazor Server\u003C/li\u003E\n\u003Cli\u003EWASM (PKCE)\u003C/li\u003E\n\u003Cli\u003EAPIs\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Authentication is not a token \u2014 it is a process\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003ENow that you understand flows:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Continue to Auth Modes\u003C/p\u003E\n",
+ "Headings": [
+ {
+ "Id": "what-does-flow-based-mean",
+ "Text": "\uD83D\uDD11 What Does \u201CFlow-Based\u201D Mean?",
+ "Level": 0
+ },
+ {
+ "Id": "authentication-as-flows",
+ "Text": "\uD83E\uDDED Authentication as Flows",
+ "Level": 0
+ },
+ {
+ "Id": "example-login-flow",
+ "Text": "\uD83D\uDD01 Example: Login Flow",
+ "Level": 0
+ },
+ {
+ "Id": "example-refresh-flow",
+ "Text": "\uD83D\uDD04 Example: Refresh Flow",
+ "Level": 0
+ },
+ {
+ "Id": "example-validate-flow",
+ "Text": "\uD83D\uDD0D Example: Validate Flow",
+ "Level": 0
+ },
+ {
+ "Id": "why-token-based-thinking-falls-short",
+ "Text": "\u26A0\uFE0F Why Token-Based Thinking Falls Short",
+ "Level": 0
+ },
+ {
+ "Id": "ultimateauth-approach",
+ "Text": "\u2705 UltimateAuth Approach",
+ "Level": 0
+ },
+ {
+ "Id": "key-idea",
+ "Text": "\uD83E\uDDE0 Key Idea",
+ "Level": 0
+ },
+ {
+ "Id": "server-controlled-by-design",
+ "Text": "\uD83D\uDD10 Server-Controlled by Design",
+ "Level": 0
+ },
+ {
+ "Id": "flow-examples-in-code",
+ "Text": "\u2699\uFE0F Flow Examples in Code",
+ "Level": 0
+ },
+ {
+ "Id": "how-this-changes-development",
+ "Text": "\uD83E\uDDE9 How This Changes Development",
+ "Level": 0
+ },
+ {
+ "Id": "benefits-of-flow-based-authentication",
+ "Text": "\uD83D\uDCCC Benefits of Flow-Based Authentication",
+ "Level": 0
+ },
+ {
+ "Id": "predictable-behavior",
+ "Text": "\u2714 Predictable Behavior",
+ "Level": 1
+ },
+ {
+ "Id": "better-security",
+ "Text": "\u2714 Better Security",
+ "Level": 1
+ },
+ {
+ "Id": "extensibility",
+ "Text": "\u2714 Extensibility",
+ "Level": 1
+ },
+ {
+ "Id": "consistent-across-clients",
+ "Text": "\u2714 Consistent Across Clients",
+ "Level": 1
+ },
+ {
+ "Id": "mental-model",
+ "Text": "\uD83E\uDDE0 Mental Model",
+ "Level": 0
+ },
+ {
+ "Id": "next-step",
+ "Text": "\u27A1\uFE0F Next Step",
+ "Level": 0
+ }
+ ]
+}
\ No newline at end of file
diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/index.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/index.json
new file mode 100644
index 00000000..d5c1b23a
--- /dev/null
+++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/index.json
@@ -0,0 +1,6 @@
+{
+ "Slug": "fundamentals/index",
+ "Title": "Fundamental Overview",
+ "Html": "\n\u003Cp\u003EThis section explains how UltimateAuth works internally.\u003C/p\u003E\n\u003Ch1 id=\u0022section-order\u0022\u003ESection Order\u003C/h1\u003E\n\u003Cp\u003EIf you are new, follow this order:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EAuthentication Model\u003C/li\u003E\n\u003Cli\u003EFlow-Based Authentication\u003C/li\u003E\n\u003Cli\u003EAuthentication Modes\u003C/li\u003E\n\u003Cli\u003EClient Profiles\u003C/li\u003E\n\u003Cli\u003ERuntime Architecture\u003C/li\u003E\n\u003Cli\u003ERequest Lifecycle\u003C/li\u003E\n\u003C/ul\u003E\n",
+ "Headings": []
+}
\ No newline at end of file
diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/request-lifecycle.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/request-lifecycle.json
new file mode 100644
index 00000000..23ab07ac
--- /dev/null
+++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/request-lifecycle.json
@@ -0,0 +1,97 @@
+{
+ "Slug": "fundamentals/request-lifecycle",
+ "Title": "Request Lifecycle",
+ "Html": "\n\u003Cp\u003EThis section explains what happens when a request enters UltimateAuth.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 From the moment an HTTP request arrives\u003Cbr /\u003E\n\uD83D\uDC49 Until authentication state is established or a flow is executed\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022two-types-of-requests\u0022\u003E\uD83E\uDDE0 Two Types of Requests\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth processes requests in two different ways:\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022passive-requests\u0022\u003E1. Passive Requests\u003C/h3\u003E\n\u003Cp\u003ERegular application requests (page load, API call)\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022active-flow-requests\u0022\u003E2. Active Flow Requests\u003C/h3\u003E\n\u003Cp\u003EAuthentication flows (login, refresh, logout)\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Both share the same foundation, but diverge at the flow level.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022middleware-pipeline\u0022\u003E\uD83E\uDDE9 Middleware Pipeline\u003C/h2\u003E\n\u003Cp\u003EEvery request passes through the UltimateAuth middleware pipeline:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003ETenant \u2192 Session Resolution \u2192 (Validation) \u2192 User Resolution\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022tenant-resolution\u0022\u003E\uD83C\uDFE2 Tenant Resolution\u003C/h3\u003E\n\u003Cp\u003EThe system determines the tenant:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EMulti-tenant \u2192 resolved via \u003Ccode\u003EITenantResolver\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003ESingle-tenant \u2192 default context applied\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 If tenant cannot be resolved \u2192 request fails early\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022session-resolution\u0022\u003E\uD83D\uDD10 Session Resolution\u003C/h3\u003E\n\u003Cp\u003EThe system attempts to extract a session:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EFrom headers, cookies, or tokens\u003C/li\u003E\n\u003Cli\u003EConverted into a \u003Ccode\u003ESessionContext\u003C/code\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cpre\u003E\u003Ccode\u003ESessionId \u2192 SessionContext\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 If no session is found \u2192 request becomes anonymous\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022session-validation-resource-apis\u0022\u003E\u2714 Session Validation (Resource APIs)\u003C/h3\u003E\n\u003Cp\u003EFor API scenarios:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession is validated immediately\u003C/li\u003E\n\u003Cli\u003EDevice context is considered\u003C/li\u003E\n\u003Cli\u003EA validation result is attached to the request\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This enables stateless or semi-stateful validation\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022user-resolution\u0022\u003E\uD83D\uDC64 User Resolution\u003C/h3\u003E\n\u003Cp\u003EThe system resolves the current user:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EBased on validated session\u003C/li\u003E\n\u003Cli\u003EUsing \u003Ccode\u003EIUserAccessor\u003C/code\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This produces the final user context\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022passive-request-flow\u0022\u003E\uD83D\uDD04 Passive Request Flow\u003C/h2\u003E\n\u003Cp\u003EFor normal requests:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003ERequest \n\u2192 Middleware pipeline \n\u2192 Session resolved \n\u2192 User resolved \n\u2192 Application executes \n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 No flow execution happens\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022active-flow-requests-1\u0022\u003E\uD83D\uDD10 Active Flow Requests\u003C/h2\u003E\n\u003Cp\u003EFor auth endpoints (login, refresh, etc.):\u003C/p\u003E\n\u003Cp\u003EThe lifecycle continues beyond middleware.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022step-1-flow-detection\u0022\u003EStep 1: Flow Detection\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode\u003EEndpoint \u2192 FlowType\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022step-2-context-creation\u0022\u003EStep 2: Context Creation\u003C/h3\u003E\n\u003Cp\u003EAn \u003Ccode\u003EAuthFlowContext\u003C/code\u003E is created.\u003C/p\u003E\n\u003Cp\u003EIt includes:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EClient profile\u003C/li\u003E\n\u003Cli\u003EEffective mode\u003C/li\u003E\n\u003Cli\u003ETenant\u003C/li\u003E\n\u003Cli\u003EDevice\u003C/li\u003E\n\u003Cli\u003ESession\u003C/li\u003E\n\u003Cli\u003EResponse configuration\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This defines the execution environment\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022step-3-flow-execution\u0022\u003EStep 3: Flow Execution\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode\u003EAuthFlowContext \u2192 Flow Service \u2192 Orchestrator \u2192 Authority\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022step-4-state-mutation\u0022\u003EStep 4: State Mutation\u003C/h3\u003E\n\u003Cp\u003EDepending on the flow:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession may be created, updated, or revoked\u003C/li\u003E\n\u003Cli\u003ETokens may be issued\u003C/li\u003E\n\u003Cli\u003ESecurity state may change\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022step-5-response-generation\u0022\u003EStep 5: Response Generation\u003C/h3\u003E\n\u003Cp\u003EThe system writes the response:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESessionId\u003C/li\u003E\n\u003Cli\u003EAccess token\u003C/li\u003E\n\u003Cli\u003ERefresh token\u003C/li\u003E\n\u003Cli\u003ERedirect (if needed)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022example-login-request\u0022\u003E\uD83D\uDD01 Example: Login Request\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode\u003EHTTP Request \n\u2192 Tenant resolved \n\u2192 Session resolved (anonymous) \n\u2192 Flow detected (Login) \n\u2192 AuthFlowContext created \n\u2192 Credentials validated \n\u2192 Session created \n\u2192 Tokens issued \n\u2192 Response returned \n\u003C/code\u003E\u003C/pre\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022flow-execution-boundary\u0022\u003E\uD83D\uDD10 Flow Execution Boundary\u003C/h2\u003E\n\u003Cp\u003EAuthentication flows are only executed for endpoints explicitly marked with flow metadata.\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERegular requests do not create an AuthFlowContext\u003C/li\u003E\n\u003Cli\u003EAuthFlowContext is only created during flow execution\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This ensures that authentication logic does not interfere with normal application behavior.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003E\uD83D\uDC49 Middleware prepares the request\u003Cbr /\u003E\n\uD83D\uDC49 Flows change the state\u003C/p\u003E\n",
+ "Headings": [
+ {
+ "Id": "two-types-of-requests",
+ "Text": "\uD83E\uDDE0 Two Types of Requests",
+ "Level": 0
+ },
+ {
+ "Id": "passive-requests",
+ "Text": "1. Passive Requests",
+ "Level": 1
+ },
+ {
+ "Id": "active-flow-requests",
+ "Text": "2. Active Flow Requests",
+ "Level": 1
+ },
+ {
+ "Id": "middleware-pipeline",
+ "Text": "\uD83E\uDDE9 Middleware Pipeline",
+ "Level": 0
+ },
+ {
+ "Id": "tenant-resolution",
+ "Text": "\uD83C\uDFE2 Tenant Resolution",
+ "Level": 1
+ },
+ {
+ "Id": "session-resolution",
+ "Text": "\uD83D\uDD10 Session Resolution",
+ "Level": 1
+ },
+ {
+ "Id": "session-validation-resource-apis",
+ "Text": "\u2714 Session Validation (Resource APIs)",
+ "Level": 1
+ },
+ {
+ "Id": "user-resolution",
+ "Text": "\uD83D\uDC64 User Resolution",
+ "Level": 1
+ },
+ {
+ "Id": "passive-request-flow",
+ "Text": "\uD83D\uDD04 Passive Request Flow",
+ "Level": 0
+ },
+ {
+ "Id": "active-flow-requests-1",
+ "Text": "\uD83D\uDD10 Active Flow Requests",
+ "Level": 0
+ },
+ {
+ "Id": "step-1-flow-detection",
+ "Text": "Step 1: Flow Detection",
+ "Level": 1
+ },
+ {
+ "Id": "step-2-context-creation",
+ "Text": "Step 2: Context Creation",
+ "Level": 1
+ },
+ {
+ "Id": "step-3-flow-execution",
+ "Text": "Step 3: Flow Execution",
+ "Level": 1
+ },
+ {
+ "Id": "step-4-state-mutation",
+ "Text": "Step 4: State Mutation",
+ "Level": 1
+ },
+ {
+ "Id": "step-5-response-generation",
+ "Text": "Step 5: Response Generation",
+ "Level": 1
+ },
+ {
+ "Id": "example-login-request",
+ "Text": "\uD83D\uDD01 Example: Login Request",
+ "Level": 0
+ },
+ {
+ "Id": "flow-execution-boundary",
+ "Text": "\uD83D\uDD10 Flow Execution Boundary",
+ "Level": 0
+ },
+ {
+ "Id": "mental-model",
+ "Text": "\uD83E\uDDE0 Mental Model",
+ "Level": 0
+ }
+ ]
+}
\ No newline at end of file
diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/runtime-architecture.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/runtime-architecture.json
new file mode 100644
index 00000000..fd508bf5
--- /dev/null
+++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/runtime-architecture.json
@@ -0,0 +1,72 @@
+{
+ "Slug": "fundamentals/runtime-architecture",
+ "Title": "Runtime Architecture",
+ "Html": "\n\u003Cp\u003EUltimateAuth processes authentication through a structured execution pipeline.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 It is not just middleware-based authentication\u003Cbr /\u003E\n\uD83D\uDC49 It is a \u003Cstrong\u003Eflow-driven execution system\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022the-big-picture\u0022\u003E\uD83E\uDDE0 The Big Picture\u003C/h2\u003E\n\u003Cp\u003EWhen a request reaches an auth endpoint:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003ERequest\n \u2192 Endpoint Filter\n \u2192 AuthFlowContext\n \u2192 Endpoint Handler\n \u2192 Flow Service\n \u2192 Orchestrator\n \u2192 Authority\n \u2192 Stores / Issuers\n \u2192 Response\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EEach step has a clearly defined responsibility.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022request-entry-point\u0022\u003E\uD83D\uDD04 Request Entry Point\u003C/h2\u003E\n\u003Cp\u003EAuthentication begins at the endpoint level.\u003C/p\u003E\n\u003Cp\u003EAn endpoint filter detects the flow:\u003C/p\u003E\n\u003Cp\u003E\u003Ccode\u003EEndpoint \u2192 FlowType (Login, Refresh, Logout\u2026)\u003C/code\u003E\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 The system knows which flow is being executed before any logic runs.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022authflowcontext-the-core-state\u0022\u003E\uD83E\uDDFE AuthFlowContext \u2014 The Core State\u003C/h2\u003E\n\u003Cp\u003EBefore any operation starts, UltimateAuth creates an AuthFlowContext.\u003C/p\u003E\n\u003Cp\u003EThis is the central object that carries:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EClient profile\u003C/li\u003E\n\u003Cli\u003EEffective authentication mode\u003C/li\u003E\n\u003Cli\u003ETenant information\u003C/li\u003E\n\u003Cli\u003EDevice context\u003C/li\u003E\n\u003Cli\u003ESession state\u003C/li\u003E\n\u003Cli\u003EResponse configuration\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This context defines the entire execution environment\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022flow-service-entry-layer\u0022\u003E\u2699\uFE0F Flow Service \u2014 Entry Layer\u003C/h2\u003E\n\u003Cp\u003EAfter the context is created, the request is passed to the Flow Service.\u003C/p\u003E\n\u003Cp\u003EThe Flow Service:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EActs as the entry point for all flows\u003C/li\u003E\n\u003Cli\u003ENormalizes execution\u003C/li\u003E\n\u003Cli\u003EDelegates work to orchestrators\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 It does not implement business logic directly\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022orchestrator-flow-coordinator\u0022\u003E\uD83E\uDDED Orchestrator \u2014 Flow Coordinator\u003C/h2\u003E\n\u003Cp\u003EThe Orchestrator manages the execution of a flow.\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ECoordinates multiple steps\u003C/li\u003E\n\u003Cli\u003EEnsures correct execution order\u003C/li\u003E\n\u003Cli\u003EDelegates decisions to Authority\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Think of it as the flow engine\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022authority-decision-layer\u0022\u003E\uD83D\uDD10 Authority \u2014 Decision Layer\u003C/h2\u003E\n\u003Cp\u003EThe Authority is the most critical component.\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EValidates authentication state\u003C/li\u003E\n\u003Cli\u003EApplies security rules\u003C/li\u003E\n\u003Cli\u003EApproves or rejects operations\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 No sensitive operation bypasses Authority\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022services-stores\u0022\u003E\u2699\uFE0F Services \u0026amp; Stores\u003C/h2\u003E\n\u003Cp\u003EOnce decisions are made:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EServices handle domain logic\u003C/li\u003E\n\u003Cli\u003EStores handle persistence\u003C/li\u003E\n\u003Cli\u003EIssuers generate tokens or session artifacts\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 These layers do not make security decisions\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022end-to-end-example-login\u0022\u003E\uD83D\uDD01 End-to-End Example (Login)\u003C/h2\u003E\n\u003Cp\u003ELogin Request\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003E \u2192 Endpoint Filter (Login Flow)\n \u2192 AuthFlowContext created\n \u2192 Flow Service\n \u2192 Orchestrator\n \u2192 Authority validates credentials\n \u2192 Session created (Store)\n \u2192 Tokens issued (Issuer)\n \u2192 Response generated\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022why-this-architecture-matters\u0022\u003E\uD83E\uDDE0 Why This Architecture Matters\u003C/h2\u003E\n\u003Cp\u003E\u2714 Centralized Decision Making\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EAuthority is always in control\u003C/li\u003E\n\u003Cli\u003ENo scattered validation logic\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\u2714 Predictable Execution\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EEvery flow follows the same pipeline\u003C/li\u003E\n\u003Cli\u003ENo hidden behavior\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\u2714 Extensibility\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EReplace stores\u003C/li\u003E\n\u003Cli\u003EExtend flows\u003C/li\u003E\n\u003Cli\u003ECustomize orchestration\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\u2714 Security by Design\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ENo bypass of Authority\u003C/li\u003E\n\u003Cli\u003EContext-driven validation\u003C/li\u003E\n\u003Cli\u003EFlow-aware execution\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022relation-to-other-concepts\u0022\u003E\uD83D\uDD17 Relation to Other Concepts\u003C/h2\u003E\n\u003Cp\u003EThis architecture connects all previous concepts:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EAuth Model (Root / Chain / Session) \u2192 validated in Authority\u003C/li\u003E\n\u003Cli\u003EAuth Flows \u2192 executed by Orchestrator\u003C/li\u003E\n\u003Cli\u003EAuth Modes \u2192 applied via EffectiveMode\u003C/li\u003E\n\u003Cli\u003EClient Profiles \u2192 influence behavior at runtime\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Flow defines what happens\n\uD83D\uDC49 Context defines how it happens\n\uD83D\uDC49 Authority decides if it happens\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022key-takeaways\u0022\u003E\uD83D\uDCCC Key Takeaways\u003C/h2\u003E\n\u003Cp\u003EAuthentication is executed as a pipeline\nAuthFlowContext carries execution state\nOrchestrator coordinates flows\nAuthority enforces security\nServices and Stores execute operations\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003ENow that you understand the execution model:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Continue to Request Lifecycle\u003C/p\u003E\n",
+ "Headings": [
+ {
+ "Id": "the-big-picture",
+ "Text": "\uD83E\uDDE0 The Big Picture",
+ "Level": 0
+ },
+ {
+ "Id": "request-entry-point",
+ "Text": "\uD83D\uDD04 Request Entry Point",
+ "Level": 0
+ },
+ {
+ "Id": "authflowcontext-the-core-state",
+ "Text": "\uD83E\uDDFE AuthFlowContext \u2014 The Core State",
+ "Level": 0
+ },
+ {
+ "Id": "flow-service-entry-layer",
+ "Text": "\u2699\uFE0F Flow Service \u2014 Entry Layer",
+ "Level": 0
+ },
+ {
+ "Id": "orchestrator-flow-coordinator",
+ "Text": "\uD83E\uDDED Orchestrator \u2014 Flow Coordinator",
+ "Level": 0
+ },
+ {
+ "Id": "authority-decision-layer",
+ "Text": "\uD83D\uDD10 Authority \u2014 Decision Layer",
+ "Level": 0
+ },
+ {
+ "Id": "services-stores",
+ "Text": "\u2699\uFE0F Services \u0026 Stores",
+ "Level": 0
+ },
+ {
+ "Id": "end-to-end-example-login",
+ "Text": "\uD83D\uDD01 End-to-End Example (Login)",
+ "Level": 0
+ },
+ {
+ "Id": "why-this-architecture-matters",
+ "Text": "\uD83E\uDDE0 Why This Architecture Matters",
+ "Level": 0
+ },
+ {
+ "Id": "relation-to-other-concepts",
+ "Text": "\uD83D\uDD17 Relation to Other Concepts",
+ "Level": 0
+ },
+ {
+ "Id": "mental-model",
+ "Text": "\uD83E\uDDE0 Mental Model",
+ "Level": 0
+ },
+ {
+ "Id": "key-takeaways",
+ "Text": "\uD83D\uDCCC Key Takeaways",
+ "Level": 0
+ },
+ {
+ "Id": "next-step",
+ "Text": "\u27A1\uFE0F Next Step",
+ "Level": 0
+ }
+ ]
+}
\ No newline at end of file
diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/getting-started/index.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/getting-started/index.json
new file mode 100644
index 00000000..63bc48a1
--- /dev/null
+++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/getting-started/index.json
@@ -0,0 +1,67 @@
+{
+ "Slug": "getting-started/index",
+ "Title": "Getting Started",
+ "Html": "\n\u003Cp\u003EWelcome to \u003Cstrong\u003EUltimateAuth\u003C/strong\u003E \u2014 the modern authentication framework for .NET.\u003C/p\u003E\n\u003Cp\u003EUltimateAuth is designed to make authentication \u003Cstrong\u003Esimple to use\u003C/strong\u003E, while still being \u003Cstrong\u003Epowerful, flexible, and deeply secure\u003C/strong\u003E enough for real-world applications.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022what-is-ultimateauth\u0022\u003EWhat is UltimateAuth?\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth is a \u003Cstrong\u003Eflow-driven authentication framework\u003C/strong\u003E that reimagines how authentication works in modern .NET applications.\u003C/p\u003E\n\u003Cp\u003EIt unifies:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession-based authentication\u003C/li\u003E\n\u003Cli\u003EToken-based authentication (JWT)\u003C/li\u003E\n\u003Cli\u003EPKCE flows for public clients\u003C/li\u003E\n\u003Cli\u003EMulti-client environments (Blazor Server, WASM, MAUI, APIs)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003Einto a single, consistent system.\u003C/p\u003E\n\u003Cp\u003EInstead of choosing between cookies, sessions, or tokens, UltimateAuth allows you to use \u003Cstrong\u003Ethe right model for each scenario \u2014 automatically\u003C/strong\u003E.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022what-makes-ultimateauth-different\u0022\u003EWhat Makes UltimateAuth Different?\u003C/h2\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022session-is-a-first-class-concept\u0022\u003E\uD83D\uDD39 Session is a First-Class Concept\u003C/h3\u003E\n\u003Cp\u003EUnlike traditional systems, UltimateAuth treats sessions as a \u003Cstrong\u003Ecore domain\u003C/strong\u003E, not a side effect.\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERoot \u2192 global security authority\u003C/li\u003E\n\u003Cli\u003EChain \u2192 device context\u003C/li\u003E\n\u003Cli\u003ESession \u2192 actual authentication instance\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EThis allows:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EInstant revocation\u003C/li\u003E\n\u003Cli\u003EMulti-device control\u003C/li\u003E\n\u003Cli\u003ESecure session lifecycle management\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022flow-based-not-token-based\u0022\u003E\uD83D\uDD39 Flow-Based, Not Token-Based\u003C/h3\u003E\n\u003Cp\u003EUltimateAuth is not cookie-based or token-based.\u003C/p\u003E\n\u003Cp\u003EIt is \u003Cstrong\u003Eflow-based\u003C/strong\u003E:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELogin is a flow\u003C/li\u003E\n\u003Cli\u003ERefresh is a flow\u003C/li\u003E\n\u003Cli\u003ERe-authentication is a flow\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Authentication becomes \u003Cstrong\u003Eexplicit, predictable, and controllable\u003C/strong\u003E\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022built-for-blazor-and-modern-clients\u0022\u003E\uD83D\uDD39 Built for Blazor and Modern Clients\u003C/h3\u003E\n\u003Cp\u003EUltimateAuth is designed from the ground up for:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EBlazor Server\u003C/li\u003E\n\u003Cli\u003EBlazor WASM\u003C/li\u003E\n\u003Cli\u003E.NET MAUI\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EWith:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ENative PKCE support\u003C/li\u003E\n\u003Cli\u003EBuilt-in Blazor components (\u003Ccode\u003EUAuthLoginForm\u003C/code\u003E, \u003Ccode\u003EUAuthApp\u003C/code\u003E)\u003C/li\u003E\n\u003Cli\u003EAutomatic client profile detection\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 No hacks. No manual glue code.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022runtime-aware-authentication\u0022\u003E\uD83D\uDD39 Runtime-Aware Authentication\u003C/h3\u003E\n\u003Cp\u003EAuthentication behavior is not static.\u003C/p\u003E\n\u003Cp\u003EUltimateAuth adapts based on:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EClient type\u003C/li\u003E\n\u003Cli\u003EAuth mode\u003C/li\u003E\n\u003Cli\u003ERequest context\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Same system, different optimized behavior.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022what-problems-it-solves\u0022\u003EWhat Problems It Solves\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth addresses real-world challenges:\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022multiple-client-types\u0022\u003E\uD83D\uDD39 Multiple Client Types\u003C/h3\u003E\n\u003Cp\u003EBlazor Server, WASM, MAUI, and APIs all behave differently.\u003C/p\u003E\n\u003Cp\u003EUltimateAuth handles these differences automatically using \u003Cstrong\u003EClient Profiles\u003C/strong\u003E.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022session-vs-token-confusion\u0022\u003E\uD83D\uDD39 Session vs Token Confusion\u003C/h3\u003E\n\u003Cp\u003EShould you use cookies, sessions, or JWT?\u003C/p\u003E\n\u003Cp\u003EUltimateAuth removes this decision by supporting multiple auth modes and selecting the correct behavior at runtime.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022secure-session-management\u0022\u003E\uD83D\uDD39 Secure Session Management\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EDevice-aware sessions\u003C/li\u003E\n\u003Cli\u003ESession revocation\u003C/li\u003E\n\u003Cli\u003ERefresh token rotation\u003C/li\u003E\n\u003Cli\u003EReplay protection\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EAll built-in \u2014 no custom implementation required.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022complex-auth-flows\u0022\u003E\uD83D\uDD39 Complex Auth Flows\u003C/h3\u003E\n\u003Cp\u003ELogin, logout, refresh, PKCE, multi-device control etc.\u003C/p\u003E\n\u003Cp\u003EAll exposed as \u003Cstrong\u003Esimple application-level APIs\u003C/strong\u003E.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022when-to-use-ultimateauth\u0022\u003EWhen to Use UltimateAuth\u003C/h2\u003E\n\u003Cp\u003EUse UltimateAuth if:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EYou are building a modern .NET application \u003Cstrong\u003EBlazor Server, WASM or MAUI\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003EYou need \u003Cstrong\u003Esession \u002B token hybrid authentication\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003EYou want \u003Cstrong\u003Efull control over authentication flows\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003EYou are building a \u003Cstrong\u003Emulti-client system (web \u002B mobile \u002B API)\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003EYou need \u003Cstrong\u003Estrong security with extensibility\u003C/strong\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Continue to \u003Cstrong\u003EQuick Start\u003C/strong\u003E to build your first UltimateAuth application.\u003C/p\u003E\n",
+ "Headings": [
+ {
+ "Id": "what-is-ultimateauth",
+ "Text": "What is UltimateAuth?",
+ "Level": 0
+ },
+ {
+ "Id": "what-makes-ultimateauth-different",
+ "Text": "What Makes UltimateAuth Different?",
+ "Level": 0
+ },
+ {
+ "Id": "session-is-a-first-class-concept",
+ "Text": "\uD83D\uDD39 Session is a First-Class Concept",
+ "Level": 1
+ },
+ {
+ "Id": "flow-based-not-token-based",
+ "Text": "\uD83D\uDD39 Flow-Based, Not Token-Based",
+ "Level": 1
+ },
+ {
+ "Id": "built-for-blazor-and-modern-clients",
+ "Text": "\uD83D\uDD39 Built for Blazor and Modern Clients",
+ "Level": 1
+ },
+ {
+ "Id": "runtime-aware-authentication",
+ "Text": "\uD83D\uDD39 Runtime-Aware Authentication",
+ "Level": 1
+ },
+ {
+ "Id": "what-problems-it-solves",
+ "Text": "What Problems It Solves",
+ "Level": 0
+ },
+ {
+ "Id": "multiple-client-types",
+ "Text": "\uD83D\uDD39 Multiple Client Types",
+ "Level": 1
+ },
+ {
+ "Id": "session-vs-token-confusion",
+ "Text": "\uD83D\uDD39 Session vs Token Confusion",
+ "Level": 1
+ },
+ {
+ "Id": "secure-session-management",
+ "Text": "\uD83D\uDD39 Secure Session Management",
+ "Level": 1
+ },
+ {
+ "Id": "complex-auth-flows",
+ "Text": "\uD83D\uDD39 Complex Auth Flows",
+ "Level": 1
+ },
+ {
+ "Id": "when-to-use-ultimateauth",
+ "Text": "When to Use UltimateAuth",
+ "Level": 0
+ }
+ ]
+}
\ No newline at end of file
diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/getting-started/quickstart.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/getting-started/quickstart.json
new file mode 100644
index 00000000..8caf8257
--- /dev/null
+++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/getting-started/quickstart.json
@@ -0,0 +1,62 @@
+{
+ "Slug": "getting-started/quickstart",
+ "Title": "QuickStart",
+ "Html": "\n\u003Cp\u003EIn this guide, you will set up UltimateAuth in a few minutes and perform your \u003Cstrong\u003Efirst login\u003C/strong\u003E.\u003C/p\u003E\n\u003Chr /\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022create-a-project\u0022\u003E1. Create a Project\u003C/h2\u003E\n\u003Cp\u003ECreate a new Blazor Server web app:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-bash\u0022\u003Edotnet new blazorserver -n UltimateAuthDemo\ncd UltimateAuthDemo\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022install-packages\u0022\u003E2. Install Packages\u003C/h2\u003E\n\u003Cp\u003EAdd UltimateAuth packages:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Edotnet add package CodeBeam.UltimateAuth.Server\ndotnet add package CodeBeam.UltimateAuth.Client.Blazor\ndotnet add package CodeBeam.UltimateAuth.InMemory.Bundle\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022configure-services\u0022\u003E3. Configure Services\u003C/h2\u003E\n\u003Cp\u003EUpdate your Program.cs:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Ebuilder.Services\n .AddUltimateAuthServer()\n .AddUltimateAuthInMemory();\n\nbuilder.Services\n .AddUltimateAuthClientBlazor();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022configure-middleware\u0022\u003E4. Configure Middleware\u003C/h2\u003E\n\u003Cp\u003EIn \u003Ccode\u003EProgram.cs\u003C/code\u003E\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eapp.UseUltimateAuthWithAspNetCore();\napp.MapUltimateAuthEndpoints();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022enable-blazor-integration\u0022\u003E5. Enable Blazor Integration\u003C/h2\u003E\n\u003Cp\u003EIn \u003Ccode\u003EProgram.cs\u003C/code\u003E\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eapp.MapRazorComponents\u0026lt;App\u0026gt;()\n .AddInteractiveServerRenderMode() // or webassembly (depends on your application type)\n .AddUltimateAuthRoutes(UAuthAssemblies.BlazorClient());\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022add-uauth-script\u0022\u003E6. Add UAuth Script\u003C/h2\u003E\n\u003Cp\u003EAdd this to \u003Ccode\u003EApp.razor\u003C/code\u003E or \u003Ccode\u003Eindex.html\u003C/code\u003E:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003E\u0026lt;script src=\u0026quot;_content/CodeBeam.UltimateAuth.Client.Blazor/uauth.min.js\u0026quot;\u0026gt;\u0026lt;/script\u0026gt;\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022configure-application-lifecycle\u0022\u003E7. Configure Application Lifecycle\u003C/h2\u003E\n\u003Cp\u003EReplace \u003Ccode\u003ERoutes.razor\u003C/code\u003E with this code:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003E\u0026lt;UAuthApp UseBuiltInRouter=\u0026quot;true\u0026quot; AppAssembly=\u0026quot;typeof(Program).Assembly\u0026quot; DefaultLayout=\u0026quot;typeof(Layout.MainLayout)\u0026quot; /\u0026gt;\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022perform-your-first-login\u0022\u003E8. Perform Your First Login\u003C/h2\u003E\n\u003Cp\u003EExample using IUAuthClient:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003E[Inject] IUAuthClient UAuthClient { get; set; } = null!;\n\nprivate async Task Login()\n{\n await UAuthClient.Flows.LoginAsync(new LoginRequest\n {\n Identifier = \u0026quot;demo\u0026quot;,\n Secret = \u0026quot;password\u0026quot;\n });\n}\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022thats-it\u0022\u003E\uD83C\uDF89 That\u2019s It\u003C/h2\u003E\n\u003Cp\u003EYou now have a working authentication system with:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession-based authentication\u003C/li\u003E\n\u003Cli\u003EAutomatic client detection\u003C/li\u003E\n\u003Cli\u003EBuilt-in login flow\u003C/li\u003E\n\u003Cli\u003ESecure session handling\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022what-just-happened\u0022\u003EWhat Just Happened?\u003C/h2\u003E\n\u003Cp\u003EWhen you logged in:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EA session (with root and chain) was created on the server,\u003C/li\u003E\n\u003Cli\u003EYour client received an authentication grant (cookie or token),\u003C/li\u003E\n\u003Cli\u003EUltimateAuth established your auth state automatically.\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 You didn\u2019t manage cookies, tokens, or redirects manually.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022next-steps\u0022\u003ENext Steps\u003C/h2\u003E\n\u003Cp\u003EDiscover the setup for real world applications with entity framework core.\u003C/p\u003E\n",
+ "Headings": [
+ {
+ "Id": "create-a-project",
+ "Text": "1. Create a Project",
+ "Level": 0
+ },
+ {
+ "Id": "install-packages",
+ "Text": "2. Install Packages",
+ "Level": 0
+ },
+ {
+ "Id": "configure-services",
+ "Text": "3. Configure Services",
+ "Level": 0
+ },
+ {
+ "Id": "configure-middleware",
+ "Text": "4. Configure Middleware",
+ "Level": 0
+ },
+ {
+ "Id": "enable-blazor-integration",
+ "Text": "5. Enable Blazor Integration",
+ "Level": 0
+ },
+ {
+ "Id": "add-uauth-script",
+ "Text": "6. Add UAuth Script",
+ "Level": 0
+ },
+ {
+ "Id": "configure-application-lifecycle",
+ "Text": "7. Configure Application Lifecycle",
+ "Level": 0
+ },
+ {
+ "Id": "perform-your-first-login",
+ "Text": "8. Perform Your First Login",
+ "Level": 0
+ },
+ {
+ "Id": "thats-it",
+ "Text": "\uD83C\uDF89 That\u2019s It",
+ "Level": 0
+ },
+ {
+ "Id": "what-just-happened",
+ "Text": "What Just Happened?",
+ "Level": 0
+ },
+ {
+ "Id": "next-steps",
+ "Text": "Next Steps",
+ "Level": 0
+ }
+ ]
+}
\ No newline at end of file
diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/getting-started/real-world-setup.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/getting-started/real-world-setup.json
new file mode 100644
index 00000000..24236da8
--- /dev/null
+++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/getting-started/real-world-setup.json
@@ -0,0 +1,47 @@
+{
+ "Slug": "getting-started/real-world-setup",
+ "Title": "Real World Setup",
+ "Html": "\n\u003Cp\u003EThe Quick Start uses an in-memory setup for simplicity.\nIn real-world applications, you should replace it with a persistent configuration as shown below.\u003C/p\u003E\n\u003Cp\u003EIn real applications, you will typically configure:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EA persistent database\u003C/li\u003E\n\u003Cli\u003EAn appropriate client profile\u003C/li\u003E\n\u003Cli\u003EA suitable authentication mode\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EThis guide shows how to set up UltimateAuth for real-world scenarios.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022using-entity-framework-core\u0022\u003E\uD83D\uDDC4\uFE0F Using Entity Framework Core\u003C/h2\u003E\n\u003Cp\u003EFor production, you should use a persistent store. In this setup, you no longer need the \u003Ccode\u003ECodeBeam.UltimateAuth.InMemory.Bundle\u003C/code\u003E package.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022install-packages\u0022\u003EInstall Packages\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-bash\u0022\u003Edotnet add package CodeBeam.UltimateAuth.EntityFrameworkCore.Bundle\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022configure-services\u0022\u003EConfigure Services\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Ebuilder.Services\n .AddUltimateAuthServer()\n .AddUltimateAuthEntityFrameworkCore(db =\u0026gt;\n {\n db.UseSqlite(\u0026quot;Data Source=uauth.db\u0026quot;);\n // or UseSqlServer / UseNpgsql\n });\n\nbuilder.Services\n .AddUltimateAuthClientBlazor();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022create-database-migrations\u0022\u003ECreate Database \u0026amp; Migrations\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-bash\u0022\u003Edotnet ef migrations add InitUAuth\ndotnet ef database update\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003Eor\u003C/p\u003E\n\u003Cp\u003EIf you are using Visual Studio, you can run these commands in Package Manager Console*:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-bash\u0022\u003EAdd-Migration InitUAuth -Context UAuthDbContext\nUpdate-Database -Context UAuthDbContext\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E*Needs \u003Ccode\u003EMicrosoft.EntityFrameworkCore.Design\u003C/code\u003E and \u003Ccode\u003EMicrosoft.EntityFrameworkCore.Tools\u003C/code\u003E\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022configure-services-with-options\u0022\u003EConfigure Services With Options\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth provides rich options for server and client service registration.\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Ebuilder.Services.AddUltimateAuthServer(o =\u0026gt; {\n o.Diagnostics.EnableRefreshDetails = true;\n o.Login.MaxFailedAttempts = 4;\n o.Identifiers.AllowMultipleUsernames = true;\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022blazor-wasm-setup\u0022\u003EBlazor WASM Setup\u003C/h2\u003E\n\u003Cp\u003EBlazor WASM applications run entirely on the client and cannot securely handle credentials.\nFor this reason, UltimateAuth uses a dedicated Auth server called \u003Cstrong\u003EUAuthHub\u003C/strong\u003E.\u003C/p\u003E\n\u003Cp\u003EWASM \u003Ccode\u003EProgram.cs\u003C/code\u003E:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Ebuilder.Services.AddUltimateAuthClientBlazor(o =\u0026gt;\n{\n o.Endpoints.BasePath = \u0026quot;https://localhost:6110/auth\u0026quot;; // UAuthHub URL\n o.Pkce.ReturnUrl = \u0026quot;https://localhost:6130/home\u0026quot;; // Your (WASM) application domain \u002B return path\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EUAuthHub \u003Ccode\u003EProgram.cs\u003C/code\u003E:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Ebuilder.Services.AddUltimateAuthServer()\n .AddUltimateAuthInMemory()\n .AddUAuthHub(o =\u0026gt; o.AllowedClientOrigins.Add(\u0026quot;https://localhost:6130\u0026quot;)); // WASM application\u0027s URL\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EUAuthHub Pipeline Configuration\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eapp.MapUltimateAuthEndpoints();\napp.MapUAuthHub();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003E\u2139\uFE0F UltimateAuth automatically selects the appropriate authentication mode (PureOpaque, Hybrid, etc.) based on the client type.\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022resourceapi-setup\u0022\u003EResourceApi Setup\u003C/h2\u003E\n\u003Cp\u003EYou may want to secure your custom API with UltimateAuth. UltimateAuth provides a lightweight option for this case. (ResourceApi doesn\u0027t have to be a blazor application, it can be any server-side project like MVC.)\u003C/p\u003E\n\u003Cp\u003EResourceApi\u0027s \u003Ccode\u003EProgram.cs\u003C/code\u003E\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Ebuilder.Services.AddUltimateAuthResourceApi(o =\u0026gt;\n {\n o.UAuthHubBaseUrl = \u0026quot;https://localhost:6110\u0026quot;;\n o.AllowedClientOrigins.Add(\u0026quot;https://localhost:6130\u0026quot;);\n });\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EConfigure pipeline:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eapp.UseUltimateAuthResourceApiWithAspNetCore();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003ENotes:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EResourceApi should connect with an UAuthHub, not a pure-server. Make sure \u003Ccode\u003E.AddUAuthHub()\u003C/code\u003E after calling \u003Ccode\u003Ebuilder.Services.AddUltimateAuthServer()\u003C/code\u003E.\u003C/li\u003E\n\u003Cli\u003EUltimateAuth automatically configures CORS based on the provided origins.\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EUse ResourceApi when:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EYou have a separate backend API\u003C/li\u003E\n\u003Cli\u003EYou want to validate sessions or tokens externally\u003C/li\u003E\n\u003Cli\u003EYour API is not hosting UltimateAuth directly\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022how-to-think-about-setup\u0022\u003E\uD83E\uDDE0 How to Think About Setup\u003C/h2\u003E\n\u003Cp\u003EIn UltimateAuth:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EThe \u003Cstrong\u003EServer\u003C/strong\u003E manages authentication flows and sessions\u003C/li\u003E\n\u003Cli\u003EThe \u003Cstrong\u003EClient\u003C/strong\u003E interacts through flows (not tokens directly)\u003C/li\u003E\n\u003Cli\u003EThe \u003Cstrong\u003EStorage layer\u003C/strong\u003E (InMemory / EF Core) defines persistence\u003C/li\u003E\n\u003Cli\u003EThe \u003Cstrong\u003EApplication type\u003C/strong\u003E determines runtime behavior\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 You configure the system once, and UltimateAuth adapts automatically.\u003C/p\u003E\n",
+ "Headings": [
+ {
+ "Id": "using-entity-framework-core",
+ "Text": "\uD83D\uDDC4\uFE0F Using Entity Framework Core",
+ "Level": 0
+ },
+ {
+ "Id": "install-packages",
+ "Text": "Install Packages",
+ "Level": 1
+ },
+ {
+ "Id": "configure-services",
+ "Text": "Configure Services",
+ "Level": 1
+ },
+ {
+ "Id": "create-database-migrations",
+ "Text": "Create Database \u0026 Migrations",
+ "Level": 1
+ },
+ {
+ "Id": "configure-services-with-options",
+ "Text": "Configure Services With Options",
+ "Level": 0
+ },
+ {
+ "Id": "blazor-wasm-setup",
+ "Text": "Blazor WASM Setup",
+ "Level": 0
+ },
+ {
+ "Id": "resourceapi-setup",
+ "Text": "ResourceApi Setup",
+ "Level": 0
+ },
+ {
+ "Id": "how-to-think-about-setup",
+ "Text": "\uD83E\uDDE0 How to Think About Setup",
+ "Level": 0
+ }
+ ]
+}
\ No newline at end of file
diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/plugin-domains/authorization-domain.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/plugin-domains/authorization-domain.json
new file mode 100644
index 00000000..d69338a7
--- /dev/null
+++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/plugin-domains/authorization-domain.json
@@ -0,0 +1,77 @@
+{
+ "Slug": "plugin-domains/authorization-domain",
+ "Title": "Authorization",
+ "Html": "\n\u003Cp\u003EUltimateAuth provides a flexible and extensible authorization system based on:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERoles\u003C/li\u003E\n\u003Cli\u003EPermissions\u003C/li\u003E\n\u003Cli\u003EPolicies\u003C/li\u003E\n\u003Cli\u003EAccess orchestration\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022core-concepts\u0022\u003E\uD83E\uDDE9 Core Concepts\u003C/h2\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022permissions\u0022\u003E\uD83D\uDD11 Permissions\u003C/h3\u003E\n\u003Cp\u003EIn UltimateAuth, permissions are not just arbitrary strings.\u003C/p\u003E\n\u003Cp\u003EThey follow a \u003Cstrong\u003Estructured action model\u003C/strong\u003E.\u003C/p\u003E\n\u003Ch4 id=\u0022permission-structure\u0022\u003E\uD83E\uDDE9 Permission Structure\u003C/h4\u003E\n\u003Cp\u003EPermissions are built using a consistent format:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003Eresource.operation.scope\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003Eor\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003Eresource.subresource.operation.scope\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch4 id=\u0022examples\u0022\u003E\u2705 Examples\u003C/h4\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u003Ccode\u003Eusers.create.admin\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003E\u003Ccode\u003Eusers.profile.update.self\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003E\u003Ccode\u003Esessions.revokechain.admin\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003E\u003Ccode\u003Ecredentials.change.self\u003C/code\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This structure is not accidental \u2014\u003Cbr /\u003E\nit is \u003Cstrong\u003Edesigned for consistency, readability, and policy evaluation\u003C/strong\u003E.\u003C/p\u003E\n\u003Chr /\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022built-in-action-catalog\u0022\u003E\u2699\uFE0F Built-in Action Catalog\u003C/h3\u003E\n\u003Cp\u003EUltimateAuth provides a predefined action catalog.\u003C/p\u003E\n\u003Cp\u003EExamples:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u003Ccode\u003Eflows.logout.self\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003E\u003Ccode\u003Esessions.listchains.admin\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003E\u003Ccode\u003Eusers.delete.self\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003E\u003Ccode\u003Ecredentials.revoke.admin\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003E\u003Ccode\u003Eauthorization.roles.assign.admin\u003C/code\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This ensures:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ENo magic strings\u003C/li\u003E\n\u003Cli\u003EDiscoverable permissions\u003C/li\u003E\n\u003Cli\u003EConsistent naming across the system\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022scope-semantics\u0022\u003E\uD83E\uDDE0 Scope Semantics\u003C/h3\u003E\n\u003Cp\u003EThe last part of the permission defines \u003Cstrong\u003Escope\u003C/strong\u003E:\u003C/p\u003E\n\u003Ctable\u003E\n\u003Cthead\u003E\n\u003Ctr\u003E\n\u003Cth\u003EScope\u003C/th\u003E\n\u003Cth\u003EMeaning\u003C/th\u003E\n\u003C/tr\u003E\n\u003C/thead\u003E\n\u003Ctbody\u003E\n\u003Ctr\u003E\n\u003Ctd\u003E\u003Ccode\u003Eself\u003C/code\u003E\u003C/td\u003E\n\u003Ctd\u003EUser acts on own resources\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003E\u003Ccode\u003Eadmin\u003C/code\u003E\u003C/td\u003E\n\u003Ctd\u003EUser acts on other users\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003E\u003Ccode\u003Eanonymous\u003C/code\u003E\u003C/td\u003E\n\u003Ctd\u003ENo authentication required\u003C/td\u003E\n\u003C/tr\u003E\n\u003C/tbody\u003E\n\u003C/table\u003E\n\u003Cbr\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022wildcards-grouping\u0022\u003E\uD83C\uDF32 Wildcards \u0026amp; Grouping\u003C/h3\u003E\n\u003Cp\u003EPermissions support hierarchical matching:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u003Ccode\u003Eusers.*\u003C/code\u003E \u2192 all user actions\u003C/li\u003E\n\u003Cli\u003E\u003Ccode\u003Eusers.profile.*\u003C/code\u003E \u2192 all profile operations\u003C/li\u003E\n\u003Cli\u003E\u003Ccode\u003E*\u003C/code\u003E \u2192 full access\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022normalization\u0022\u003E\u26A1 Normalization\u003C/h3\u003E\n\u003Cp\u003EPermissions are automatically normalized:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EFull coverage \u2192 replaced with \u003Ccode\u003E*\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003EFull group \u2192 replaced with \u003Ccode\u003Eprefix.*\u003C/code\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022role\u0022\u003ERole\u003C/h3\u003E\n\u003Cp\u003EA role is a collection of permissions.\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERoles are tenant-scoped\u003C/li\u003E\n\u003Cli\u003ERoles can be dynamically updated\u003C/li\u003E\n\u003Cli\u003EPermissions are normalized internally\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022userrole\u0022\u003EUserRole\u003C/h3\u003E\n\u003Cp\u003EUsers are assigned roles:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EMany-to-many relationship\u003C/li\u003E\n\u003Cli\u003EAssignment is timestamped\u003C/li\u003E\n\u003Cli\u003ERole resolution is runtime-based\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022permission-resolution\u0022\u003E\uD83D\uDD04 Permission Resolution\u003C/h2\u003E\n\u003Cp\u003EPermissions are evaluated using:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EExact match\u003C/li\u003E\n\u003Cli\u003EPrefix match\u003C/li\u003E\n\u003Cli\u003EWildcard match\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003ECompiledPermissionSet optimizes runtime checks.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022claims-integration\u0022\u003E\uD83E\uDDE0 Claims Integration\u003C/h2\u003E\n\u003Cp\u003EAuthorization integrates with authentication via claims:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERoles \u2192 \u003Ccode\u003EClaimTypes.Role\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003EPermissions \u2192 \u003Ccode\u003Epermission\u003C/code\u003E claim\u003C/li\u003E\n\u003Cli\u003ETenant \u2192 \u003Ccode\u003Etenant\u003C/code\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EThis allows:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EToken-based authorization\u003C/li\u003E\n\u003Cli\u003EStateless permission checks (for JWT modes)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022authorization-flow\u0022\u003E\u2699\uFE0F Authorization Flow\u003C/h2\u003E\n\u003Cp\u003EAuthorization is executed through:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 AccessOrchestrator\u003C/p\u003E\n\u003Cp\u003ESteps:\u003C/p\u003E\n\u003Col\u003E\n\u003Cli\u003EBuild AccessContext\u003C/li\u003E\n\u003Cli\u003EExecute policies\u003C/li\u003E\n\u003Cli\u003EAllow or deny operation\u003C/li\u003E\n\u003C/ol\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022policies\u0022\u003E\uD83D\uDEE1 Policies\u003C/h2\u003E\n\u003Cp\u003EPolicies are the core of authorization logic.\u003C/p\u003E\n\u003Cp\u003EDefault policies include:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERequireAuthenticated\u003C/li\u003E\n\u003Cli\u003EDenyCrossTenant\u003C/li\u003E\n\u003Cli\u003ERequireActiveUser\u003C/li\u003E\n\u003Cli\u003ERequireSelf\u003C/li\u003E\n\u003Cli\u003ERequireSystem\u003C/li\u003E\n\u003Cli\u003EMustHavePermission\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022plugin-integration\u0022\u003E\uD83D\uDD0C Plugin Integration\u003C/h2\u003E\n\u003Cp\u003EAuthorization is a plugin domain.\u003C/p\u003E\n\u003Cp\u003EIt:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EDoes NOT depend on other domains\u003C/li\u003E\n\u003Cli\u003EUses contracts only\u003C/li\u003E\n\u003Cli\u003EIntegrates via policies and claims\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022key-takeaways\u0022\u003E\uD83C\uDFAF Key Takeaways\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EAuthorization is policy-driven\u003C/li\u003E\n\u003Cli\u003ERoles are permission containers\u003C/li\u003E\n\u003Cli\u003EPermissions support wildcard \u0026amp; prefix\u003C/li\u003E\n\u003Cli\u003EPolicies enforce rules\u003C/li\u003E\n\u003Cli\u003EFully extensible and replaceable\u003C/li\u003E\n\u003C/ul\u003E\n",
+ "Headings": [
+ {
+ "Id": "core-concepts",
+ "Text": "\uD83E\uDDE9 Core Concepts",
+ "Level": 0
+ },
+ {
+ "Id": "permissions",
+ "Text": "\uD83D\uDD11 Permissions",
+ "Level": 1
+ },
+ {
+ "Id": "built-in-action-catalog",
+ "Text": "\u2699\uFE0F Built-in Action Catalog",
+ "Level": 1
+ },
+ {
+ "Id": "scope-semantics",
+ "Text": "\uD83E\uDDE0 Scope Semantics",
+ "Level": 1
+ },
+ {
+ "Id": "wildcards-grouping",
+ "Text": "\uD83C\uDF32 Wildcards \u0026 Grouping",
+ "Level": 1
+ },
+ {
+ "Id": "normalization",
+ "Text": "\u26A1 Normalization",
+ "Level": 1
+ },
+ {
+ "Id": "role",
+ "Text": "Role",
+ "Level": 1
+ },
+ {
+ "Id": "userrole",
+ "Text": "UserRole",
+ "Level": 1
+ },
+ {
+ "Id": "permission-resolution",
+ "Text": "\uD83D\uDD04 Permission Resolution",
+ "Level": 0
+ },
+ {
+ "Id": "claims-integration",
+ "Text": "\uD83E\uDDE0 Claims Integration",
+ "Level": 0
+ },
+ {
+ "Id": "authorization-flow",
+ "Text": "\u2699\uFE0F Authorization Flow",
+ "Level": 0
+ },
+ {
+ "Id": "policies",
+ "Text": "\uD83D\uDEE1 Policies",
+ "Level": 0
+ },
+ {
+ "Id": "plugin-integration",
+ "Text": "\uD83D\uDD0C Plugin Integration",
+ "Level": 0
+ },
+ {
+ "Id": "key-takeaways",
+ "Text": "\uD83C\uDFAF Key Takeaways",
+ "Level": 0
+ }
+ ]
+}
\ No newline at end of file
diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/plugin-domains/credential-domain.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/plugin-domains/credential-domain.json
new file mode 100644
index 00000000..7852b23f
--- /dev/null
+++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/plugin-domains/credential-domain.json
@@ -0,0 +1,57 @@
+{
+ "Slug": "plugin-domains/credential-domain",
+ "Title": "Credentials",
+ "Html": "\n\u003Cp\u003ECredentials in UltimateAuth define how a user proves their identity.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022core-concept\u0022\u003E\uD83E\uDDE0 Core Concept\u003C/h2\u003E\n\u003Cp\u003EAuthentication is not tied to users directly.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 It is performed through credentials.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022what-is-a-credential\u0022\u003E\uD83D\uDD11 What is a Credential?\u003C/h2\u003E\n\u003Cp\u003EA credential represents a secret or factor used for authentication.\u003C/p\u003E\n\u003Cp\u003EExamples:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EPassword\u003C/li\u003E\n\u003Cli\u003EOTP (future)\u003C/li\u003E\n\u003Cli\u003EExternal providers (future)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022password-credential\u0022\u003E\uD83D\uDD12 Password Credential\u003C/h2\u003E\n\u003Cp\u003EThe default credential type is password.\u003C/p\u003E\n\u003Cp\u003EA password credential contains:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EHashed secret (never raw password)\u003C/li\u003E\n\u003Cli\u003ESecurity state (active, revoked, expired)\u003C/li\u003E\n\u003Cli\u003EMetadata (last used, source, etc.)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Credentials are always stored securely and validated through hashing.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022credential-validation\u0022\u003E\u2699\uFE0F Credential Validation\u003C/h2\u003E\n\u003Cp\u003ECredential validation is handled by a validator:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EVerifies secret using hashing\u003C/li\u003E\n\u003Cli\u003EChecks credential usability (revoked, expired, etc.)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Validation is isolated from business logic.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022integration-with-users\u0022\u003E\uD83D\uDD17 Integration with Users\u003C/h2\u003E\n\u003Cp\u003ECredentials are NOT created directly inside user logic.\u003C/p\u003E\n\u003Cp\u003EInstead:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 They are integrated via lifecycle hooks\u003C/p\u003E\n\u003Cp\u003EExample:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EWhen a user is created \u2192 password credential may be created\u003C/li\u003E\n\u003Cli\u003EWhen a user is deleted \u2192 credentials are removed\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This keeps domains decoupled.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022credential-lifecycle\u0022\u003E\uD83D\uDD04 Credential Lifecycle\u003C/h2\u003E\n\u003Cp\u003ECredentials support:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ECreation\u003C/li\u003E\n\u003Cli\u003ESecret change\u003C/li\u003E\n\u003Cli\u003ERevocation\u003C/li\u003E\n\u003Cli\u003EExpiration\u003C/li\u003E\n\u003Cli\u003EDeletion\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022security-behavior\u0022\u003E\uD83D\uDD01 Security Behavior\u003C/h2\u003E\n\u003Cp\u003ECredential changes trigger security actions:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EChanging password revokes sessions\u003C/li\u003E\n\u003Cli\u003EReset flows require verification tokens\u003C/li\u003E\n\u003Cli\u003EInvalid attempts are tracked\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Credentials are tightly coupled with security.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022reset-flow\u0022\u003E\uD83D\uDD11 Reset Flow\u003C/h2\u003E\n\u003Cp\u003EPassword reset is a multi-step process:\u003C/p\u003E\n\u003Col\u003E\n\u003Cli\u003EBegin reset (generate token or code)\u003C/li\u003E\n\u003Cli\u003EValidate token\u003C/li\u003E\n\u003Cli\u003EApply new secret\u003C/li\u003E\n\u003C/ol\u003E\n\u003Cp\u003E\uD83D\uDC49 Reset flow is protected against enumeration and abuse.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EUsers define identity.\u003C/p\u003E\n\u003Cp\u003ECredentials define authentication.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022summary\u0022\u003E\uD83C\uDFAF Summary\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003ECredentials handle authentication secrets\u003C/li\u003E\n\u003Cli\u003EPassword is default but extensible\u003C/li\u003E\n\u003Cli\u003EIntegrated via lifecycle hooks\u003C/li\u003E\n\u003Cli\u003EStrong security guarantees\u003C/li\u003E\n\u003Cli\u003EFully extensible for new credential types\u003C/li\u003E\n\u003C/ul\u003E\n",
+ "Headings": [
+ {
+ "Id": "core-concept",
+ "Text": "\uD83E\uDDE0 Core Concept",
+ "Level": 0
+ },
+ {
+ "Id": "what-is-a-credential",
+ "Text": "\uD83D\uDD11 What is a Credential?",
+ "Level": 0
+ },
+ {
+ "Id": "password-credential",
+ "Text": "\uD83D\uDD12 Password Credential",
+ "Level": 0
+ },
+ {
+ "Id": "credential-validation",
+ "Text": "\u2699\uFE0F Credential Validation",
+ "Level": 0
+ },
+ {
+ "Id": "integration-with-users",
+ "Text": "\uD83D\uDD17 Integration with Users",
+ "Level": 0
+ },
+ {
+ "Id": "credential-lifecycle",
+ "Text": "\uD83D\uDD04 Credential Lifecycle",
+ "Level": 0
+ },
+ {
+ "Id": "security-behavior",
+ "Text": "\uD83D\uDD01 Security Behavior",
+ "Level": 0
+ },
+ {
+ "Id": "reset-flow",
+ "Text": "\uD83D\uDD11 Reset Flow",
+ "Level": 0
+ },
+ {
+ "Id": "mental-model",
+ "Text": "\uD83E\uDDE0 Mental Model",
+ "Level": 0
+ },
+ {
+ "Id": "summary",
+ "Text": "\uD83C\uDFAF Summary",
+ "Level": 0
+ }
+ ]
+}
\ No newline at end of file
diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/plugin-domains/index.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/plugin-domains/index.json
new file mode 100644
index 00000000..4ed257bd
--- /dev/null
+++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/plugin-domains/index.json
@@ -0,0 +1,77 @@
+{
+ "Slug": "plugin-domains/index",
+ "Title": "Plugin Domains",
+ "Html": "\n\u003Cp\u003EAuthentication alone is not enough.\u003C/p\u003E\n\u003Cp\u003EReal-world systems also require:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EUser management\u003C/li\u003E\n\u003Cli\u003ECredential handling\u003C/li\u003E\n\u003Cli\u003EAuthorization rules\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 UltimateAuth provides these as \u003Cstrong\u003Eplugin domains\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022what-is-a-plugin-domain\u0022\u003E\uD83E\uDDE0 What Is a Plugin Domain?\u003C/h2\u003E\n\u003Cp\u003EA plugin domain is a \u003Cstrong\u003Emodular business layer\u003C/strong\u003E built on top of UltimateAuth.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Core handles authentication\u003Cbr /\u003E\n\uD83D\uDC49 Plugin domains handle identity and access logic\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022architecture\u0022\u003E\uD83C\uDFD7 Architecture\u003C/h2\u003E\n\u003Cp\u003EEach plugin domain is composed of multiple layers:\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022bridge-package\u0022\u003E\uD83D\uDD39 Bridge Package\u003C/h3\u003E\n\u003Cp\u003EDefines the server needed bridge interfaces:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 This package provides required minimal info for server package.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022contracts-package\u0022\u003E\uD83D\uDD39 Contracts Package\u003C/h3\u003E\n\u003Cp\u003EShared models between:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EServer\u003C/li\u003E\n\u003Cli\u003EClient\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Includes DTOs, requests, responses\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022reference-implementation\u0022\u003E\uD83D\uDD39 Reference Implementation\u003C/h3\u003E\n\u003Cp\u003EProvides default behavior:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EApplication services\u003C/li\u003E\n\u003Cli\u003EStore interfaces\u003C/li\u003E\n\u003Cli\u003EDefault implementations\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Acts as a production-ready baseline\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022persistence-layer\u0022\u003E\uD83D\uDD39 Persistence Layer\u003C/h3\u003E\n\u003Cp\u003EProvides storage implementations:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EInMemory\u003C/li\u003E\n\u003Cli\u003EEntity Framework Core\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Additional providers (Redis, etc.) can be added\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022extensibility-model\u0022\u003E\uD83D\uDD04 Extensibility Model\u003C/h2\u003E\n\u003Cp\u003EPlugin domains are designed to be:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EReplaceable\u003C/li\u003E\n\u003Cli\u003EExtendable\u003C/li\u003E\n\u003Cli\u003EComposable\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 You can implement your own persistence\u003Cbr /\u003E\n\uD83D\uDC49 You can extend behavior\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022recommended-approach\u0022\u003E\u26A0\uFE0F Recommended Approach\u003C/h2\u003E\n\u003Cp\u003EIn most cases:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 You should NOT replace a plugin domain entirely\u003C/p\u003E\n\u003Cp\u003EInstead:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EUse provided implementations\u003C/li\u003E\n\u003Cli\u003EExtend via interfaces\u003C/li\u003E\n\u003Cli\u003ECustomize behavior where needed\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This ensures compatibility with the framework\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022domain-isolation\u0022\u003E\uD83D\uDD0C Domain Isolation\u003C/h2\u003E\n\u003Cp\u003EPlugin domains are designed to be \u003Cstrong\u003Efully isolated\u003C/strong\u003E from each other.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 A plugin domain does NOT reference another plugin domain\u003Cbr /\u003E\n\uD83D\uDC49 There are no direct dependencies between domains\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022why\u0022\u003E\uD83E\uDDE0 Why?\u003C/h3\u003E\n\u003Cp\u003EThis ensures:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELoose coupling\u003C/li\u003E\n\u003Cli\u003EIndependent evolution\u003C/li\u003E\n\u003Cli\u003EReplaceability\u003C/li\u003E\n\u003Cli\u003EClear boundaries\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022communication-via-hooks\u0022\u003E\uD83D\uDD04 Communication via Hooks\u003C/h2\u003E\n\u003Cp\u003EPlugin domains communicate \u003Cstrong\u003Eonly through integration hooks\u003C/strong\u003E.\u003C/p\u003E\n\u003Cp\u003EFor example:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EUser domain triggers \u2192 \u003Ccode\u003EOnUserCreated\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003ECredential domain listens \u2192 creates password credential\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This is implemented via abstractions such as:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u003Ccode\u003EIUserLifecycleIntegration\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003Edomain events / integration points\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Core = authentication engine\u003Cbr /\u003E\n\uD83D\uDC49 Plugin domains = business logic\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022why-this-matters\u0022\u003E\uD83C\uDFAF Why This Matters\u003C/h2\u003E\n\u003Cp\u003EThis architecture allows UltimateAuth to:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EStay modular\u003C/li\u003E\n\u003Cli\u003ESupport multiple domains\u003C/li\u003E\n\u003Cli\u003EEnable enterprise customization\u003C/li\u003E\n\u003Cli\u003EAvoid monolithic identity systems\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 You don\u2019t build everything from scratch\u003Cbr /\u003E\n\uD83D\uDC49 You assemble what you need\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EManage users \u2192 Users\u003C/li\u003E\n\u003Cli\u003EHandle credentials \u2192 Credentials\u003C/li\u003E\n\u003Cli\u003EControl access \u2192 Authorization\u003C/li\u003E\n\u003C/ul\u003E\n",
+ "Headings": [
+ {
+ "Id": "what-is-a-plugin-domain",
+ "Text": "\uD83E\uDDE0 What Is a Plugin Domain?",
+ "Level": 0
+ },
+ {
+ "Id": "architecture",
+ "Text": "\uD83C\uDFD7 Architecture",
+ "Level": 0
+ },
+ {
+ "Id": "bridge-package",
+ "Text": "\uD83D\uDD39 Bridge Package",
+ "Level": 1
+ },
+ {
+ "Id": "contracts-package",
+ "Text": "\uD83D\uDD39 Contracts Package",
+ "Level": 1
+ },
+ {
+ "Id": "reference-implementation",
+ "Text": "\uD83D\uDD39 Reference Implementation",
+ "Level": 1
+ },
+ {
+ "Id": "persistence-layer",
+ "Text": "\uD83D\uDD39 Persistence Layer",
+ "Level": 1
+ },
+ {
+ "Id": "extensibility-model",
+ "Text": "\uD83D\uDD04 Extensibility Model",
+ "Level": 0
+ },
+ {
+ "Id": "recommended-approach",
+ "Text": "\u26A0\uFE0F Recommended Approach",
+ "Level": 0
+ },
+ {
+ "Id": "domain-isolation",
+ "Text": "\uD83D\uDD0C Domain Isolation",
+ "Level": 0
+ },
+ {
+ "Id": "why",
+ "Text": "\uD83E\uDDE0 Why?",
+ "Level": 1
+ },
+ {
+ "Id": "communication-via-hooks",
+ "Text": "\uD83D\uDD04 Communication via Hooks",
+ "Level": 0
+ },
+ {
+ "Id": "mental-model",
+ "Text": "\uD83E\uDDE0 Mental Model",
+ "Level": 0
+ },
+ {
+ "Id": "why-this-matters",
+ "Text": "\uD83C\uDFAF Why This Matters",
+ "Level": 0
+ },
+ {
+ "Id": "next-step",
+ "Text": "\u27A1\uFE0F Next Step",
+ "Level": 0
+ }
+ ]
+}
\ No newline at end of file
diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/plugin-domains/policies.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/plugin-domains/policies.json
new file mode 100644
index 00000000..75b0e118
--- /dev/null
+++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/plugin-domains/policies.json
@@ -0,0 +1,77 @@
+{
+ "Slug": "plugin-domains/policies",
+ "Title": "Policies",
+ "Html": "\n\u003Cp\u003EUltimateAuth uses a \u003Cstrong\u003Epolicy-driven authorization model\u003C/strong\u003E.\u003C/p\u003E\n\u003Cp\u003EPolicies are not simple checks \u2014\u003Cbr /\u003E\nthey are \u003Cstrong\u003Ecomposable decision units\u003C/strong\u003E evaluated at runtime.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EAuthorization in UltimateAuth is:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Context-based\u003Cbr /\u003E\n\uD83D\uDC49 Policy-driven\u003Cbr /\u003E\n\uD83D\uDC49 Orchestrated\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022flow\u0022\u003EFlow\u003C/h3\u003E\n\u003Col\u003E\n\u003Cli\u003EBuild \u003Ccode\u003EAccessContext\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003EResolve policies\u003C/li\u003E\n\u003Cli\u003EExecute authority\u003C/li\u003E\n\u003Cli\u003EAllow / Deny / Reauth\u003C/li\u003E\n\u003C/ol\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022accesscontext\u0022\u003E\u2699\uFE0F AccessContext\u003C/h2\u003E\n\u003Cp\u003EEvery authorization decision is based on:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EActor (who is calling)\u003C/li\u003E\n\u003Cli\u003ETarget (what is being accessed)\u003C/li\u003E\n\u003Cli\u003EAction (what is being done)\u003C/li\u003E\n\u003Cli\u003ETenant\u003C/li\u003E\n\u003Cli\u003EClaims / permissions\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022policy-resolution\u0022\u003E\uD83D\uDD0C Policy Resolution\u003C/h2\u003E\n\u003Cp\u003EPolicies are resolved using:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EAction prefix matching\u003C/li\u003E\n\u003Cli\u003ERuntime filtering (\u003Ccode\u003EAppliesTo\u003C/code\u003E)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EExample:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u003Ccode\u003Eusers.create.admin\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003E\u003Ccode\u003Eusers.*\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003E\u003Ccode\u003Eauthorization.roles.*\u003C/code\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022policy-types\u0022\u003E\uD83E\uDDE9 Policy Types\u003C/h2\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022global-policies\u0022\u003EGlobal Policies\u003C/h3\u003E\n\u003Cp\u003EAlways evaluated:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERequireAuthenticated\u003C/li\u003E\n\u003Cli\u003EDenyCrossTenant\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022runtime-policies\u0022\u003ERuntime Policies\u003C/h3\u003E\n\u003Cp\u003EResolved dynamically:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERequireActiveUser\u003C/li\u003E\n\u003Cli\u003EMustHavePermission\u003C/li\u003E\n\u003Cli\u003ERequireSelf\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022invariants\u0022\u003EInvariants\u003C/h3\u003E\n\u003Cp\u003EExecuted first:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ECannot be bypassed\u003C/li\u003E\n\u003Cli\u003EHard security rules\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022policy-evaluation\u0022\u003E\u2696\uFE0F Policy Evaluation\u003C/h2\u003E\n\u003Cp\u003EEvaluation order:\u003C/p\u003E\n\u003Col\u003E\n\u003Cli\u003EInvariants\u003C/li\u003E\n\u003Cli\u003EGlobal policies\u003C/li\u003E\n\u003Cli\u003ERuntime policies\u003C/li\u003E\n\u003C/ol\u003E\n\u003Cp\u003E\uD83D\uDC49 First deny wins\u003Cbr /\u003E\n\uD83D\uDC49 Allow means \u201Cno objection\u201D\u003Cbr /\u003E\n\uD83D\uDC49 Reauth can be requested\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022example-policy\u0022\u003E\uD83D\uDD10 Example Policy\u003C/h2\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022deny-admin-self-modification\u0022\u003EDeny Admin Self Modification\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EBlocks admin modifying own account\u003C/li\u003E\n\u003Cli\u003EApplies only to \u003Ccode\u003E.admin\u003C/code\u003E actions\u003C/li\u003E\n\u003Cli\u003EIgnores read operations\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022require-active-user\u0022\u003ERequire Active User\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EEnsures user exists\u003C/li\u003E\n\u003Cli\u003EEnsures user is active\u003C/li\u003E\n\u003Cli\u003ESkips anonymous actions\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022access-orchestrator\u0022\u003E\uD83D\uDE80 Access Orchestrator\u003C/h2\u003E\n\u003Cp\u003EThe orchestrator is the entry point:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EEnriches context (claims, permissions)\u003C/li\u003E\n\u003Cli\u003EResolves policies\u003C/li\u003E\n\u003Cli\u003EExecutes authority\u003C/li\u003E\n\u003Cli\u003ERuns command if allowed\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022key-principles\u0022\u003E\uD83C\uDFAF Key Principles\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EPolicies are composable\u003C/li\u003E\n\u003Cli\u003EAuthorization is deterministic\u003C/li\u003E\n\u003Cli\u003ENo hidden magic\u003C/li\u003E\n\u003Cli\u003EFully extensible\u003C/li\u003E\n\u003C/ul\u003E\n\u003Chr /\u003E\n\u003Cp\u003E\uD83D\uDC49 Authorization is not a single check\u003Cbr /\u003E\n\uD83D\uDC49 It is a \u003Cstrong\u003Epipeline of decisions\u003C/strong\u003E\u003C/p\u003E\n",
+ "Headings": [
+ {
+ "Id": "mental-model",
+ "Text": "\uD83E\uDDE0 Mental Model",
+ "Level": 0
+ },
+ {
+ "Id": "flow",
+ "Text": "Flow",
+ "Level": 1
+ },
+ {
+ "Id": "accesscontext",
+ "Text": "\u2699\uFE0F AccessContext",
+ "Level": 0
+ },
+ {
+ "Id": "policy-resolution",
+ "Text": "\uD83D\uDD0C Policy Resolution",
+ "Level": 0
+ },
+ {
+ "Id": "policy-types",
+ "Text": "\uD83E\uDDE9 Policy Types",
+ "Level": 0
+ },
+ {
+ "Id": "global-policies",
+ "Text": "Global Policies",
+ "Level": 1
+ },
+ {
+ "Id": "runtime-policies",
+ "Text": "Runtime Policies",
+ "Level": 1
+ },
+ {
+ "Id": "invariants",
+ "Text": "Invariants",
+ "Level": 1
+ },
+ {
+ "Id": "policy-evaluation",
+ "Text": "\u2696\uFE0F Policy Evaluation",
+ "Level": 0
+ },
+ {
+ "Id": "example-policy",
+ "Text": "\uD83D\uDD10 Example Policy",
+ "Level": 0
+ },
+ {
+ "Id": "deny-admin-self-modification",
+ "Text": "Deny Admin Self Modification",
+ "Level": 1
+ },
+ {
+ "Id": "require-active-user",
+ "Text": "Require Active User",
+ "Level": 1
+ },
+ {
+ "Id": "access-orchestrator",
+ "Text": "\uD83D\uDE80 Access Orchestrator",
+ "Level": 0
+ },
+ {
+ "Id": "key-principles",
+ "Text": "\uD83C\uDFAF Key Principles",
+ "Level": 0
+ }
+ ]
+}
\ No newline at end of file
diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/plugin-domains/users-domain.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/plugin-domains/users-domain.json
new file mode 100644
index 00000000..03aa54a2
--- /dev/null
+++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/plugin-domains/users-domain.json
@@ -0,0 +1,47 @@
+{
+ "Slug": "plugin-domains/users-domain",
+ "Title": "Users",
+ "Html": "\n\u003Cp\u003EUsers in UltimateAuth are not a single entity.\u003C/p\u003E\n\u003Cp\u003EInstead, they are composed of multiple parts that together define identity.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022core-concept\u0022\u003E\uD83E\uDDE0 Core Concept\u003C/h2\u003E\n\u003Cp\u003EA user is represented by three main components:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELifecycle (security \u0026amp; state)\u003C/li\u003E\n\u003Cli\u003EIdentifiers (login surface)\u003C/li\u003E\n\u003Cli\u003EProfile (user data)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022lifecycle-security-anchor\u0022\u003E\uD83D\uDD10 Lifecycle (Security Anchor)\u003C/h2\u003E\n\u003Cp\u003ELifecycle defines:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EWhen a user created\u003C/li\u003E\n\u003Cli\u003EWhether a user is active, suspended, or deleted\u003C/li\u003E\n\u003Cli\u003ESecurity version (used for invalidating sessions/tokens)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This is the core of authentication security.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022identifiers-login-system\u0022\u003E\uD83D\uDD11 Identifiers (Login System)\u003C/h2\u003E\n\u003Cp\u003EIdentifiers represent how a user logs in:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EEmail\u003C/li\u003E\n\u003Cli\u003EUsername\u003C/li\u003E\n\u003Cli\u003EPhone\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EEach identifier has:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ENormalized value\u003C/li\u003E\n\u003Cli\u003EPrimary flag\u003C/li\u003E\n\u003Cli\u003EVerification state\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022login-identifiers\u0022\u003E\u2B50 Login Identifiers\u003C/h3\u003E\n\u003Cp\u003ENot all identifiers are used for login.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Only \u003Cstrong\u003Eprimary identifiers\u003C/strong\u003E are considered login identifiers.\u003C/p\u003E\n\u003Ch4 id=\u0022configurable-behavior\u0022\u003E\u2699\uFE0F Configurable Behavior\u003C/h4\u003E\n\u003Cp\u003EDevelopers can control:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EWhich identifier types are allowed for login\u003C/li\u003E\n\u003Cli\u003EWhether email/phone must be verified\u003C/li\u003E\n\u003Cli\u003EWhether multiple identifiers are allowed\u003C/li\u003E\n\u003Cli\u003EGlobal uniqueness rules\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch4 id=\u0022custom-login-logic\u0022\u003E\uD83D\uDD0C Custom Login Logic\u003C/h4\u003E\n\u003Cp\u003EUltimateAuth allows custom login identifier resolution.\u003C/p\u003E\n\u003Cp\u003EYou can:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EAdd custom identifier types\u003C/li\u003E\n\u003Cli\u003EOverride resolution logic\u003C/li\u003E\n\u003Cli\u003EImplement your own resolver\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This means login is fully extensible.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022profile-user-data\u0022\u003E\uD83E\uDDFE Profile (User Data)\u003C/h2\u003E\n\u003Cp\u003EProfile contains non-auth data:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EName\u003C/li\u003E\n\u003Cli\u003EBio\u003C/li\u003E\n\u003Cli\u003ELocalization\u003C/li\u003E\n\u003Cli\u003EMetadata\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This is not used for authentication.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022application-service\u0022\u003E\u2699\uFE0F Application Service\u003C/h2\u003E\n\u003Cp\u003EUser operations are handled by an orchestration layer.\u003C/p\u003E\n\u003Cp\u003EIt is responsible for:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ECreating users\u003C/li\u003E\n\u003Cli\u003EManaging identifiers\u003C/li\u003E\n\u003Cli\u003EApplying validation rules\u003C/li\u003E\n\u003Cli\u003ETriggering integrations\u003C/li\u003E\n\u003Cli\u003ERevoking sessions when needed\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022mental-model\u0022\u003E\uD83D\uDD01 Mental Model\u003C/h2\u003E\n\u003Cp\u003EAuthentication answers:\u003C/p\u003E\n\u003Cp\u003E\u2192 Who are you?\u003C/p\u003E\n\u003Cp\u003EUsers domain answers:\u003C/p\u003E\n\u003Cp\u003E\u2192 What is this user?\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022summary\u0022\u003E\uD83C\uDFAF Summary\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EUsers are composed, not singular\u003C/li\u003E\n\u003Cli\u003ELogin is based on identifiers\u003C/li\u003E\n\u003Cli\u003ELogin identifiers are configurable\u003C/li\u003E\n\u003Cli\u003ESystem is extensible by design\u003C/li\u003E\n\u003C/ul\u003E\n",
+ "Headings": [
+ {
+ "Id": "core-concept",
+ "Text": "\uD83E\uDDE0 Core Concept",
+ "Level": 0
+ },
+ {
+ "Id": "lifecycle-security-anchor",
+ "Text": "\uD83D\uDD10 Lifecycle (Security Anchor)",
+ "Level": 0
+ },
+ {
+ "Id": "identifiers-login-system",
+ "Text": "\uD83D\uDD11 Identifiers (Login System)",
+ "Level": 0
+ },
+ {
+ "Id": "login-identifiers",
+ "Text": "\u2B50 Login Identifiers",
+ "Level": 1
+ },
+ {
+ "Id": "profile-user-data",
+ "Text": "\uD83E\uDDFE Profile (User Data)",
+ "Level": 0
+ },
+ {
+ "Id": "application-service",
+ "Text": "\u2699\uFE0F Application Service",
+ "Level": 0
+ },
+ {
+ "Id": "mental-model",
+ "Text": "\uD83D\uDD01 Mental Model",
+ "Level": 0
+ },
+ {
+ "Id": "summary",
+ "Text": "\uD83C\uDFAF Summary",
+ "Level": 0
+ }
+ ]
+}
\ No newline at end of file
diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/readme.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/readme.json
new file mode 100644
index 00000000..98a6eac6
--- /dev/null
+++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/readme.json
@@ -0,0 +1,47 @@
+{
+ "Slug": "readme",
+ "Title": "\uD83D\uDD10 UltimateAuth Docs",
+ "Html": "\n\u003Cp\u003EThe modern, unified auth framework with platform-level abilities for .NET - Reimagined.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022sections\u0022\u003ESections\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth docs consists of 7 sections. There are suggested read line for starters:\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022getting-started\u0022\u003E1) Getting Started\u003C/h3\u003E\n\u003Cp\u003EStart here to integrate UltimateAuth into your application:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EGetting Started Guide\u003C/li\u003E\n\u003Cli\u003ESetup with different client profiles\u003C/li\u003E\n\u003Cli\u003EBasic usage\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022fundamentals\u0022\u003E2) Fundamentals\u003C/h3\u003E\n\u003Cp\u003EUnderstand how UltimateAuth works internally:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ECore Concepts\u003C/li\u003E\n\u003Cli\u003ESession Architecture (Root \u2192 Chain \u2192 Session)\u003C/li\u003E\n\u003Cli\u003EToken Model\u003C/li\u003E\n\u003Cli\u003EAuthorization Model\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022auth-flows\u0022\u003E3) Auth Flows\u003C/h3\u003E\n\u003Cp\u003EExplore authentication lifecycle:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELogin Flow\u003C/li\u003E\n\u003Cli\u003ERefresh Flow\u003C/li\u003E\n\u003Cli\u003ELogout Flow\u003C/li\u003E\n\u003Cli\u003ESession Lifecycle\u003C/li\u003E\n\u003Cli\u003EToken Behavior\u003C/li\u003E\n\u003Cli\u003EDevice Management\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022plugin-domains\u0022\u003E4) Plugin Domains\u003C/h3\u003E\n\u003Cp\u003EUltimateAuth is built as modular domains:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EUsers\u003C/li\u003E\n\u003Cli\u003ECredentials\u003C/li\u003E\n\u003Cli\u003EAuthorization\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022configuration-extensibility\u0022\u003E5) Configuration \u0026amp; Extensibility\u003C/h3\u003E\n\u003Cp\u003ECustomize behavior and integrate with your system:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EServer Options\u003C/li\u003E\n\u003Cli\u003EClient Options\u003C/li\u003E\n\u003Cli\u003EConfiguration Sources\u003C/li\u003E\n\u003Cli\u003EAdvanced Configuration\u003C/li\u003E\n\u003Cli\u003EExtending Domains \u0026amp; Stores\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022client-api\u0022\u003E6) Client \u0026amp; API\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EReal world usage of UltimateAuth\u003C/li\u003E\n\u003Cli\u003EAPI and code examples of authentication, sessions, users, credentials and authorization\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022security-advanced\u0022\u003E7) Security \u0026amp; Advanced\u003C/h3\u003E\n\u003Cp\u003EDeep dive into the security model:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession Security Model\u003C/li\u003E\n\u003Cli\u003ERefresh Token Rotation\u003C/li\u003E\n\u003Cli\u003EAccess Token Behavior\u003C/li\u003E\n\u003Cli\u003EPolicy Pipeline\u003C/li\u003E\n\u003C/ul\u003E\n",
+ "Headings": [
+ {
+ "Id": "sections",
+ "Text": "Sections",
+ "Level": 0
+ },
+ {
+ "Id": "getting-started",
+ "Text": "1) Getting Started",
+ "Level": 1
+ },
+ {
+ "Id": "fundamentals",
+ "Text": "2) Fundamentals",
+ "Level": 1
+ },
+ {
+ "Id": "auth-flows",
+ "Text": "3) Auth Flows",
+ "Level": 1
+ },
+ {
+ "Id": "plugin-domains",
+ "Text": "4) Plugin Domains",
+ "Level": 1
+ },
+ {
+ "Id": "configuration-extensibility",
+ "Text": "5) Configuration \u0026 Extensibility",
+ "Level": 1
+ },
+ {
+ "Id": "client-api",
+ "Text": "6) Client \u0026 API",
+ "Level": 1
+ },
+ {
+ "Id": "security-advanced",
+ "Text": "7) Security \u0026 Advanced",
+ "Level": 1
+ }
+ ]
+}
\ No newline at end of file
diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/security/access-token-behavior.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/security/access-token-behavior.json
new file mode 100644
index 00000000..d90f47f3
--- /dev/null
+++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/security/access-token-behavior.json
@@ -0,0 +1,97 @@
+{
+ "Slug": "security/access-token-behavior",
+ "Title": "Access Token Behavior",
+ "Html": "\n\u003Cp\u003EAccess tokens in UltimateAuth are intentionally \u003Cstrong\u003Eshort-lived and mode-aware\u003C/strong\u003E.\nThey are not the primary source of truth for authentication.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022core-principle\u0022\u003E\uD83E\uDDE0 Core Principle\u003C/h2\u003E\n\u003Cp\u003EIn UltimateAuth:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Access tokens are \u003Cstrong\u003Etransport tokens\u003C/strong\u003E\n\uD83D\uDC49 Sessions are the \u003Cstrong\u003Esource of truth\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022token-types\u0022\u003E\uD83D\uDD11 Token Types\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth supports two access token types:\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022opaque-tokens\u0022\u003E\uD83D\uDD39 Opaque Tokens\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ERandom, non-readable values\u003C/li\u003E\n\u003Cli\u003EStored and validated server-side\u003C/li\u003E\n\u003Cli\u003ETypically used with session-based auth\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022jwt-tokens\u0022\u003E\uD83D\uDD39 JWT Tokens\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ESelf-contained tokens\u003C/li\u003E\n\u003Cli\u003EContain claims (user, tenant, session, etc.)\u003C/li\u003E\n\u003Cli\u003ESigned and verifiable without storage\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022mode-dependent-behavior\u0022\u003E\u2699\uFE0F Mode-Dependent Behavior\u003C/h2\u003E\n\u003Cp\u003EAccess token behavior depends on auth mode:\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022pureopaque\u0022\u003E\uD83D\uDFE2 PureOpaque\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ENo JWT issued\u003C/li\u003E\n\u003Cli\u003ESession cookie is the primary mechanism\u003C/li\u003E\n\u003Cli\u003EAccess token may not exist externally\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Validation = session validation\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022hybrid\u0022\u003E\uD83D\uDFE1 Hybrid\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EOpaque \u002B JWT together\u003C/li\u003E\n\u003Cli\u003ESession still authoritative\u003C/li\u003E\n\u003Cli\u003EJWT used for API access\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Validation = session \u002B token\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022semihybrid\u0022\u003E\uD83D\uDFE0 SemiHybrid\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EJWT is primary access token\u003C/li\u003E\n\u003Cli\u003ESession still exists\u003C/li\u003E\n\u003Cli\u003ERefresh rotation enabled\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Balanced approach\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022purejwt\u0022\u003E\uD83D\uDD35 PureJwt\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EOnly JWT \u002B refresh tokens\u003C/li\u003E\n\u003Cli\u003ENo session state required\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Stateless mode\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022lifetime-strategy\u0022\u003E\u23F1 Lifetime Strategy\u003C/h2\u003E\n\u003Cp\u003EAccess tokens are:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eshort-lived\u003C/li\u003E\n\u003Cli\u003Ereplaceable\u003C/li\u003E\n\u003Cli\u003Enot trusted long-term\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003ETypical lifetime:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003E5\u201315 minutes\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022refresh-interaction\u0022\u003E\uD83D\uDD04 Refresh Interaction\u003C/h2\u003E\n\u003Cp\u003EAccess tokens are never extended directly.\u003C/p\u003E\n\u003Cp\u003EInstead:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EAccess Token \u2192 expires \u2192 Refresh \u2192 new Access Token\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 This ensures:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eforward-only security\u003C/li\u003E\n\u003Cli\u003Eno silent extension\u003C/li\u003E\n\u003Cli\u003Ereplay window minimization\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022claims-model\u0022\u003E\uD83E\uDDFE Claims Model\u003C/h2\u003E\n\u003Cp\u003EJWT tokens may include:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Esub (user id)\u003C/li\u003E\n\u003Cli\u003Etenant\u003C/li\u003E\n\u003Cli\u003Esid (session id)\u003C/li\u003E\n\u003Cli\u003Ejti (token id)\u003C/li\u003E\n\u003Cli\u003Ecustom claims\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Claims are generated at issuance time\n\uD83D\uDC49 Not dynamically updated afterward\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022important-implication\u0022\u003E\u26A0\uFE0F Important Implication\u003C/h2\u003E\n\u003Cp\u003EIf something changes:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Euser roles\u003C/li\u003E\n\u003Cli\u003Epermissions\u003C/li\u003E\n\u003Cli\u003Esecurity state\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 existing JWTs are NOT updated\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 This is why:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Etokens are short-lived\u003C/li\u003E\n\u003Cli\u003Erefresh is required\u003C/li\u003E\n\u003Cli\u003Esession validation may still apply\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022security-boundaries\u0022\u003E\uD83D\uDEE1 Security Boundaries\u003C/h2\u003E\n\u003Cp\u003EAccess tokens are:\u003C/p\u003E\n\u003Cp\u003E\u274C not revocable individually (JWT)\n\u274C not long-term identity\u003C/p\u003E\n\u003Cp\u003E\u2714 tied to session or refresh flow\n\u2714 bounded by expiration\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022why-this-matters\u0022\u003E\uD83D\uDD25 Why This Matters\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth avoids a common mistake:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 treating JWT as the system of record\u003C/p\u003E\n\u003Cp\u003EInstead:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 JWT is a \u003Cstrong\u003Esnapshot\u003C/strong\u003E, not truth\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022design-tradeoff\u0022\u003E\u26A0\uFE0F Design Tradeoff\u003C/h2\u003E\n\u003Cp\u003EShort-lived tokens mean:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Emore refresh calls\u003C/li\u003E\n\u003Cli\u003Eslightly more backend interaction\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 But this enables:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Esafer rotation\u003C/li\u003E\n\u003Cli\u003Ebetter revocation\u003C/li\u003E\n\u003Cli\u003Ereduced attack window\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Access tokens are \u003Cstrong\u003Etemporary access grants\u003C/strong\u003E\n\uD83D\uDC49 Not persistent identity\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003EContinue to \u003Cstrong\u003EPolicy Pipeline Deep Dive\u003C/strong\u003E to understand how access decisions are enforced.\u003C/p\u003E\n",
+ "Headings": [
+ {
+ "Id": "core-principle",
+ "Text": "\uD83E\uDDE0 Core Principle",
+ "Level": 0
+ },
+ {
+ "Id": "token-types",
+ "Text": "\uD83D\uDD11 Token Types",
+ "Level": 0
+ },
+ {
+ "Id": "opaque-tokens",
+ "Text": "\uD83D\uDD39 Opaque Tokens",
+ "Level": 1
+ },
+ {
+ "Id": "jwt-tokens",
+ "Text": "\uD83D\uDD39 JWT Tokens",
+ "Level": 1
+ },
+ {
+ "Id": "mode-dependent-behavior",
+ "Text": "\u2699\uFE0F Mode-Dependent Behavior",
+ "Level": 0
+ },
+ {
+ "Id": "pureopaque",
+ "Text": "\uD83D\uDFE2 PureOpaque",
+ "Level": 1
+ },
+ {
+ "Id": "hybrid",
+ "Text": "\uD83D\uDFE1 Hybrid",
+ "Level": 1
+ },
+ {
+ "Id": "semihybrid",
+ "Text": "\uD83D\uDFE0 SemiHybrid",
+ "Level": 1
+ },
+ {
+ "Id": "purejwt",
+ "Text": "\uD83D\uDD35 PureJwt",
+ "Level": 1
+ },
+ {
+ "Id": "lifetime-strategy",
+ "Text": "\u23F1 Lifetime Strategy",
+ "Level": 0
+ },
+ {
+ "Id": "refresh-interaction",
+ "Text": "\uD83D\uDD04 Refresh Interaction",
+ "Level": 0
+ },
+ {
+ "Id": "claims-model",
+ "Text": "\uD83E\uDDFE Claims Model",
+ "Level": 0
+ },
+ {
+ "Id": "important-implication",
+ "Text": "\u26A0\uFE0F Important Implication",
+ "Level": 0
+ },
+ {
+ "Id": "security-boundaries",
+ "Text": "\uD83D\uDEE1 Security Boundaries",
+ "Level": 0
+ },
+ {
+ "Id": "why-this-matters",
+ "Text": "\uD83D\uDD25 Why This Matters",
+ "Level": 0
+ },
+ {
+ "Id": "design-tradeoff",
+ "Text": "\u26A0\uFE0F Design Tradeoff",
+ "Level": 0
+ },
+ {
+ "Id": "mental-model",
+ "Text": "\uD83E\uDDE0 Mental Model",
+ "Level": 0
+ },
+ {
+ "Id": "next-step",
+ "Text": "\u27A1\uFE0F Next Step",
+ "Level": 0
+ }
+ ]
+}
\ No newline at end of file
diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/security/policy-pipeline.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/security/policy-pipeline.json
new file mode 100644
index 00000000..85cf991d
--- /dev/null
+++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/security/policy-pipeline.json
@@ -0,0 +1,77 @@
+{
+ "Slug": "security/policy-pipeline",
+ "Title": "Policy Pipeline",
+ "Html": "\n\u003Cp\u003EUltimateAuth does not rely on simple role checks.\u003C/p\u003E\n\u003Cp\u003EAuthorization is executed through a \u003Cstrong\u003Emulti-stage policy pipeline\u003C/strong\u003E that evaluates:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Einvariants (always enforced rules)\u003C/li\u003E\n\u003Cli\u003Eglobal policies\u003C/li\u003E\n\u003Cli\u003Eruntime (action-based) policies\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022core-principle\u0022\u003E\uD83E\uDDE0 Core Principle\u003C/h2\u003E\n\u003Cp\u003E\uD83D\uDC49 Authorization is not a single check\u003Cbr /\u003E\n\uD83D\uDC49 It is a \u003Cstrong\u003Edecision pipeline\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022high-level-flow\u0022\u003E\uD83D\uDD01 High-Level Flow\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EAccessContext\n \u2193\nEnrichment (claims, permissions)\n \u2193\nInvariants\n \u2193\nGlobal Policies\n \u2193\nRuntime Policies (action-based)\n \u2193\nFinal Decision\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022accesscontext\u0022\u003E\uD83E\uDDE9 AccessContext\u003C/h2\u003E\n\u003Cp\u003EEvery authorization decision is based on an \u003Ccode\u003EAccessContext\u003C/code\u003E.\u003C/p\u003E\n\u003Cp\u003EIt contains:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eactor (who is making the request)\u003C/li\u003E\n\u003Cli\u003Etarget (resource / user)\u003C/li\u003E\n\u003Cli\u003Etenant\u003C/li\u003E\n\u003Cli\u003Eaction (string-based, e.g. \u0026quot;users.delete\u0026quot;)\u003C/li\u003E\n\u003Cli\u003Eattributes (claims, permissions, etc.)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Everything in the pipeline reads from this context\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022step-1-context-enrichment\u0022\u003E\uD83D\uDD04 Step 1: Context Enrichment\u003C/h2\u003E\n\u003Cp\u003EBefore policies run, context is enriched:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Epermissions are loaded\u003C/li\u003E\n\u003Cli\u003Ecompiled into \u003Ccode\u003ECompiledPermissionSet\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003Eattached to context\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This allows policies to run without hitting storage repeatedly\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022step-2-invariants\u0022\u003E\uD83D\uDEE1 Step 2: Invariants\u003C/h2\u003E\n\u003Cp\u003EInvariants are \u003Cstrong\u003Ealways enforced rules\u003C/strong\u003E.\u003C/p\u003E\n\u003Cp\u003EThey run first and cannot be bypassed.\u003C/p\u003E\n\u003Cp\u003EExamples:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Euser must be authenticated\u003C/li\u003E\n\u003Cli\u003Ecross-tenant access is denied\u003C/li\u003E\n\u003Cli\u003Erequest must be valid\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 If an invariant fails:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003E\u2192 DENY immediately\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022step-3-global-policies\u0022\u003E\uD83C\uDF10 Step 3: Global Policies\u003C/h2\u003E\n\u003Cp\u003EGlobal policies apply to all requests but are conditional.\u003C/p\u003E\n\u003Cp\u003EExamples:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERequireActiveUserPolicy\u003C/li\u003E\n\u003Cli\u003EDenyAdminSelfModificationPolicy\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EEach policy:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Edecides if it applies (\u003Ccode\u003EAppliesTo\u003C/code\u003E)\u003C/li\u003E\n\u003Cli\u003Eevaluates (\u003Ccode\u003EDecide\u003C/code\u003E)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Important:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u201CAllow\u201D means \u003Cem\u003Eno objection\u003C/em\u003E\u003C/li\u003E\n\u003Cli\u003Enot final approval\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022step-4-runtime-policies\u0022\u003E\uD83C\uDFAF Step 4: Runtime Policies\u003C/h2\u003E\n\u003Cp\u003ERuntime policies are:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eaction-based\u003C/li\u003E\n\u003Cli\u003Edynamically resolved\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EThey come from:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EAccessPolicyRegistry \u2192 CompiledAccessPolicySet\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EPolicies are selected by:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003Econtext.Action.StartsWith(prefix)\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EExample:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EAction: users.delete.admin\n\nMatches:\n- users.*\n- users.delete.*\n- users.delete.admin\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022step-5-decision-engine\u0022\u003E\u2696\uFE0F Step 5: Decision Engine\u003C/h2\u003E\n\u003Cp\u003EAll policies are evaluated by:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Ccode\u003EIAccessAuthority\u003C/code\u003E\u003C/p\u003E\n\u003Cp\u003EEvaluation order:\u003C/p\u003E\n\u003Col\u003E\n\u003Cli\u003EInvariants\u003C/li\u003E\n\u003Cli\u003EGlobal policies\u003C/li\u003E\n\u003Cli\u003ERuntime policies\u003C/li\u003E\n\u003C/ol\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022decision-rules\u0022\u003EDecision Rules\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EFirst \u003Cstrong\u003Edeny\u003C/strong\u003E \u2192 stops execution\u003C/li\u003E\n\u003Cli\u003E\u201CAllow\u201D \u2192 continue\u003C/li\u003E\n\u003Cli\u003E\u201CRequiresReauthentication\u201D \u2192 tracked\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EFinal result:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EAllow\nDeny(reason)\nReauthenticationRequired\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022permission-integration\u0022\u003E\uD83D\uDD10 Permission Integration\u003C/h2\u003E\n\u003Cp\u003EPermissions are not directly checked in services.\u003C/p\u003E\n\u003Cp\u003EInstead:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Epermissions are compiled\u003C/li\u003E\n\u003Cli\u003Epolicies use them\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EExample policy:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EMustHavePermissionPolicy\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EChecks:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003ECompiledPermissionSet.IsAllowed(action)\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 This decouples:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Epermission storage\u003C/li\u003E\n\u003Cli\u003Eauthorization logic\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022why-this-matters\u0022\u003E\uD83D\uDD25 Why This Matters\u003C/h2\u003E\n\u003Cp\u003EThis pipeline enables:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Ecomposable security rules\u003C/li\u003E\n\u003Cli\u003Econsistent enforcement\u003C/li\u003E\n\u003Cli\u003Eseparation of concerns\u003C/li\u003E\n\u003Cli\u003Eextensibility\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003ECompared to typical systems:\u003C/p\u003E\n\u003Ctable\u003E\n\u003Cthead\u003E\n\u003Ctr\u003E\n\u003Cth\u003EFeature\u003C/th\u003E\n\u003Cth\u003ETraditional\u003C/th\u003E\n\u003Cth\u003EUltimateAuth\u003C/th\u003E\n\u003C/tr\u003E\n\u003C/thead\u003E\n\u003Ctbody\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EInline checks\u003C/td\u003E\n\u003Ctd\u003E\u2705\u003C/td\u003E\n\u003Ctd\u003E\u274C\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003ECentral pipeline\u003C/td\u003E\n\u003Ctd\u003E\u274C\u003C/td\u003E\n\u003Ctd\u003E\u2705\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EPolicy composition\u003C/td\u003E\n\u003Ctd\u003E\u274C\u003C/td\u003E\n\u003Ctd\u003E\u2705\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EAction-based rules\u003C/td\u003E\n\u003Ctd\u003E\u274C\u003C/td\u003E\n\u003Ctd\u003E\u2705\u003C/td\u003E\n\u003C/tr\u003E\n\u003C/tbody\u003E\n\u003C/table\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022design-tradeoff\u0022\u003E\u26A0\uFE0F Design Tradeoff\u003C/h2\u003E\n\u003Cp\u003EThis model introduces:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Emore abstraction\u003C/li\u003E\n\u003Cli\u003Emore components\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 But gives:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Epredictability\u003C/li\u003E\n\u003Cli\u003Eauditability\u003C/li\u003E\n\u003Cli\u003Eflexibility\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Authorization is not \u201Cif (role == admin)\u201D\u003Cbr /\u003E\n\uD83D\uDC49 It is a \u003Cstrong\u003Epipeline of decisions\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022summary\u0022\u003E\u27A1\uFE0F Summary\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth authorization:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eis policy-driven\u003C/li\u003E\n\u003Cli\u003Eruns through a structured pipeline\u003C/li\u003E\n\u003Cli\u003Eseparates invariants, global rules, and action rules\u003C/li\u003E\n\u003Cli\u003Eproduces deterministic decisions\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This makes it suitable for complex, multi-tenant, security-sensitive systems\u003C/p\u003E\n",
+ "Headings": [
+ {
+ "Id": "core-principle",
+ "Text": "\uD83E\uDDE0 Core Principle",
+ "Level": 0
+ },
+ {
+ "Id": "high-level-flow",
+ "Text": "\uD83D\uDD01 High-Level Flow",
+ "Level": 0
+ },
+ {
+ "Id": "accesscontext",
+ "Text": "\uD83E\uDDE9 AccessContext",
+ "Level": 0
+ },
+ {
+ "Id": "step-1-context-enrichment",
+ "Text": "\uD83D\uDD04 Step 1: Context Enrichment",
+ "Level": 0
+ },
+ {
+ "Id": "step-2-invariants",
+ "Text": "\uD83D\uDEE1 Step 2: Invariants",
+ "Level": 0
+ },
+ {
+ "Id": "step-3-global-policies",
+ "Text": "\uD83C\uDF10 Step 3: Global Policies",
+ "Level": 0
+ },
+ {
+ "Id": "step-4-runtime-policies",
+ "Text": "\uD83C\uDFAF Step 4: Runtime Policies",
+ "Level": 0
+ },
+ {
+ "Id": "step-5-decision-engine",
+ "Text": "\u2696\uFE0F Step 5: Decision Engine",
+ "Level": 0
+ },
+ {
+ "Id": "decision-rules",
+ "Text": "Decision Rules",
+ "Level": 1
+ },
+ {
+ "Id": "permission-integration",
+ "Text": "\uD83D\uDD10 Permission Integration",
+ "Level": 0
+ },
+ {
+ "Id": "why-this-matters",
+ "Text": "\uD83D\uDD25 Why This Matters",
+ "Level": 0
+ },
+ {
+ "Id": "design-tradeoff",
+ "Text": "\u26A0\uFE0F Design Tradeoff",
+ "Level": 0
+ },
+ {
+ "Id": "mental-model",
+ "Text": "\uD83E\uDDE0 Mental Model",
+ "Level": 0
+ },
+ {
+ "Id": "summary",
+ "Text": "\u27A1\uFE0F Summary",
+ "Level": 0
+ }
+ ]
+}
\ No newline at end of file
diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/security/refresh-rotation.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/security/refresh-rotation.json
new file mode 100644
index 00000000..dd21f370
--- /dev/null
+++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/security/refresh-rotation.json
@@ -0,0 +1,87 @@
+{
+ "Slug": "security/refresh-rotation",
+ "Title": "Refresh Rotation",
+ "Html": "\n\u003Cp\u003ERefresh tokens in UltimateAuth are not simple long-lived tokens.\u003C/p\u003E\n\u003Cp\u003EThey are part of a \u003Cstrong\u003Erotation-based security system\u003C/strong\u003E designed to:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eprevent token replay\u003C/li\u003E\n\u003Cli\u003Edetect token theft\u003C/li\u003E\n\u003Cli\u003Eenforce forward-only session progression\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022why-rotation\u0022\u003E\uD83E\uDDE0 Why Rotation?\u003C/h2\u003E\n\u003Cp\u003EIn traditional systems:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Erefresh tokens are long-lived\u003C/li\u003E\n\u003Cli\u003Ethey can be reused multiple times\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 If stolen, an attacker can:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Esilently keep refreshing access tokens\u003C/li\u003E\n\u003Cli\u003Estay undetected\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EUltimateAuth solves this with:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Cstrong\u003Esingle-use refresh tokens (rotation)\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022rotation-model\u0022\u003E\uD83D\uDD01 Rotation Model\u003C/h2\u003E\n\u003Cp\u003EEach refresh token is:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eused exactly once\u003C/li\u003E\n\u003Cli\u003Ereplaced with a new token\u003C/li\u003E\n\u003Cli\u003Elinked to a chain\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EToken A \u2192 Token B \u2192 Token C \u2192 ...\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EWhen a refresh happens:\u003C/p\u003E\n\u003Col\u003E\n\u003Cli\u003EToken A is validated\u003C/li\u003E\n\u003Cli\u003EToken A is \u003Cstrong\u003Erevoked\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003EToken B is issued\u003C/li\u003E\n\u003Cli\u003EToken A is marked as replaced by B\u003C/li\u003E\n\u003C/ol\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022token-state\u0022\u003E\uD83D\uDD10 Token State\u003C/h2\u003E\n\u003Cp\u003EA refresh token can be:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EActive \u2192 valid and usable\u003C/li\u003E\n\u003Cli\u003ERevoked \u2192 already used or manually revoked\u003C/li\u003E\n\u003Cli\u003EExpired \u2192 lifetime exceeded\u003C/li\u003E\n\u003Cli\u003EReplaced \u2192 already rotated\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Only \u003Cstrong\u003Eactive tokens\u003C/strong\u003E are valid\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022reuse-detection\u0022\u003E\uD83D\uDEA8 Reuse Detection\u003C/h2\u003E\n\u003Cp\u003EThis is the most critical security feature.\u003C/p\u003E\n\u003Cp\u003EIf a refresh token is used \u003Cstrong\u003Eafter it has already been rotated\u003C/strong\u003E, it means:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 The token was reused\u003Cbr /\u003E\n\uD83D\uDC49 Likely stolen\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022what-happens\u0022\u003EWhat happens?\u003C/h3\u003E\n\u003Cp\u003EWhen reuse is detected:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Ethe system identifies the session chain\u003C/li\u003E\n\u003Cli\u003Ethe entire chain can be revoked\u003C/li\u003E\n\u003Cli\u003Eall related sessions become invalid\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This immediately cuts off both:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eattacker\u003C/li\u003E\n\u003Cli\u003Elegitimate user (forcing reauthentication)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022chain-awareness\u0022\u003E\uD83D\uDD17 Chain Awareness\u003C/h2\u003E\n\u003Cp\u003ERefresh tokens belong to a \u003Cstrong\u003Esession chain\u003C/strong\u003E.\u003C/p\u003E\n\u003Cp\u003EThis enables:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Etracking rotation history\u003C/li\u003E\n\u003Cli\u003Edetecting anomalies\u003C/li\u003E\n\u003Cli\u003Eapplying revocation at the correct scope\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EWithout chains:\u003C/p\u003E\n\u003Cp\u003E\u274C You cannot safely detect reuse\u003Cbr /\u003E\n\u274C You cannot know which tokens belong together\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022rotation-flow\u0022\u003E\uD83D\uDD04 Rotation Flow\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EClient \u2192 Refresh(Token A)\n \u2192 Validate A\n \u2192 Revoke A\n \u2192 Issue B\n \u2192 Return new tokens\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022invalid-scenarios\u0022\u003E\u26A0\uFE0F Invalid Scenarios\u003C/h2\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022expired-token\u0022\u003E1. Expired Token\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EToken expired \u2192 reject\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022revoked-token\u0022\u003E2. Revoked Token\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EToken already used \u2192 reuse detected\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022session-mismatch\u0022\u003E3. Session Mismatch\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EToken does not belong to expected session \u2192 reject\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022security-guarantees\u0022\u003E\uD83E\uDDE0 Security Guarantees\u003C/h2\u003E\n\u003Cp\u003ERotation ensures:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Erefresh tokens are forward-only\u003C/li\u003E\n\u003Cli\u003Eold tokens cannot be reused safely\u003C/li\u003E\n\u003Cli\u003Estolen tokens are detectable\u003C/li\u003E\n\u003Cli\u003Ecompromise triggers containment\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022why-this-matters\u0022\u003E\uD83D\uDD25 Why This Matters\u003C/h2\u003E\n\u003Cp\u003ECompared to traditional refresh tokens:\u003C/p\u003E\n\u003Ctable\u003E\n\u003Cthead\u003E\n\u003Ctr\u003E\n\u003Cth\u003EFeature\u003C/th\u003E\n\u003Cth\u003ETraditional\u003C/th\u003E\n\u003Cth\u003EUltimateAuth\u003C/th\u003E\n\u003C/tr\u003E\n\u003C/thead\u003E\n\u003Ctbody\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EReusable tokens\u003C/td\u003E\n\u003Ctd\u003E\u2705\u003C/td\u003E\n\u003Ctd\u003E\u274C\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EReuse detection\u003C/td\u003E\n\u003Ctd\u003E\u274C\u003C/td\u003E\n\u003Ctd\u003E\u2705\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EChain awareness\u003C/td\u003E\n\u003Ctd\u003E\u274C\u003C/td\u003E\n\u003Ctd\u003E\u2705\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EAutomatic containment\u003C/td\u003E\n\u003Ctd\u003E\u274C\u003C/td\u003E\n\u003Ctd\u003E\u2705\u003C/td\u003E\n\u003C/tr\u003E\n\u003C/tbody\u003E\n\u003C/table\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022design-tradeoff\u0022\u003E\u26A0\uFE0F Design Tradeoff\u003C/h2\u003E\n\u003Cp\u003ERotation requires:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Etoken storage\u003C/li\u003E\n\u003Cli\u003Estate tracking\u003C/li\u003E\n\u003Cli\u003Eadditional validation logic\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 UltimateAuth chooses security over simplicity.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 A refresh token is not a reusable key\u003Cbr /\u003E\n\uD83D\uDC49 It is a \u003Cstrong\u003Eone-time step in a chain\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003EContinue to \u003Cstrong\u003EAccess Token Behavior\u003C/strong\u003E to understand how short-lived tokens interact with rotation.\u003C/p\u003E\n",
+ "Headings": [
+ {
+ "Id": "why-rotation",
+ "Text": "\uD83E\uDDE0 Why Rotation?",
+ "Level": 0
+ },
+ {
+ "Id": "rotation-model",
+ "Text": "\uD83D\uDD01 Rotation Model",
+ "Level": 0
+ },
+ {
+ "Id": "token-state",
+ "Text": "\uD83D\uDD10 Token State",
+ "Level": 0
+ },
+ {
+ "Id": "reuse-detection",
+ "Text": "\uD83D\uDEA8 Reuse Detection",
+ "Level": 0
+ },
+ {
+ "Id": "what-happens",
+ "Text": "What happens?",
+ "Level": 1
+ },
+ {
+ "Id": "chain-awareness",
+ "Text": "\uD83D\uDD17 Chain Awareness",
+ "Level": 0
+ },
+ {
+ "Id": "rotation-flow",
+ "Text": "\uD83D\uDD04 Rotation Flow",
+ "Level": 0
+ },
+ {
+ "Id": "invalid-scenarios",
+ "Text": "\u26A0\uFE0F Invalid Scenarios",
+ "Level": 0
+ },
+ {
+ "Id": "expired-token",
+ "Text": "1. Expired Token",
+ "Level": 1
+ },
+ {
+ "Id": "revoked-token",
+ "Text": "2. Revoked Token",
+ "Level": 1
+ },
+ {
+ "Id": "session-mismatch",
+ "Text": "3. Session Mismatch",
+ "Level": 1
+ },
+ {
+ "Id": "security-guarantees",
+ "Text": "\uD83E\uDDE0 Security Guarantees",
+ "Level": 0
+ },
+ {
+ "Id": "why-this-matters",
+ "Text": "\uD83D\uDD25 Why This Matters",
+ "Level": 0
+ },
+ {
+ "Id": "design-tradeoff",
+ "Text": "\u26A0\uFE0F Design Tradeoff",
+ "Level": 0
+ },
+ {
+ "Id": "mental-model",
+ "Text": "\uD83E\uDDE0 Mental Model",
+ "Level": 0
+ },
+ {
+ "Id": "next-step",
+ "Text": "\u27A1\uFE0F Next Step",
+ "Level": 0
+ }
+ ]
+}
\ No newline at end of file
diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/security/session-security-model.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/security/session-security-model.json
new file mode 100644
index 00000000..abb996ca
--- /dev/null
+++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/security/session-security-model.json
@@ -0,0 +1,97 @@
+{
+ "Slug": "security/session-security-model",
+ "Title": "Session Security Model",
+ "Html": "\n\u003Cp\u003EUltimateAuth is built around a \u003Cstrong\u003Ehierarchical session model\u003C/strong\u003E.\u003C/p\u003E\n\u003Cp\u003EThis is the foundation of its security design.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022why-a-session-model\u0022\u003E\uD83E\uDDE0 Why a Session Model?\u003C/h2\u003E\n\u003Cp\u003EMany systems treat authentication as one of these:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Ea cookie\u003C/li\u003E\n\u003Cli\u003Ea bearer token\u003C/li\u003E\n\u003Cli\u003Ea login flag\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EThat works for simple apps.\u003C/p\u003E\n\u003Cp\u003EIt breaks down when you need:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eper-device isolation\u003C/li\u003E\n\u003Cli\u003Etargeted revocation\u003C/li\u003E\n\u003Cli\u003Esecurity state propagation\u003C/li\u003E\n\u003Cli\u003Ereliable reauthentication boundaries\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EUltimateAuth solves this by modeling authentication as:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003ERoot \u2192 Chain \u2192 Session\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022the-three-layers\u0022\u003E\uD83E\uDDE9 The Three Layers\u003C/h2\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022root\u0022\u003E\uD83D\uDD39 Root\u003C/h3\u003E\n\u003Cp\u003EA \u003Cstrong\u003ERoot\u003C/strong\u003E represents the user-level authentication authority.\u003C/p\u003E\n\u003Cp\u003EIt is:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Etenant-bound\u003C/li\u003E\n\u003Cli\u003Euser-bound\u003C/li\u003E\n\u003Cli\u003Elong-lived\u003C/li\u003E\n\u003Cli\u003Esecurity-versioned\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EThe root is the highest-level trust anchor for authentication state.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022chain\u0022\u003E\uD83D\uDCF1 Chain\u003C/h3\u003E\n\u003Cp\u003EA \u003Cstrong\u003EChain\u003C/strong\u003E represents a device-level authentication boundary.\u003C/p\u003E\n\u003Cp\u003EIt groups sessions that belong to the same device or client context.\u003C/p\u003E\n\u003Cp\u003EA chain is where UltimateAuth models:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Edevice continuity\u003C/li\u003E\n\u003Cli\u003Etouch activity\u003C/li\u003E\n\u003Cli\u003Erotation tracking\u003C/li\u003E\n\u003Cli\u003Edevice-level revoke\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022session\u0022\u003E\uD83D\uDD11 Session\u003C/h3\u003E\n\u003Cp\u003EA \u003Cstrong\u003ESession\u003C/strong\u003E is a single authentication instance.\u003C/p\u003E\n\u003Cp\u003EIt is the most granular identity proof in the system.\u003C/p\u003E\n\u003Cp\u003EA session contains:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Ecreation time\u003C/li\u003E\n\u003Cli\u003Eexpiration time\u003C/li\u003E\n\u003Cli\u003Erevocation state\u003C/li\u003E\n\u003Cli\u003Esecurity version snapshot\u003C/li\u003E\n\u003Cli\u003Echain relationship\u003C/li\u003E\n\u003Cli\u003Edevice snapshot\u003C/li\u003E\n\u003Cli\u003Eclaims snapshot\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022relationship-model\u0022\u003E\uD83D\uDD17 Relationship Model\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EUser\n \u2514\u2500\u2500 Root\n \u2514\u2500\u2500 Chain (device)\n \u2514\u2500\u2500 Session (login instance)\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Root answers: \u003Cstrong\u003Ewhat is the current security authority for this user?\u003C/strong\u003E\u003Cbr /\u003E\n\uD83D\uDC49 Chain answers: \u003Cstrong\u003Ewhich device context is this?\u003C/strong\u003E\u003Cbr /\u003E\n\uD83D\uDC49 Session answers: \u003Cstrong\u003Ewhich authentication instance is being used?\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022security-versioning\u0022\u003E\uD83D\uDEE1 Security Versioning\u003C/h2\u003E\n\u003Cp\u003EOne of the most important protections in UltimateAuth is \u003Cstrong\u003Esecurity versioning\u003C/strong\u003E.\u003C/p\u003E\n\u003Cp\u003EA root maintains a security version.\u003C/p\u003E\n\u003Cp\u003EEach session stores the security version that existed at creation time.\u003C/p\u003E\n\u003Cp\u003EValidation compares them.\u003C/p\u003E\n\u003Cp\u003EIf they no longer match:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003ESession \u2192 invalid\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EThis is how UltimateAuth can invalidate existing sessions after events such as:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Epassword change\u003C/li\u003E\n\u003Cli\u003Ecredential reset\u003C/li\u003E\n\u003Cli\u003Eaccount recovery\u003C/li\u003E\n\u003Cli\u003Eadministrative security action\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022validation-model\u0022\u003E\uD83D\uDD0D Validation Model\u003C/h2\u003E\n\u003Cp\u003ESession validation is not a single check.\u003C/p\u003E\n\u003Cp\u003EIt is a layered verification process.\u003C/p\u003E\n\u003Cp\u003EA session is considered valid only if all of these still hold:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Ethe session exists\u003C/li\u003E\n\u003Cli\u003Ethe session is active\u003C/li\u003E\n\u003Cli\u003Ethe chain exists\u003C/li\u003E\n\u003Cli\u003Ethe chain is active\u003C/li\u003E\n\u003Cli\u003Ethe chain belongs to the expected tenant\u003C/li\u003E\n\u003Cli\u003Ethe chain matches the session\u003C/li\u003E\n\u003Cli\u003Ethe root exists\u003C/li\u003E\n\u003Cli\u003Ethe root is not revoked\u003C/li\u003E\n\u003Cli\u003Ethe root matches the chain\u003C/li\u003E\n\u003Cli\u003Ethe root security version matches the session snapshot\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022device-awareness\u0022\u003E\uD83D\uDCF1 Device Awareness\u003C/h2\u003E\n\u003Cp\u003EChains provide device-level isolation.\u003C/p\u003E\n\u003Cp\u003EWhen a device identifier is available, validation can compare:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Edevice stored on chain\u003C/li\u003E\n\u003Cli\u003Edevice presented by request\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EIf they do not match, UltimateAuth can reject the request depending on configuration.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 This makes device-bound security enforceable without turning every authentication flow into a custom implementation.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022expiration-and-activity\u0022\u003E\u23F1 Expiration and Activity\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth separates:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Esession expiration\u003C/li\u003E\n\u003Cli\u003Echain activity\u003C/li\u003E\n\u003Cli\u003Eroot security state\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EThis is important.\u003C/p\u003E\n\u003Cp\u003EA session may expire because of time.\u003Cbr /\u003E\nA chain may become inactive because of idle timeout.\u003Cbr /\u003E\nA root may invalidate everything because security state changed.\u003C/p\u003E\n\u003Cp\u003EThese are different failure modes with different meanings.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022revocation-boundaries\u0022\u003E\uD83D\uDEAA Revocation Boundaries\u003C/h2\u003E\n\u003Cp\u003ERevocation is also hierarchical.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022session-revoke\u0022\u003ESession revoke\u003C/h3\u003E\n\u003Cp\u003EInvalidates one authentication instance.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022chain-revoke\u0022\u003EChain revoke\u003C/h3\u003E\n\u003Cp\u003EInvalidates all sessions for one device.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022root-revoke\u0022\u003ERoot revoke\u003C/h3\u003E\n\u003Cp\u003EInvalidates all chains and sessions for the user.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 This gives UltimateAuth targeted containment.\u003C/p\u003E\n\u003Cp\u003EInstead of \u201Clog out everywhere or nowhere,\u201D\u003Cbr /\u003E\nyou can revoke exactly the right scope.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022why-this-matters\u0022\u003E\uD83D\uDD25 Why This Matters\u003C/h2\u003E\n\u003Cp\u003EThis model gives you controls that flat token systems usually do not provide:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Erevoke one device without affecting others\u003C/li\u003E\n\u003Cli\u003Einvalidate sessions after security changes\u003C/li\u003E\n\u003Cli\u003Ereason about device trust explicitly\u003C/li\u003E\n\u003Cli\u003Eseparate authentication lifetime from token lifetime\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022design-tradeoff\u0022\u003E\u26A0\uFE0F Design Tradeoff\u003C/h2\u003E\n\u003Cp\u003EThis model is more sophisticated than plain JWT-only authentication.\u003C/p\u003E\n\u003Cp\u003EThat is intentional.\u003C/p\u003E\n\u003Cp\u003EUltimateAuth chooses:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eexplicit state\u003C/li\u003E\n\u003Cli\u003Erevocability\u003C/li\u003E\n\u003Cli\u003Etraceable security decisions\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003Eover:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eminimal infrastructure\u003C/li\u003E\n\u003Cli\u003Epurely stateless assumptions\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Authentication in UltimateAuth is not \u201Ca token\u201D\u003Cbr /\u003E\n\uD83D\uDC49 It is a \u003Cstrong\u003Esecurity hierarchy with revocation boundaries\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003EContinue to \u003Cstrong\u003ERefresh Token Rotation\u003C/strong\u003E to understand how continuation security works after login.\u003C/p\u003E\n",
+ "Headings": [
+ {
+ "Id": "why-a-session-model",
+ "Text": "\uD83E\uDDE0 Why a Session Model?",
+ "Level": 0
+ },
+ {
+ "Id": "the-three-layers",
+ "Text": "\uD83E\uDDE9 The Three Layers",
+ "Level": 0
+ },
+ {
+ "Id": "root",
+ "Text": "\uD83D\uDD39 Root",
+ "Level": 1
+ },
+ {
+ "Id": "chain",
+ "Text": "\uD83D\uDCF1 Chain",
+ "Level": 1
+ },
+ {
+ "Id": "session",
+ "Text": "\uD83D\uDD11 Session",
+ "Level": 1
+ },
+ {
+ "Id": "relationship-model",
+ "Text": "\uD83D\uDD17 Relationship Model",
+ "Level": 0
+ },
+ {
+ "Id": "security-versioning",
+ "Text": "\uD83D\uDEE1 Security Versioning",
+ "Level": 0
+ },
+ {
+ "Id": "validation-model",
+ "Text": "\uD83D\uDD0D Validation Model",
+ "Level": 0
+ },
+ {
+ "Id": "device-awareness",
+ "Text": "\uD83D\uDCF1 Device Awareness",
+ "Level": 0
+ },
+ {
+ "Id": "expiration-and-activity",
+ "Text": "\u23F1 Expiration and Activity",
+ "Level": 0
+ },
+ {
+ "Id": "revocation-boundaries",
+ "Text": "\uD83D\uDEAA Revocation Boundaries",
+ "Level": 0
+ },
+ {
+ "Id": "session-revoke",
+ "Text": "Session revoke",
+ "Level": 1
+ },
+ {
+ "Id": "chain-revoke",
+ "Text": "Chain revoke",
+ "Level": 1
+ },
+ {
+ "Id": "root-revoke",
+ "Text": "Root revoke",
+ "Level": 1
+ },
+ {
+ "Id": "why-this-matters",
+ "Text": "\uD83D\uDD25 Why This Matters",
+ "Level": 0
+ },
+ {
+ "Id": "design-tradeoff",
+ "Text": "\u26A0\uFE0F Design Tradeoff",
+ "Level": 0
+ },
+ {
+ "Id": "mental-model",
+ "Text": "\uD83E\uDDE0 Mental Model",
+ "Level": 0
+ },
+ {
+ "Id": "next-step",
+ "Text": "\u27A1\uFE0F Next Step",
+ "Level": 0
+ }
+ ]
+}
\ No newline at end of file
diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm/Components/App.razor b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm/Components/App.razor
index 4c724313..8534df7b 100644
--- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm/Components/App.razor
+++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm/Components/App.razor
@@ -37,6 +37,7 @@
+
diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm/Program.cs b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm/Program.cs
index d75216af..01e3ec41 100644
--- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm/Program.cs
+++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm/Program.cs
@@ -11,6 +11,9 @@
builder.Services.AddMudServices();
builder.Services.AddMudExtensions();
+builder.Services.AddHttpClient();
+builder.Services.AddScoped();
+
var app = builder.Build();
if (app.Environment.IsDevelopment())
diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm/wwwroot/app.css b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm/wwwroot/app.css
index 9eede6fb..9811aa15 100644
--- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm/wwwroot/app.css
+++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm/wwwroot/app.css
@@ -197,3 +197,78 @@ h1:focus {
position: relative;
z-index: 9999;
}
+
+
+
+
+.docs-container {
+ padding-top: 32px;
+ padding-bottom: 64px;
+}
+
+.docs-title {
+ font-weight: 600;
+}
+
+.docs-content {
+ line-height: 1.7;
+ font-size: 15px;
+}
+
+ .docs-content h1,
+ .docs-content h2,
+ .docs-content h3 {
+ margin-top: 2rem;
+ font-weight: 600;
+ }
+
+ .docs-content p {
+ margin: 1rem 0;
+ }
+
+ .docs-content ul {
+ padding-left: 1.5rem;
+ }
+
+ .docs-content pre {
+ background: #0d1117;
+ padding: 1rem;
+ border-radius: 10px;
+ overflow-x: auto;
+ margin: 1.5rem 0;
+ }
+
+ .docs-content code {
+ font-family: Consolas, monospace;
+ font-size: 0.9rem;
+ }
+
+ .docs-content p code {
+ background: rgba(135,131,120,0.15);
+ padding: 2px 6px;
+ border-radius: 6px;
+ }
+
+.docs-loading {
+ display: flex;
+ justify-content: center;
+ margin-top: 100px;
+}
+
+.docs-toc-container {
+ position: sticky;
+ top: 80px;
+ height: fit-content;
+}
+
+.toc-active {
+ font-weight: 600;
+ color: var(--mud-palette-primary);
+ border-left: 2px solid var(--mud-palette-primary);
+ background: var(--mud-palette-background-gray);
+}
+
+.docs-content h2,
+.docs-content h3 {
+ scroll-margin-top: 240px;
+}
diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm/wwwroot/docs.js b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm/wwwroot/docs.js
new file mode 100644
index 00000000..8d695850
--- /dev/null
+++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm/wwwroot/docs.js
@@ -0,0 +1,73 @@
+ο»Ώwindow.uaDocsScrollSpy = (() => {
+
+ let lastId = null;
+ let handler = null;
+
+ function start(dotnetRef) {
+
+ stop();
+
+ handler = () => {
+
+ const elements = document.querySelectorAll(".docs-content h2, .docs-content h3");
+ if (!elements.length) return;
+
+ const center = window.innerHeight / 2;
+
+ let best = null;
+ let minDiff = Number.MAX_SAFE_INTEGER;
+
+ for (const el of elements) {
+ const rect = el.getBoundingClientRect();
+ const diff = Math.abs(rect.top - center);
+
+ if (rect.top <= center && diff < minDiff) {
+ minDiff = diff;
+ best = el;
+ }
+ }
+
+ if (!best) return;
+
+ const id = best.id;
+
+ if (id !== lastId) {
+ lastId = id;
+ history.replaceState(null, "", window.location.pathname + "#" + id);
+ dotnetRef.invokeMethodAsync("SetActiveHeading", id);
+ }
+ };
+
+ document.addEventListener('scroll', handler, true);
+ window.addEventListener('resize', handler, true);
+
+ handler();
+ }
+
+ function stop() {
+ if (handler) {
+ document.removeEventListener('scroll', handler, true);
+ window.removeEventListener('resize', handler, true);
+ handler = null;
+ }
+ }
+
+ function scrollTo(id) {
+ const el = document.getElementById(id);
+ if (!el) return;
+
+ const offset = 300;
+ const y = window.scrollY + el.getBoundingClientRect().top - offset;
+
+ window.scrollTo({
+ top: y,
+ behavior: "smooth"
+ });
+ }
+
+ return {
+ start,
+ stop,
+ scrollTo
+ };
+})();
diff --git a/nuget/CodeBeam.UltimateAuth.EntityFrameworkCore/CodeBeam.UltimateAuth.EntityFrameworkCore.Bundle.csproj b/nuget/CodeBeam.UltimateAuth.EntityFrameworkCore/CodeBeam.UltimateAuth.EntityFrameworkCore.Bundle.csproj
index 6b0ed53a..29faf8f7 100644
--- a/nuget/CodeBeam.UltimateAuth.EntityFrameworkCore/CodeBeam.UltimateAuth.EntityFrameworkCore.Bundle.csproj
+++ b/nuget/CodeBeam.UltimateAuth.EntityFrameworkCore/CodeBeam.UltimateAuth.EntityFrameworkCore.Bundle.csproj
@@ -13,7 +13,7 @@
authentication;authorization;identity;efcore;inmemory;bundle;auth-framework;security;jwt
- logo.png
+ uauthlogo.png
README.md
@@ -28,7 +28,7 @@
-
+
diff --git a/nuget/CodeBeam.UltimateAuth.EntityFrameworkCore/logo.png b/nuget/CodeBeam.UltimateAuth.EntityFrameworkCore/logo.png
deleted file mode 100644
index aa51a469..00000000
Binary files a/nuget/CodeBeam.UltimateAuth.EntityFrameworkCore/logo.png and /dev/null differ
diff --git a/nuget/CodeBeam.UltimateAuth.EntityFrameworkCore/uauthlogo.png b/nuget/CodeBeam.UltimateAuth.EntityFrameworkCore/uauthlogo.png
new file mode 100644
index 00000000..911f2530
Binary files /dev/null and b/nuget/CodeBeam.UltimateAuth.EntityFrameworkCore/uauthlogo.png differ
diff --git a/nuget/CodeBeam.UltimateAuth.InMemory/CodeBeam.UltimateAuth.InMemory.Bundle.csproj b/nuget/CodeBeam.UltimateAuth.InMemory/CodeBeam.UltimateAuth.InMemory.Bundle.csproj
index ee75955b..45fc3e18 100644
--- a/nuget/CodeBeam.UltimateAuth.InMemory/CodeBeam.UltimateAuth.InMemory.Bundle.csproj
+++ b/nuget/CodeBeam.UltimateAuth.InMemory/CodeBeam.UltimateAuth.InMemory.Bundle.csproj
@@ -14,7 +14,7 @@
authentication;authorization;identity;efcore;inmemory;bundle;auth-framework;security;jwt
- logo.png
+ uauthlogo.png
README.md
@@ -29,7 +29,7 @@
-
+
diff --git a/nuget/CodeBeam.UltimateAuth.InMemory/logo.png b/nuget/CodeBeam.UltimateAuth.InMemory/logo.png
deleted file mode 100644
index aa51a469..00000000
Binary files a/nuget/CodeBeam.UltimateAuth.InMemory/logo.png and /dev/null differ
diff --git a/nuget/CodeBeam.UltimateAuth.InMemory/uauthlogo.png b/nuget/CodeBeam.UltimateAuth.InMemory/uauthlogo.png
new file mode 100644
index 00000000..911f2530
Binary files /dev/null and b/nuget/CodeBeam.UltimateAuth.InMemory/uauthlogo.png differ
diff --git a/nuget/CodeBeam.UltimateAuth.Reference.Bundle/CodeBeam.UltimateAuth.Reference.Bundle.csproj b/nuget/CodeBeam.UltimateAuth.Reference.Bundle/CodeBeam.UltimateAuth.Reference.Bundle.csproj
index 5c921100..e8cde4d1 100644
--- a/nuget/CodeBeam.UltimateAuth.Reference.Bundle/CodeBeam.UltimateAuth.Reference.Bundle.csproj
+++ b/nuget/CodeBeam.UltimateAuth.Reference.Bundle/CodeBeam.UltimateAuth.Reference.Bundle.csproj
@@ -14,7 +14,7 @@
authentication;authorization;identity;reference;bundle;modular;auth-framework;users;credentials;policies
- logo.png
+ uauthlogo.png
README.md
@@ -26,7 +26,7 @@
-
+
diff --git a/nuget/CodeBeam.UltimateAuth.Reference.Bundle/logo.png b/nuget/CodeBeam.UltimateAuth.Reference.Bundle/logo.png
deleted file mode 100644
index aa51a469..00000000
Binary files a/nuget/CodeBeam.UltimateAuth.Reference.Bundle/logo.png and /dev/null differ
diff --git a/nuget/CodeBeam.UltimateAuth.Reference.Bundle/uauthlogo.png b/nuget/CodeBeam.UltimateAuth.Reference.Bundle/uauthlogo.png
new file mode 100644
index 00000000..911f2530
Binary files /dev/null and b/nuget/CodeBeam.UltimateAuth.Reference.Bundle/uauthlogo.png differ
diff --git a/src/CodeBeam.UltimateAuth.Core/CodeBeam.UltimateAuth.Core.csproj b/src/CodeBeam.UltimateAuth.Core/CodeBeam.UltimateAuth.Core.csproj
index 1b60c23c..c59475f7 100644
--- a/src/CodeBeam.UltimateAuth.Core/CodeBeam.UltimateAuth.Core.csproj
+++ b/src/CodeBeam.UltimateAuth.Core/CodeBeam.UltimateAuth.Core.csproj
@@ -8,7 +8,7 @@
Core domain primitives and abstractions for UltimateAuth. This package is not intended to be installed directly in most applications. Use CodeBeam.UltimateAuth.Server or Client packages instead.
authentication;authorization;identity;security;oauth;login;session;auth;refresh-token;pkce;dotnet;aspnetcore;blazor;maui;auth-framework
- logo.png
+ uauthlogo.png
README.md
@@ -19,7 +19,7 @@
-
+
diff --git a/src/CodeBeam.UltimateAuth.Core/logo.png b/src/CodeBeam.UltimateAuth.Core/logo.png
deleted file mode 100644
index aa51a469..00000000
Binary files a/src/CodeBeam.UltimateAuth.Core/logo.png and /dev/null differ
diff --git a/src/CodeBeam.UltimateAuth.Core/uauthlogo.png b/src/CodeBeam.UltimateAuth.Core/uauthlogo.png
new file mode 100644
index 00000000..911f2530
Binary files /dev/null and b/src/CodeBeam.UltimateAuth.Core/uauthlogo.png differ
diff --git a/src/CodeBeam.UltimateAuth.Server/CodeBeam.UltimateAuth.Server.csproj b/src/CodeBeam.UltimateAuth.Server/CodeBeam.UltimateAuth.Server.csproj
index 8694bcb9..6f21fab2 100644
--- a/src/CodeBeam.UltimateAuth.Server/CodeBeam.UltimateAuth.Server.csproj
+++ b/src/CodeBeam.UltimateAuth.Server/CodeBeam.UltimateAuth.Server.csproj
@@ -13,7 +13,7 @@
authentication;authorization;identity;security;server;oauth;pkce;jwt;aspnetcore;auth-framework
- logo.png
+ uauthlogo.png
README.md
@@ -34,7 +34,7 @@
-
+
diff --git a/src/CodeBeam.UltimateAuth.Server/logo.png b/src/CodeBeam.UltimateAuth.Server/logo.png
deleted file mode 100644
index aa51a469..00000000
Binary files a/src/CodeBeam.UltimateAuth.Server/logo.png and /dev/null differ
diff --git a/src/CodeBeam.UltimateAuth.Server/uauthlogo.png b/src/CodeBeam.UltimateAuth.Server/uauthlogo.png
new file mode 100644
index 00000000..911f2530
Binary files /dev/null and b/src/CodeBeam.UltimateAuth.Server/uauthlogo.png differ
diff --git a/src/authentication/CodeBeam.UltimateAuth.Authentication.EntityFrameworkCore/CodeBeam.UltimateAuth.Authentication.EntityFrameworkCore.csproj b/src/authentication/CodeBeam.UltimateAuth.Authentication.EntityFrameworkCore/CodeBeam.UltimateAuth.Authentication.EntityFrameworkCore.csproj
index f4246bad..4437d969 100644
--- a/src/authentication/CodeBeam.UltimateAuth.Authentication.EntityFrameworkCore/CodeBeam.UltimateAuth.Authentication.EntityFrameworkCore.csproj
+++ b/src/authentication/CodeBeam.UltimateAuth.Authentication.EntityFrameworkCore/CodeBeam.UltimateAuth.Authentication.EntityFrameworkCore.csproj
@@ -12,7 +12,7 @@
authentication;session;identity;efcore;database;auth-framework
- logo.png
+ uauthlogo.png
README.md
@@ -22,7 +22,7 @@
-
+
diff --git a/src/authentication/CodeBeam.UltimateAuth.Authentication.EntityFrameworkCore/logo.png b/src/authentication/CodeBeam.UltimateAuth.Authentication.EntityFrameworkCore/logo.png
deleted file mode 100644
index aa51a469..00000000
Binary files a/src/authentication/CodeBeam.UltimateAuth.Authentication.EntityFrameworkCore/logo.png and /dev/null differ
diff --git a/src/authentication/CodeBeam.UltimateAuth.Authentication.EntityFrameworkCore/uauthlogo.png b/src/authentication/CodeBeam.UltimateAuth.Authentication.EntityFrameworkCore/uauthlogo.png
new file mode 100644
index 00000000..911f2530
Binary files /dev/null and b/src/authentication/CodeBeam.UltimateAuth.Authentication.EntityFrameworkCore/uauthlogo.png differ
diff --git a/src/authentication/CodeBeam.UltimateAuth.Authentication.InMemory/CodeBeam.UltimateAuth.Authentication.InMemory.csproj b/src/authentication/CodeBeam.UltimateAuth.Authentication.InMemory/CodeBeam.UltimateAuth.Authentication.InMemory.csproj
index 1129e77a..c47f0f33 100644
--- a/src/authentication/CodeBeam.UltimateAuth.Authentication.InMemory/CodeBeam.UltimateAuth.Authentication.InMemory.csproj
+++ b/src/authentication/CodeBeam.UltimateAuth.Authentication.InMemory/CodeBeam.UltimateAuth.Authentication.InMemory.csproj
@@ -12,7 +12,7 @@
authentication;session;identity;inmemory;auth-framework
- logo.png
+ uauthlogo.png
README.md
@@ -22,7 +22,7 @@
-
+
diff --git a/src/authentication/CodeBeam.UltimateAuth.Authentication.InMemory/logo.png b/src/authentication/CodeBeam.UltimateAuth.Authentication.InMemory/logo.png
deleted file mode 100644
index aa51a469..00000000
Binary files a/src/authentication/CodeBeam.UltimateAuth.Authentication.InMemory/logo.png and /dev/null differ
diff --git a/src/authentication/CodeBeam.UltimateAuth.Authentication.InMemory/uauthlogo.png b/src/authentication/CodeBeam.UltimateAuth.Authentication.InMemory/uauthlogo.png
new file mode 100644
index 00000000..911f2530
Binary files /dev/null and b/src/authentication/CodeBeam.UltimateAuth.Authentication.InMemory/uauthlogo.png differ
diff --git a/src/authorization/CodeBeam.UltimateAuth.Authorization.Contracts/CodeBeam.UltimateAuth.Authorization.Contracts.csproj b/src/authorization/CodeBeam.UltimateAuth.Authorization.Contracts/CodeBeam.UltimateAuth.Authorization.Contracts.csproj
index 0d5428ad..46f73334 100644
--- a/src/authorization/CodeBeam.UltimateAuth.Authorization.Contracts/CodeBeam.UltimateAuth.Authorization.Contracts.csproj
+++ b/src/authorization/CodeBeam.UltimateAuth.Authorization.Contracts/CodeBeam.UltimateAuth.Authorization.Contracts.csproj
@@ -13,7 +13,7 @@
authentication;authorization;roles;permissions;contracts;shared;auth-framework
- logo.png
+ uauthlogo.png
README.md
@@ -22,7 +22,7 @@
-
+
diff --git a/src/authorization/CodeBeam.UltimateAuth.Authorization.Contracts/logo.png b/src/authorization/CodeBeam.UltimateAuth.Authorization.Contracts/logo.png
deleted file mode 100644
index aa51a469..00000000
Binary files a/src/authorization/CodeBeam.UltimateAuth.Authorization.Contracts/logo.png and /dev/null differ
diff --git a/src/authorization/CodeBeam.UltimateAuth.Authorization.Contracts/uauthlogo.png b/src/authorization/CodeBeam.UltimateAuth.Authorization.Contracts/uauthlogo.png
new file mode 100644
index 00000000..911f2530
Binary files /dev/null and b/src/authorization/CodeBeam.UltimateAuth.Authorization.Contracts/uauthlogo.png differ
diff --git a/src/authorization/CodeBeam.UltimateAuth.Authorization.EntityFrameworkCore/CodeBeam.UltimateAuth.Authorization.EntityFrameworkCore.csproj b/src/authorization/CodeBeam.UltimateAuth.Authorization.EntityFrameworkCore/CodeBeam.UltimateAuth.Authorization.EntityFrameworkCore.csproj
index ecb06423..76cfca96 100644
--- a/src/authorization/CodeBeam.UltimateAuth.Authorization.EntityFrameworkCore/CodeBeam.UltimateAuth.Authorization.EntityFrameworkCore.csproj
+++ b/src/authorization/CodeBeam.UltimateAuth.Authorization.EntityFrameworkCore/CodeBeam.UltimateAuth.Authorization.EntityFrameworkCore.csproj
@@ -12,7 +12,7 @@
authentication;authorization;roles;claims;efcore;database;auth-framework
- logo.png
+ uauthlogo.png
README.md
@@ -23,7 +23,7 @@
-
+
diff --git a/src/authorization/CodeBeam.UltimateAuth.Authorization.EntityFrameworkCore/logo.png b/src/authorization/CodeBeam.UltimateAuth.Authorization.EntityFrameworkCore/logo.png
deleted file mode 100644
index aa51a469..00000000
Binary files a/src/authorization/CodeBeam.UltimateAuth.Authorization.EntityFrameworkCore/logo.png and /dev/null differ
diff --git a/src/authorization/CodeBeam.UltimateAuth.Authorization.EntityFrameworkCore/uauthlogo.png b/src/authorization/CodeBeam.UltimateAuth.Authorization.EntityFrameworkCore/uauthlogo.png
new file mode 100644
index 00000000..911f2530
Binary files /dev/null and b/src/authorization/CodeBeam.UltimateAuth.Authorization.EntityFrameworkCore/uauthlogo.png differ
diff --git a/src/authorization/CodeBeam.UltimateAuth.Authorization.InMemory/CodeBeam.UltimateAuth.Authorization.InMemory.csproj b/src/authorization/CodeBeam.UltimateAuth.Authorization.InMemory/CodeBeam.UltimateAuth.Authorization.InMemory.csproj
index 90ed11f3..a7723fcd 100644
--- a/src/authorization/CodeBeam.UltimateAuth.Authorization.InMemory/CodeBeam.UltimateAuth.Authorization.InMemory.csproj
+++ b/src/authorization/CodeBeam.UltimateAuth.Authorization.InMemory/CodeBeam.UltimateAuth.Authorization.InMemory.csproj
@@ -13,7 +13,7 @@
authentication;authorization;roles;claims;inmemory;auth-framework
- logo.png
+ uauthlogo.png
README.md
@@ -24,7 +24,7 @@
-
+
diff --git a/src/authorization/CodeBeam.UltimateAuth.Authorization.InMemory/logo.png b/src/authorization/CodeBeam.UltimateAuth.Authorization.InMemory/logo.png
deleted file mode 100644
index aa51a469..00000000
Binary files a/src/authorization/CodeBeam.UltimateAuth.Authorization.InMemory/logo.png and /dev/null differ
diff --git a/src/authorization/CodeBeam.UltimateAuth.Authorization.InMemory/uauthlogo.png b/src/authorization/CodeBeam.UltimateAuth.Authorization.InMemory/uauthlogo.png
new file mode 100644
index 00000000..911f2530
Binary files /dev/null and b/src/authorization/CodeBeam.UltimateAuth.Authorization.InMemory/uauthlogo.png differ
diff --git a/src/authorization/CodeBeam.UltimateAuth.Authorization.Reference/CodeBeam.UltimateAuth.Authorization.Reference.csproj b/src/authorization/CodeBeam.UltimateAuth.Authorization.Reference/CodeBeam.UltimateAuth.Authorization.Reference.csproj
index 1eff5491..82976ca3 100644
--- a/src/authorization/CodeBeam.UltimateAuth.Authorization.Reference/CodeBeam.UltimateAuth.Authorization.Reference.csproj
+++ b/src/authorization/CodeBeam.UltimateAuth.Authorization.Reference/CodeBeam.UltimateAuth.Authorization.Reference.csproj
@@ -12,7 +12,7 @@
authorization;roles;claims;rbac;policy;security;auth-framework
- logo.png
+ uauthlogo.png
README.md
@@ -24,7 +24,7 @@
-
+
diff --git a/src/authorization/CodeBeam.UltimateAuth.Authorization.Reference/logo.png b/src/authorization/CodeBeam.UltimateAuth.Authorization.Reference/logo.png
deleted file mode 100644
index aa51a469..00000000
Binary files a/src/authorization/CodeBeam.UltimateAuth.Authorization.Reference/logo.png and /dev/null differ
diff --git a/src/authorization/CodeBeam.UltimateAuth.Authorization.Reference/uauthlogo.png b/src/authorization/CodeBeam.UltimateAuth.Authorization.Reference/uauthlogo.png
new file mode 100644
index 00000000..911f2530
Binary files /dev/null and b/src/authorization/CodeBeam.UltimateAuth.Authorization.Reference/uauthlogo.png differ
diff --git a/src/authorization/CodeBeam.UltimateAuth.Authorization/CodeBeam.UltimateAuth.Authorization.csproj b/src/authorization/CodeBeam.UltimateAuth.Authorization/CodeBeam.UltimateAuth.Authorization.csproj
index 50c5dc51..00dad5a1 100644
--- a/src/authorization/CodeBeam.UltimateAuth.Authorization/CodeBeam.UltimateAuth.Authorization.csproj
+++ b/src/authorization/CodeBeam.UltimateAuth.Authorization/CodeBeam.UltimateAuth.Authorization.csproj
@@ -14,7 +14,7 @@
authentication;authorization;roles;permissions;security;module;auth-framework
- logo.png
+ uauthlogo.png
README.md
@@ -24,7 +24,7 @@
-
+
diff --git a/src/authorization/CodeBeam.UltimateAuth.Authorization/logo.png b/src/authorization/CodeBeam.UltimateAuth.Authorization/logo.png
deleted file mode 100644
index aa51a469..00000000
Binary files a/src/authorization/CodeBeam.UltimateAuth.Authorization/logo.png and /dev/null differ
diff --git a/src/authorization/CodeBeam.UltimateAuth.Authorization/uauthlogo.png b/src/authorization/CodeBeam.UltimateAuth.Authorization/uauthlogo.png
new file mode 100644
index 00000000..911f2530
Binary files /dev/null and b/src/authorization/CodeBeam.UltimateAuth.Authorization/uauthlogo.png differ
diff --git a/src/client/CodeBeam.UltimateAuth.Client.Blazor/CodeBeam.UltimateAuth.Client.Blazor.csproj b/src/client/CodeBeam.UltimateAuth.Client.Blazor/CodeBeam.UltimateAuth.Client.Blazor.csproj
index 5a9a7ac4..02509da9 100644
--- a/src/client/CodeBeam.UltimateAuth.Client.Blazor/CodeBeam.UltimateAuth.Client.Blazor.csproj
+++ b/src/client/CodeBeam.UltimateAuth.Client.Blazor/CodeBeam.UltimateAuth.Client.Blazor.csproj
@@ -12,7 +12,7 @@
authentication;authorization;identity;blazor;wasm;aspnetcore;client;auth;oauth;pkce;jwt;auth-framework
- logo.png
+ uauthlogo.png
README.md
@@ -57,7 +57,7 @@
-
+
diff --git a/src/client/CodeBeam.UltimateAuth.Client.Blazor/logo.png b/src/client/CodeBeam.UltimateAuth.Client.Blazor/logo.png
deleted file mode 100644
index aa51a469..00000000
Binary files a/src/client/CodeBeam.UltimateAuth.Client.Blazor/logo.png and /dev/null differ
diff --git a/src/client/CodeBeam.UltimateAuth.Client.Blazor/uauthlogo.png b/src/client/CodeBeam.UltimateAuth.Client.Blazor/uauthlogo.png
new file mode 100644
index 00000000..911f2530
Binary files /dev/null and b/src/client/CodeBeam.UltimateAuth.Client.Blazor/uauthlogo.png differ
diff --git a/src/client/CodeBeam.UltimateAuth.Client/CodeBeam.UltimateAuth.Client.csproj b/src/client/CodeBeam.UltimateAuth.Client/CodeBeam.UltimateAuth.Client.csproj
index b519d1ec..fa4eaed0 100644
--- a/src/client/CodeBeam.UltimateAuth.Client/CodeBeam.UltimateAuth.Client.csproj
+++ b/src/client/CodeBeam.UltimateAuth.Client/CodeBeam.UltimateAuth.Client.csproj
@@ -8,7 +8,7 @@
Core client engine for UltimateAuth. Provides platform-agnostic authentication features. This package does NOT include transport, storage or UI integration. For complete experience, use a platform adapter such as CodeBeam.UltimateAuth.Client.Blazor
authentication;authorization;identity;aspnetcore;auth;oauth;pkce;jwt;auth-framework
- logo.png
+ uauthlogo.png
README.md
@@ -20,7 +20,7 @@
-
+
diff --git a/src/client/CodeBeam.UltimateAuth.Client/logo.png b/src/client/CodeBeam.UltimateAuth.Client/logo.png
deleted file mode 100644
index aa51a469..00000000
Binary files a/src/client/CodeBeam.UltimateAuth.Client/logo.png and /dev/null differ
diff --git a/src/client/CodeBeam.UltimateAuth.Client/uauthlogo.png b/src/client/CodeBeam.UltimateAuth.Client/uauthlogo.png
new file mode 100644
index 00000000..911f2530
Binary files /dev/null and b/src/client/CodeBeam.UltimateAuth.Client/uauthlogo.png differ
diff --git a/src/credentials/CodeBeam.UltimateAuth.Credentials.Contracts/CodeBeam.UltimateAuth.Credentials.Contracts.csproj b/src/credentials/CodeBeam.UltimateAuth.Credentials.Contracts/CodeBeam.UltimateAuth.Credentials.Contracts.csproj
index 0885e14c..376e6b7a 100644
--- a/src/credentials/CodeBeam.UltimateAuth.Credentials.Contracts/CodeBeam.UltimateAuth.Credentials.Contracts.csproj
+++ b/src/credentials/CodeBeam.UltimateAuth.Credentials.Contracts/CodeBeam.UltimateAuth.Credentials.Contracts.csproj
@@ -13,7 +13,7 @@
authentication;credentials;identity;contracts;shared;dto;auth-framework
- logo.png
+ uauthlogo.png
README.md
@@ -22,7 +22,7 @@
-
+
diff --git a/src/credentials/CodeBeam.UltimateAuth.Credentials.Contracts/logo.png b/src/credentials/CodeBeam.UltimateAuth.Credentials.Contracts/logo.png
deleted file mode 100644
index aa51a469..00000000
Binary files a/src/credentials/CodeBeam.UltimateAuth.Credentials.Contracts/logo.png and /dev/null differ
diff --git a/src/credentials/CodeBeam.UltimateAuth.Credentials.Contracts/uauthlogo.png b/src/credentials/CodeBeam.UltimateAuth.Credentials.Contracts/uauthlogo.png
new file mode 100644
index 00000000..911f2530
Binary files /dev/null and b/src/credentials/CodeBeam.UltimateAuth.Credentials.Contracts/uauthlogo.png differ
diff --git a/src/credentials/CodeBeam.UltimateAuth.Credentials.EntityFrameworkCore/CodeBeam.UltimateAuth.Credentials.EntityFrameworkCore.csproj b/src/credentials/CodeBeam.UltimateAuth.Credentials.EntityFrameworkCore/CodeBeam.UltimateAuth.Credentials.EntityFrameworkCore.csproj
index 9ed75d81..978ce78c 100644
--- a/src/credentials/CodeBeam.UltimateAuth.Credentials.EntityFrameworkCore/CodeBeam.UltimateAuth.Credentials.EntityFrameworkCore.csproj
+++ b/src/credentials/CodeBeam.UltimateAuth.Credentials.EntityFrameworkCore/CodeBeam.UltimateAuth.Credentials.EntityFrameworkCore.csproj
@@ -12,7 +12,7 @@
authentication;credentials;efcore;password;database;auth-framework
- logo.png
+ uauthlogo.png
README.md
@@ -24,7 +24,7 @@
-
+
diff --git a/src/credentials/CodeBeam.UltimateAuth.Credentials.EntityFrameworkCore/logo.png b/src/credentials/CodeBeam.UltimateAuth.Credentials.EntityFrameworkCore/logo.png
deleted file mode 100644
index aa51a469..00000000
Binary files a/src/credentials/CodeBeam.UltimateAuth.Credentials.EntityFrameworkCore/logo.png and /dev/null differ
diff --git a/src/credentials/CodeBeam.UltimateAuth.Credentials.EntityFrameworkCore/uauthlogo.png b/src/credentials/CodeBeam.UltimateAuth.Credentials.EntityFrameworkCore/uauthlogo.png
new file mode 100644
index 00000000..911f2530
Binary files /dev/null and b/src/credentials/CodeBeam.UltimateAuth.Credentials.EntityFrameworkCore/uauthlogo.png differ
diff --git a/src/credentials/CodeBeam.UltimateAuth.Credentials.InMemory/CodeBeam.UltimateAuth.Credentials.InMemory.csproj b/src/credentials/CodeBeam.UltimateAuth.Credentials.InMemory/CodeBeam.UltimateAuth.Credentials.InMemory.csproj
index 40c7c5ff..7bc21c6a 100644
--- a/src/credentials/CodeBeam.UltimateAuth.Credentials.InMemory/CodeBeam.UltimateAuth.Credentials.InMemory.csproj
+++ b/src/credentials/CodeBeam.UltimateAuth.Credentials.InMemory/CodeBeam.UltimateAuth.Credentials.InMemory.csproj
@@ -13,7 +13,7 @@
authentication;credentials;inmemory;password;auth-framework
- logo.png
+ uauthlogo.png
README.md
@@ -25,7 +25,7 @@
-
+
diff --git a/src/credentials/CodeBeam.UltimateAuth.Credentials.InMemory/logo.png b/src/credentials/CodeBeam.UltimateAuth.Credentials.InMemory/logo.png
deleted file mode 100644
index aa51a469..00000000
Binary files a/src/credentials/CodeBeam.UltimateAuth.Credentials.InMemory/logo.png and /dev/null differ
diff --git a/src/credentials/CodeBeam.UltimateAuth.Credentials.InMemory/uauthlogo.png b/src/credentials/CodeBeam.UltimateAuth.Credentials.InMemory/uauthlogo.png
new file mode 100644
index 00000000..911f2530
Binary files /dev/null and b/src/credentials/CodeBeam.UltimateAuth.Credentials.InMemory/uauthlogo.png differ
diff --git a/src/credentials/CodeBeam.UltimateAuth.Credentials.Reference/CodeBeam.UltimateAuth.Credentials.Reference.csproj b/src/credentials/CodeBeam.UltimateAuth.Credentials.Reference/CodeBeam.UltimateAuth.Credentials.Reference.csproj
index 1b75f477..ec99e9ab 100644
--- a/src/credentials/CodeBeam.UltimateAuth.Credentials.Reference/CodeBeam.UltimateAuth.Credentials.Reference.csproj
+++ b/src/credentials/CodeBeam.UltimateAuth.Credentials.Reference/CodeBeam.UltimateAuth.Credentials.Reference.csproj
@@ -12,7 +12,7 @@
authentication;credentials;password;security;reference;auth-framework
- logo.png
+ uauthlogo.png
README.md
@@ -23,7 +23,7 @@
-
+
diff --git a/src/credentials/CodeBeam.UltimateAuth.Credentials.Reference/logo.png b/src/credentials/CodeBeam.UltimateAuth.Credentials.Reference/logo.png
deleted file mode 100644
index aa51a469..00000000
Binary files a/src/credentials/CodeBeam.UltimateAuth.Credentials.Reference/logo.png and /dev/null differ
diff --git a/src/credentials/CodeBeam.UltimateAuth.Credentials.Reference/uauthlogo.png b/src/credentials/CodeBeam.UltimateAuth.Credentials.Reference/uauthlogo.png
new file mode 100644
index 00000000..911f2530
Binary files /dev/null and b/src/credentials/CodeBeam.UltimateAuth.Credentials.Reference/uauthlogo.png differ
diff --git a/src/credentials/CodeBeam.UltimateAuth.Credentials/CodeBeam.UltimateAuth.Credentials.csproj b/src/credentials/CodeBeam.UltimateAuth.Credentials/CodeBeam.UltimateAuth.Credentials.csproj
index 054b6d8d..53803f40 100644
--- a/src/credentials/CodeBeam.UltimateAuth.Credentials/CodeBeam.UltimateAuth.Credentials.csproj
+++ b/src/credentials/CodeBeam.UltimateAuth.Credentials/CodeBeam.UltimateAuth.Credentials.csproj
@@ -14,7 +14,7 @@
authentication;credentials;identity;security;module;auth-framework
- logo.png
+ uauthlogo.png
README.md
@@ -24,7 +24,7 @@
-
+
diff --git a/src/credentials/CodeBeam.UltimateAuth.Credentials/logo.png b/src/credentials/CodeBeam.UltimateAuth.Credentials/logo.png
deleted file mode 100644
index aa51a469..00000000
Binary files a/src/credentials/CodeBeam.UltimateAuth.Credentials/logo.png and /dev/null differ
diff --git a/src/credentials/CodeBeam.UltimateAuth.Credentials/uauthlogo.png b/src/credentials/CodeBeam.UltimateAuth.Credentials/uauthlogo.png
new file mode 100644
index 00000000..911f2530
Binary files /dev/null and b/src/credentials/CodeBeam.UltimateAuth.Credentials/uauthlogo.png differ
diff --git a/src/persistence/CodeBeam.UltimateAuth.EntityFrameworkCore/CodeBeam.UltimateAuth.EntityFrameworkCore.csproj b/src/persistence/CodeBeam.UltimateAuth.EntityFrameworkCore/CodeBeam.UltimateAuth.EntityFrameworkCore.csproj
index f563f619..11ada993 100644
--- a/src/persistence/CodeBeam.UltimateAuth.EntityFrameworkCore/CodeBeam.UltimateAuth.EntityFrameworkCore.csproj
+++ b/src/persistence/CodeBeam.UltimateAuth.EntityFrameworkCore/CodeBeam.UltimateAuth.EntityFrameworkCore.csproj
@@ -13,7 +13,7 @@
authentication;efcore;infrastructure;orm;auth-framework
- logo.png
+ uauthlogo.png
README.md
@@ -35,7 +35,7 @@
-
+
diff --git a/src/persistence/CodeBeam.UltimateAuth.EntityFrameworkCore/logo.png b/src/persistence/CodeBeam.UltimateAuth.EntityFrameworkCore/logo.png
deleted file mode 100644
index aa51a469..00000000
Binary files a/src/persistence/CodeBeam.UltimateAuth.EntityFrameworkCore/logo.png and /dev/null differ
diff --git a/src/persistence/CodeBeam.UltimateAuth.EntityFrameworkCore/uauthlogo.png b/src/persistence/CodeBeam.UltimateAuth.EntityFrameworkCore/uauthlogo.png
new file mode 100644
index 00000000..911f2530
Binary files /dev/null and b/src/persistence/CodeBeam.UltimateAuth.EntityFrameworkCore/uauthlogo.png differ
diff --git a/src/persistence/CodeBeam.UltimateAuth.InMemory/CodeBeam.UltimateAuth.InMemory.csproj b/src/persistence/CodeBeam.UltimateAuth.InMemory/CodeBeam.UltimateAuth.InMemory.csproj
index bf35b115..ca100aa8 100644
--- a/src/persistence/CodeBeam.UltimateAuth.InMemory/CodeBeam.UltimateAuth.InMemory.csproj
+++ b/src/persistence/CodeBeam.UltimateAuth.InMemory/CodeBeam.UltimateAuth.InMemory.csproj
@@ -13,7 +13,7 @@
authentication;inmemory;testing;cache;auth-framework
- logo.png
+ uauthlogo.png
README.md
@@ -22,7 +22,7 @@
-
+
diff --git a/src/persistence/CodeBeam.UltimateAuth.InMemory/logo.png b/src/persistence/CodeBeam.UltimateAuth.InMemory/logo.png
deleted file mode 100644
index aa51a469..00000000
Binary files a/src/persistence/CodeBeam.UltimateAuth.InMemory/logo.png and /dev/null differ
diff --git a/src/persistence/CodeBeam.UltimateAuth.InMemory/uauthlogo.png b/src/persistence/CodeBeam.UltimateAuth.InMemory/uauthlogo.png
new file mode 100644
index 00000000..911f2530
Binary files /dev/null and b/src/persistence/CodeBeam.UltimateAuth.InMemory/uauthlogo.png differ
diff --git a/src/policies/CodeBeam.UltimateAuth.Policies/CodeBeam.UltimateAuth.Policies.csproj b/src/policies/CodeBeam.UltimateAuth.Policies/CodeBeam.UltimateAuth.Policies.csproj
index 0078784a..6f703658 100644
--- a/src/policies/CodeBeam.UltimateAuth.Policies/CodeBeam.UltimateAuth.Policies.csproj
+++ b/src/policies/CodeBeam.UltimateAuth.Policies/CodeBeam.UltimateAuth.Policies.csproj
@@ -14,7 +14,7 @@
authentication;authorization;policies;permissions;security;auth-framework
- logo.png
+ uauthlogo.png
README.md
@@ -24,7 +24,7 @@
-
+
diff --git a/src/policies/CodeBeam.UltimateAuth.Policies/logo.png b/src/policies/CodeBeam.UltimateAuth.Policies/logo.png
deleted file mode 100644
index aa51a469..00000000
Binary files a/src/policies/CodeBeam.UltimateAuth.Policies/logo.png and /dev/null differ
diff --git a/src/policies/CodeBeam.UltimateAuth.Policies/uauthlogo.png b/src/policies/CodeBeam.UltimateAuth.Policies/uauthlogo.png
new file mode 100644
index 00000000..911f2530
Binary files /dev/null and b/src/policies/CodeBeam.UltimateAuth.Policies/uauthlogo.png differ
diff --git a/src/security/CodeBeam.UltimateAuth.Security.Argon2/CodeBeam.UltimateAuth.Security.Argon2.csproj b/src/security/CodeBeam.UltimateAuth.Security.Argon2/CodeBeam.UltimateAuth.Security.Argon2.csproj
index 3c58caae..1ed21519 100644
--- a/src/security/CodeBeam.UltimateAuth.Security.Argon2/CodeBeam.UltimateAuth.Security.Argon2.csproj
+++ b/src/security/CodeBeam.UltimateAuth.Security.Argon2/CodeBeam.UltimateAuth.Security.Argon2.csproj
@@ -12,7 +12,7 @@
authentication;security;argon2;password-hashing;cryptography;identity;auth-framework
- logo.png
+ uauthlogo.png
README.md
@@ -26,7 +26,7 @@
-
+
diff --git a/src/security/CodeBeam.UltimateAuth.Security.Argon2/logo.png b/src/security/CodeBeam.UltimateAuth.Security.Argon2/logo.png
deleted file mode 100644
index aa51a469..00000000
Binary files a/src/security/CodeBeam.UltimateAuth.Security.Argon2/logo.png and /dev/null differ
diff --git a/src/security/CodeBeam.UltimateAuth.Security.Argon2/uauthlogo.png b/src/security/CodeBeam.UltimateAuth.Security.Argon2/uauthlogo.png
new file mode 100644
index 00000000..911f2530
Binary files /dev/null and b/src/security/CodeBeam.UltimateAuth.Security.Argon2/uauthlogo.png differ
diff --git a/src/sessions/CodeBeam.UltimateAuth.Sessions.EntityFrameworkCore/CodeBeam.UltimateAuth.Sessions.EntityFrameworkCore.csproj b/src/sessions/CodeBeam.UltimateAuth.Sessions.EntityFrameworkCore/CodeBeam.UltimateAuth.Sessions.EntityFrameworkCore.csproj
index 2f642a6e..345afa17 100644
--- a/src/sessions/CodeBeam.UltimateAuth.Sessions.EntityFrameworkCore/CodeBeam.UltimateAuth.Sessions.EntityFrameworkCore.csproj
+++ b/src/sessions/CodeBeam.UltimateAuth.Sessions.EntityFrameworkCore/CodeBeam.UltimateAuth.Sessions.EntityFrameworkCore.csproj
@@ -12,7 +12,7 @@
authentication;sessions;efcore;database;auth-framework
- logo.png
+ uauthlogo.png
README.md
@@ -22,7 +22,7 @@
-
+
diff --git a/src/sessions/CodeBeam.UltimateAuth.Sessions.EntityFrameworkCore/logo.png b/src/sessions/CodeBeam.UltimateAuth.Sessions.EntityFrameworkCore/logo.png
deleted file mode 100644
index aa51a469..00000000
Binary files a/src/sessions/CodeBeam.UltimateAuth.Sessions.EntityFrameworkCore/logo.png and /dev/null differ
diff --git a/src/sessions/CodeBeam.UltimateAuth.Sessions.EntityFrameworkCore/uauthlogo.png b/src/sessions/CodeBeam.UltimateAuth.Sessions.EntityFrameworkCore/uauthlogo.png
new file mode 100644
index 00000000..911f2530
Binary files /dev/null and b/src/sessions/CodeBeam.UltimateAuth.Sessions.EntityFrameworkCore/uauthlogo.png differ
diff --git a/src/sessions/CodeBeam.UltimateAuth.Sessions.InMemory/CodeBeam.UltimateAuth.Sessions.InMemory.csproj b/src/sessions/CodeBeam.UltimateAuth.Sessions.InMemory/CodeBeam.UltimateAuth.Sessions.InMemory.csproj
index 52f19a0c..09a0ea22 100644
--- a/src/sessions/CodeBeam.UltimateAuth.Sessions.InMemory/CodeBeam.UltimateAuth.Sessions.InMemory.csproj
+++ b/src/sessions/CodeBeam.UltimateAuth.Sessions.InMemory/CodeBeam.UltimateAuth.Sessions.InMemory.csproj
@@ -13,7 +13,7 @@
authentication;sessions;inmemory;auth-framework
- logo.png
+ uauthlogo.png
README.md
@@ -24,7 +24,7 @@
-
+
diff --git a/src/sessions/CodeBeam.UltimateAuth.Sessions.InMemory/logo.png b/src/sessions/CodeBeam.UltimateAuth.Sessions.InMemory/logo.png
deleted file mode 100644
index aa51a469..00000000
Binary files a/src/sessions/CodeBeam.UltimateAuth.Sessions.InMemory/logo.png and /dev/null differ
diff --git a/src/sessions/CodeBeam.UltimateAuth.Sessions.InMemory/uauthlogo.png b/src/sessions/CodeBeam.UltimateAuth.Sessions.InMemory/uauthlogo.png
new file mode 100644
index 00000000..911f2530
Binary files /dev/null and b/src/sessions/CodeBeam.UltimateAuth.Sessions.InMemory/uauthlogo.png differ
diff --git a/src/tokens/CodeBeam.UltimateAuth.Tokens.EntityFrameworkCore/CodeBeam.UltimateAuth.Tokens.EntityFrameworkCore.csproj b/src/tokens/CodeBeam.UltimateAuth.Tokens.EntityFrameworkCore/CodeBeam.UltimateAuth.Tokens.EntityFrameworkCore.csproj
index 18cd4640..6d1c5e19 100644
--- a/src/tokens/CodeBeam.UltimateAuth.Tokens.EntityFrameworkCore/CodeBeam.UltimateAuth.Tokens.EntityFrameworkCore.csproj
+++ b/src/tokens/CodeBeam.UltimateAuth.Tokens.EntityFrameworkCore/CodeBeam.UltimateAuth.Tokens.EntityFrameworkCore.csproj
@@ -14,7 +14,7 @@
authentication;tokens;efcore;jwt;refresh-token;database;auth-framework
README.md
- logo.png
+ uauthlogo.png
@@ -23,7 +23,7 @@
-
+
diff --git a/src/tokens/CodeBeam.UltimateAuth.Tokens.EntityFrameworkCore/logo.png b/src/tokens/CodeBeam.UltimateAuth.Tokens.EntityFrameworkCore/logo.png
deleted file mode 100644
index aa51a469..00000000
Binary files a/src/tokens/CodeBeam.UltimateAuth.Tokens.EntityFrameworkCore/logo.png and /dev/null differ
diff --git a/src/tokens/CodeBeam.UltimateAuth.Tokens.EntityFrameworkCore/uauthlogo.png b/src/tokens/CodeBeam.UltimateAuth.Tokens.EntityFrameworkCore/uauthlogo.png
new file mode 100644
index 00000000..911f2530
Binary files /dev/null and b/src/tokens/CodeBeam.UltimateAuth.Tokens.EntityFrameworkCore/uauthlogo.png differ
diff --git a/src/tokens/CodeBeam.UltimateAuth.Tokens.InMemory/CodeBeam.UltimateAuth.Tokens.InMemory.csproj b/src/tokens/CodeBeam.UltimateAuth.Tokens.InMemory/CodeBeam.UltimateAuth.Tokens.InMemory.csproj
index 90b2b5c8..4be63678 100644
--- a/src/tokens/CodeBeam.UltimateAuth.Tokens.InMemory/CodeBeam.UltimateAuth.Tokens.InMemory.csproj
+++ b/src/tokens/CodeBeam.UltimateAuth.Tokens.InMemory/CodeBeam.UltimateAuth.Tokens.InMemory.csproj
@@ -13,7 +13,7 @@
authentication;tokens;inmemory;jwt;refresh-token;auth-framework
- logo.png
+ uauthlogo.png
README.md
@@ -23,7 +23,7 @@
-
+
diff --git a/src/tokens/CodeBeam.UltimateAuth.Tokens.InMemory/logo.png b/src/tokens/CodeBeam.UltimateAuth.Tokens.InMemory/logo.png
deleted file mode 100644
index aa51a469..00000000
Binary files a/src/tokens/CodeBeam.UltimateAuth.Tokens.InMemory/logo.png and /dev/null differ
diff --git a/src/tokens/CodeBeam.UltimateAuth.Tokens.InMemory/uauthlogo.png b/src/tokens/CodeBeam.UltimateAuth.Tokens.InMemory/uauthlogo.png
new file mode 100644
index 00000000..911f2530
Binary files /dev/null and b/src/tokens/CodeBeam.UltimateAuth.Tokens.InMemory/uauthlogo.png differ
diff --git a/src/users/CodeBeam.UltimateAuth.Users.Contracts/CodeBeam.UltimateAuth.Users.Contracts.csproj b/src/users/CodeBeam.UltimateAuth.Users.Contracts/CodeBeam.UltimateAuth.Users.Contracts.csproj
index c21bbf8c..8ff8aa91 100644
--- a/src/users/CodeBeam.UltimateAuth.Users.Contracts/CodeBeam.UltimateAuth.Users.Contracts.csproj
+++ b/src/users/CodeBeam.UltimateAuth.Users.Contracts/CodeBeam.UltimateAuth.Users.Contracts.csproj
@@ -13,7 +13,7 @@
authentication;identity;users;contracts;shared;dto;auth-framework
- logo.png
+ uauthlogo.png
README.md
@@ -22,7 +22,7 @@
-
+
diff --git a/src/users/CodeBeam.UltimateAuth.Users.Contracts/logo.png b/src/users/CodeBeam.UltimateAuth.Users.Contracts/logo.png
deleted file mode 100644
index aa51a469..00000000
Binary files a/src/users/CodeBeam.UltimateAuth.Users.Contracts/logo.png and /dev/null differ
diff --git a/src/users/CodeBeam.UltimateAuth.Users.Contracts/uauthlogo.png b/src/users/CodeBeam.UltimateAuth.Users.Contracts/uauthlogo.png
new file mode 100644
index 00000000..911f2530
Binary files /dev/null and b/src/users/CodeBeam.UltimateAuth.Users.Contracts/uauthlogo.png differ
diff --git a/src/users/CodeBeam.UltimateAuth.Users.EntityFrameworkCore/CodeBeam.UltimateAuth.Users.EntityFrameworkCore.csproj b/src/users/CodeBeam.UltimateAuth.Users.EntityFrameworkCore/CodeBeam.UltimateAuth.Users.EntityFrameworkCore.csproj
index 6e53ba0c..ab0912de 100644
--- a/src/users/CodeBeam.UltimateAuth.Users.EntityFrameworkCore/CodeBeam.UltimateAuth.Users.EntityFrameworkCore.csproj
+++ b/src/users/CodeBeam.UltimateAuth.Users.EntityFrameworkCore/CodeBeam.UltimateAuth.Users.EntityFrameworkCore.csproj
@@ -12,7 +12,7 @@
authentication;users;efcore;database;sql;auth-framework
- logo.png
+ uauthlogo.png
README.md
@@ -23,7 +23,7 @@
-
+
diff --git a/src/users/CodeBeam.UltimateAuth.Users.EntityFrameworkCore/logo.png b/src/users/CodeBeam.UltimateAuth.Users.EntityFrameworkCore/logo.png
deleted file mode 100644
index aa51a469..00000000
Binary files a/src/users/CodeBeam.UltimateAuth.Users.EntityFrameworkCore/logo.png and /dev/null differ
diff --git a/src/users/CodeBeam.UltimateAuth.Users.EntityFrameworkCore/uauthlogo.png b/src/users/CodeBeam.UltimateAuth.Users.EntityFrameworkCore/uauthlogo.png
new file mode 100644
index 00000000..911f2530
Binary files /dev/null and b/src/users/CodeBeam.UltimateAuth.Users.EntityFrameworkCore/uauthlogo.png differ
diff --git a/src/users/CodeBeam.UltimateAuth.Users.InMemory/CodeBeam.UltimateAuth.Users.InMemory.csproj b/src/users/CodeBeam.UltimateAuth.Users.InMemory/CodeBeam.UltimateAuth.Users.InMemory.csproj
index 85195f3b..0d7ed5fb 100644
--- a/src/users/CodeBeam.UltimateAuth.Users.InMemory/CodeBeam.UltimateAuth.Users.InMemory.csproj
+++ b/src/users/CodeBeam.UltimateAuth.Users.InMemory/CodeBeam.UltimateAuth.Users.InMemory.csproj
@@ -13,7 +13,7 @@
authentication;users;inmemory;testing;auth-framework
- logo.png
+ uauthlogo.png
README.md
@@ -25,7 +25,7 @@
-
+
diff --git a/src/users/CodeBeam.UltimateAuth.Users.InMemory/logo.png b/src/users/CodeBeam.UltimateAuth.Users.InMemory/logo.png
deleted file mode 100644
index aa51a469..00000000
Binary files a/src/users/CodeBeam.UltimateAuth.Users.InMemory/logo.png and /dev/null differ
diff --git a/src/users/CodeBeam.UltimateAuth.Users.InMemory/uauthlogo.png b/src/users/CodeBeam.UltimateAuth.Users.InMemory/uauthlogo.png
new file mode 100644
index 00000000..911f2530
Binary files /dev/null and b/src/users/CodeBeam.UltimateAuth.Users.InMemory/uauthlogo.png differ
diff --git a/src/users/CodeBeam.UltimateAuth.Users.Reference/CodeBeam.UltimateAuth.Users.Reference.csproj b/src/users/CodeBeam.UltimateAuth.Users.Reference/CodeBeam.UltimateAuth.Users.Reference.csproj
index fca9576a..3b4b5061 100644
--- a/src/users/CodeBeam.UltimateAuth.Users.Reference/CodeBeam.UltimateAuth.Users.Reference.csproj
+++ b/src/users/CodeBeam.UltimateAuth.Users.Reference/CodeBeam.UltimateAuth.Users.Reference.csproj
@@ -12,7 +12,7 @@
authentication;identity;users;reference;plugin;auth-framework
- logo.png
+ uauthlogo.png
README.md
@@ -24,7 +24,7 @@
-
+
diff --git a/src/users/CodeBeam.UltimateAuth.Users.Reference/logo.png b/src/users/CodeBeam.UltimateAuth.Users.Reference/logo.png
deleted file mode 100644
index aa51a469..00000000
Binary files a/src/users/CodeBeam.UltimateAuth.Users.Reference/logo.png and /dev/null differ
diff --git a/src/users/CodeBeam.UltimateAuth.Users.Reference/uauthlogo.png b/src/users/CodeBeam.UltimateAuth.Users.Reference/uauthlogo.png
new file mode 100644
index 00000000..911f2530
Binary files /dev/null and b/src/users/CodeBeam.UltimateAuth.Users.Reference/uauthlogo.png differ
diff --git a/src/users/CodeBeam.UltimateAuth.Users/CodeBeam.UltimateAuth.Users.csproj b/src/users/CodeBeam.UltimateAuth.Users/CodeBeam.UltimateAuth.Users.csproj
index 62f2b6a7..9dd91208 100644
--- a/src/users/CodeBeam.UltimateAuth.Users/CodeBeam.UltimateAuth.Users.csproj
+++ b/src/users/CodeBeam.UltimateAuth.Users/CodeBeam.UltimateAuth.Users.csproj
@@ -14,7 +14,7 @@
authentication;identity;users;module;auth-framework
- logo.png
+ uauthlogo.png
README.md
@@ -24,7 +24,7 @@
-
+
diff --git a/src/users/CodeBeam.UltimateAuth.Users/logo.png b/src/users/CodeBeam.UltimateAuth.Users/logo.png
deleted file mode 100644
index aa51a469..00000000
Binary files a/src/users/CodeBeam.UltimateAuth.Users/logo.png and /dev/null differ
diff --git a/src/users/CodeBeam.UltimateAuth.Users/uauthlogo.png b/src/users/CodeBeam.UltimateAuth.Users/uauthlogo.png
new file mode 100644
index 00000000..911f2530
Binary files /dev/null and b/src/users/CodeBeam.UltimateAuth.Users/uauthlogo.png differ
diff --git a/src/utilities/CodeBeam.UltimateAuth.DocsBuilder/CodeBeam.UltimateAuth.DocsBuilder.csproj b/src/utilities/CodeBeam.UltimateAuth.DocsBuilder/CodeBeam.UltimateAuth.DocsBuilder.csproj
new file mode 100644
index 00000000..15919d0a
--- /dev/null
+++ b/src/utilities/CodeBeam.UltimateAuth.DocsBuilder/CodeBeam.UltimateAuth.DocsBuilder.csproj
@@ -0,0 +1,16 @@
+ο»Ώ
+
+
+ Exe
+ net10.0
+ enable
+ enable
+ false
+
+
+
+
+
+
+
+
diff --git a/src/utilities/CodeBeam.UltimateAuth.DocsBuilder/Program.cs b/src/utilities/CodeBeam.UltimateAuth.DocsBuilder/Program.cs
new file mode 100644
index 00000000..0b6de37a
--- /dev/null
+++ b/src/utilities/CodeBeam.UltimateAuth.DocsBuilder/Program.cs
@@ -0,0 +1,285 @@
+ο»Ώusing HtmlAgilityPack;
+using Markdig;
+using System.Text.Json;
+using System.Text.RegularExpressions;
+using System.Xml;
+
+Console.WriteLine("=== DocsBuilder START ===");
+
+var solutionRootDir = TryGetSolutionDir(args) ?? FindSolutionRootFallback();
+
+Console.WriteLine($"SolutionRoot: {solutionRootDir}");
+
+var sourceDocsDir = Path.Combine(solutionRootDir, "docs", "content");
+var groupsOrderPath = Path.Combine(sourceDocsDir, "_groups.json");
+
+Dictionary groupOrders = new(StringComparer.OrdinalIgnoreCase);
+
+if (File.Exists(groupsOrderPath))
+{
+ var groupsOrderJson = await File.ReadAllTextAsync(groupsOrderPath);
+ groupOrders = JsonSerializer.Deserialize>(groupsOrderJson)
+ ?? new Dictionary(StringComparer.OrdinalIgnoreCase);
+}
+
+var websiteDocsOutputDir = Path.Combine(
+ solutionRootDir,
+ "docs",
+ "website",
+ "CodeBeam.UltimateAuth.Docs.Wasm",
+ "CodeBeam.UltimateAuth.Docs.Wasm.Client",
+ "wwwroot",
+ "docs"
+);
+
+if (!Directory.Exists(sourceDocsDir))
+{
+ Console.Error.WriteLine($"Docs source directory not found: {sourceDocsDir}");
+ return;
+}
+
+Directory.CreateDirectory(websiteDocsOutputDir);
+
+var pipeline = new MarkdownPipelineBuilder()
+ .UseAdvancedExtensions()
+ .UseAutoIdentifiers()
+ .Build();
+
+var markdownFiles = Directory.GetFiles(sourceDocsDir, "*.md", SearchOption.AllDirectories);
+
+var allDocs = new List();
+
+foreach (var file in markdownFiles)
+{
+ var relativePath = Path.GetRelativePath(sourceDocsDir, file);
+ var outputRelativePath = Path.ChangeExtension(relativePath, ".json");
+ var outputPath = Path.Combine(websiteDocsOutputDir, outputRelativePath);
+
+ var markdown = await File.ReadAllTextAsync(file);
+
+ var (meta, content) = ParseFrontmatter(markdown);
+
+ var title = meta.TryGetValue("title", out var metaTitle) && !string.IsNullOrWhiteSpace(metaTitle)
+ ? metaTitle
+ : ExtractTitle(content) ?? Path.GetFileNameWithoutExtension(file);
+
+ var order = meta.ContainsKey("order")
+ ? int.Parse(meta["order"])
+ : 999;
+
+ var group = meta.ContainsKey("group")
+ ? meta["group"]
+ : "";
+
+ var slug = NormalizeSlug(relativePath);
+
+ var groupOrder = groupOrders.TryGetValue(group, out var resolvedGroupOrder)
+ ? resolvedGroupOrder
+ : 999;
+
+ allDocs.Add(new DocIndexItem
+ {
+ Title = title,
+ Slug = slug,
+ Order = order,
+ Group = group,
+ GroupOrder = groupOrder
+ });
+
+ var inputLastWrite = File.GetLastWriteTimeUtc(file);
+
+ if (File.Exists(outputPath))
+ {
+ var outputLastWrite = File.GetLastWriteTimeUtc(outputPath);
+
+ if (outputLastWrite >= inputLastWrite)
+ {
+ Console.WriteLine($"β© Skipped: {relativePath}");
+ continue;
+ }
+ }
+
+ Console.WriteLine($"β Processing: {relativePath}");
+
+ var html = Markdown.ToHtml(content, pipeline);
+ html = RemoveFirstH1(html);
+
+ var headings = ExtractHeadingsFromHtml(html);
+
+ html = Regex.Replace(html, "