From 7c1cbb26000539537a6a6811662e4bfd0c13d844 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mehmet=20Can=20Karag=C3=B6z?=
<78308169+mckaragoz@users.noreply.github.com>
Date: Mon, 8 Dec 2025 11:20:24 +0300
Subject: [PATCH 01/50] Update issue templates
---
.github/ISSUE_TEMPLATE/bug_report.md | 38 +++++++++++++++++++++++
.github/ISSUE_TEMPLATE/feature_request.md | 20 ++++++++++++
2 files changed, 58 insertions(+)
create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md
create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 00000000..dd84ea78
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,38 @@
+---
+name: Bug report
+about: Create a report to help us improve
+title: ''
+labels: ''
+assignees: ''
+
+---
+
+**Describe the bug**
+A clear and concise description of what the bug is.
+
+**To Reproduce**
+Steps to reproduce the behavior:
+1. Go to '...'
+2. Click on '....'
+3. Scroll down to '....'
+4. See error
+
+**Expected behavior**
+A clear and concise description of what you expected to happen.
+
+**Screenshots**
+If applicable, add screenshots to help explain your problem.
+
+**Desktop (please complete the following information):**
+ - OS: [e.g. iOS]
+ - Browser [e.g. chrome, safari]
+ - Version [e.g. 22]
+
+**Smartphone (please complete the following information):**
+ - Device: [e.g. iPhone6]
+ - OS: [e.g. iOS8.1]
+ - Browser [e.g. stock browser, safari]
+ - Version [e.g. 22]
+
+**Additional context**
+Add any other context about the problem here.
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
new file mode 100644
index 00000000..bbcbbe7d
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -0,0 +1,20 @@
+---
+name: Feature request
+about: Suggest an idea for this project
+title: ''
+labels: ''
+assignees: ''
+
+---
+
+**Is your feature request related to a problem? Please describe.**
+A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
+
+**Describe the solution you'd like**
+A clear and concise description of what you want to happen.
+
+**Describe alternatives you've considered**
+A clear and concise description of any alternative solutions or features you've considered.
+
+**Additional context**
+Add any other context or screenshots about the feature request here.
From a9d7d08f989a7cbd6d4e181f2fb2190e0d5ac516 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mehmet=20Can=20Karag=C3=B6z?=
<78308169+mckaragoz@users.noreply.github.com>
Date: Mon, 8 Dec 2025 11:24:36 +0300
Subject: [PATCH 02/50] Update bug report template for clarity and structure
---
.github/ISSUE_TEMPLATE/bug_report.md | 57 ++++++++++++++--------------
1 file changed, 29 insertions(+), 28 deletions(-)
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
index dd84ea78..9b9a7537 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -1,38 +1,39 @@
---
-name: Bug report
-about: Create a report to help us improve
-title: ''
-labels: ''
-assignees: ''
-
+name: "🐞 Bug Report"
+about: Report a reproducible issue to help us improve UltimateAuth
+title: "[Bug]: "
+labels: bug
+assignees: ""
---
-**Describe the bug**
-A clear and concise description of what the bug is.
+## 🐞 Bug Description
+A clear and concise description of the issue.
+
+
-**To Reproduce**
-Steps to reproduce the behavior:
+## 📌 Steps to Reproduce
1. Go to '...'
-2. Click on '....'
-3. Scroll down to '....'
-4. See error
+2. Call method '...'
+3. Observe behavior '...'
+
+
+
+## 🧪 Expected Behavior
+What should have happened?
+
+
+## 📷 Screenshots / Logs (if applicable)
+Paste stack traces, console logs, or screenshots.
+
-**Expected behavior**
-A clear and concise description of what you expected to happen.
-**Screenshots**
-If applicable, add screenshots to help explain your problem.
+## 🧩 Environment
+- UltimateAuth version:
+- .NET version:
+- Platform: (Blazor / MAUI / ASP.NET Core / Other)
+- OS:
-**Desktop (please complete the following information):**
- - OS: [e.g. iOS]
- - Browser [e.g. chrome, safari]
- - Version [e.g. 22]
-**Smartphone (please complete the following information):**
- - Device: [e.g. iPhone6]
- - OS: [e.g. iOS8.1]
- - Browser [e.g. stock browser, safari]
- - Version [e.g. 22]
-**Additional context**
-Add any other context about the problem here.
+## ✔ Additional Context
+Add any other relevant context about the problem here.
From 93e6fb65f4f3994ca9ed5a05da610a456119bc2c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mehmet=20Can=20Karag=C3=B6z?=
<78308169+mckaragoz@users.noreply.github.com>
Date: Mon, 8 Dec 2025 11:26:09 +0300
Subject: [PATCH 03/50] Revise feature request template for clarity and
structure
Updated the feature request template to include new sections and improved formatting.
---
.github/ISSUE_TEMPLATE/feature_request.md | 39 +++++++++++++++--------
1 file changed, 26 insertions(+), 13 deletions(-)
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
index bbcbbe7d..7a4cdb6c 100644
--- a/.github/ISSUE_TEMPLATE/feature_request.md
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -1,20 +1,33 @@
---
-name: Feature request
-about: Suggest an idea for this project
-title: ''
-labels: ''
-assignees: ''
+name: "✨ Feature Request"
+about: Suggest a new idea or enhancement for UltimateAuth
+title: "[Feature]: "
+labels: enhancement
+assignees: ""
+---
+
+## ✨ Feature Description
+Describe the feature you'd like to see.
+
+---
+
+## 💡 Why Is This Needed?
+Explain the problem this feature solves or the value it provides.
---
-**Is your feature request related to a problem? Please describe.**
-A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
+## 🛠 Suggested Implementation
+If you have any implementation ideas, describe them:
+- Proposed API shape
+- Example usage
+- Integration points
+
+---
-**Describe the solution you'd like**
-A clear and concise description of what you want to happen.
+## 🔗 Related Issues / Discussions
+(Optional) Link any related issues or design proposals.
-**Describe alternatives you've considered**
-A clear and concise description of any alternative solutions or features you've considered.
+---
-**Additional context**
-Add any other context or screenshots about the feature request here.
+## ✔ Additional Notes
+Anything else we should know?
From 76c81272b6d35b48079f7816aa0586401115d689 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mehmet=20Can=20Karag=C3=B6z?=
<78308169+mckaragoz@users.noreply.github.com>
Date: Mon, 8 Dec 2025 11:27:17 +0300
Subject: [PATCH 04/50] Add design proposal issue template
---
.github/ISSUE_TEMPLATE/design_proposal.md | 45 +++++++++++++++++++++++
1 file changed, 45 insertions(+)
create mode 100644 .github/ISSUE_TEMPLATE/design_proposal.md
diff --git a/.github/ISSUE_TEMPLATE/design_proposal.md b/.github/ISSUE_TEMPLATE/design_proposal.md
new file mode 100644
index 00000000..128f0df2
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/design_proposal.md
@@ -0,0 +1,45 @@
+---
+name: "🧠 Design Proposal"
+about: Propose a high-level design for a new component, flow, or architectural change
+title: "[Design]: "
+labels: design
+assignees: ""
+---
+
+## 🧠 Summary
+Briefly describe the concept or design you're proposing.
+
+---
+
+## 🎯 Goals
+What problem does this design solve?
+What are the objectives?
+
+---
+
+## 🧩 Proposed Architecture
+Describe how this feature or change should work:
+- Components involved
+- Flow diagrams (optional)
+- Key abstractions
+- Input/output expectations
+
+---
+
+## 🧪 Alternatives Considered
+List alternative designs and why they were not chosen.
+
+---
+
+## 🔥 Risks / Trade-offs
+Any potential drawbacks or complexities?
+
+---
+
+## 🔗 Related Issues
+(Optional) Provide related bugs, features, or discussions.
+
+---
+
+## ✔ Additional Notes
+Add anything else that may help the maintainers evaluate this proposal.
From eca445de4d5ce9afbd78f3ca156cda424eeb4941 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mehmet=20Can=20Karag=C3=B6z?=
<78308169+mckaragoz@users.noreply.github.com>
Date: Mon, 8 Dec 2025 11:38:42 +0300
Subject: [PATCH 05/50] Update GitHub Sponsors username in FUNDING.yml
---
.github/FUNDING.yml | 15 +++++++++++++++
1 file changed, 15 insertions(+)
create mode 100644 .github/FUNDING.yml
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 00000000..6e4c378a
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1,15 @@
+# These are supported funding model platforms
+
+github: CodeBeamOrg
+patreon: # Replace with a single Patreon username
+open_collective: # Replace with a single Open Collective username
+ko_fi: # Replace with a single Ko-fi username
+tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
+community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
+liberapay: # Replace with a single Liberapay username
+issuehunt: # Replace with a single IssueHunt username
+lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
+polar: # Replace with a single Polar username
+buy_me_a_coffee: # Replace with a single Buy Me a Coffee username
+thanks_dev: # Replace with a single thanks.dev username
+custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
From 4e12f049e139ddf5d6c029d70fc866371a90d2b8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mehmet=20Can=20Karag=C3=B6z?=
<78308169+mckaragoz@users.noreply.github.com>
Date: Mon, 8 Dec 2025 11:59:41 +0300
Subject: [PATCH 06/50] Create pull request template for contributions
Added a pull request template to guide contributors.
---
.github/PULL_REQUEST_TEMPLATE.md | 37 ++++++++++++++++++++++++++++++++
1 file changed, 37 insertions(+)
create mode 100644 .github/PULL_REQUEST_TEMPLATE.md
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 00000000..37f752d9
--- /dev/null
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,37 @@
+# 🚀 Pull Request
+
+Thank you for contributing to **UltimateAuth**!
+Please complete the following checklist to help us review your PR effectively.
+
+---
+
+## 📘 Summary
+Describe what this PR does and why it’s needed.
+
+---
+
+## 🔍 Details
+Explain any important implementation details, design decisions, or considerations.
+
+---
+
+## 🧩 Related Issues
+Link any related issues:
+
+
+---
+
+## 🛠 Changes
+- [ ] New feature
+- [ ] Bug fix
+- [ ] Documentation
+- [ ] Refactoring
+- [ ] Breaking change
+
+---
+
+## ✔ Checklist
+- [ ] I’ve tested my changes
+- [ ] I’ve added comments or documentation where needed
+- [ ] I’ve followed the project’s coding conventions
+- [ ] I’ve validated that the API surface is stable
From 70bebb681e042bfbd0d872bc0a0c08f3768a8756 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mehmet=20Can=20Karag=C3=B6z?=
<78308169+mckaragoz@users.noreply.github.com>
Date: Thu, 11 Dec 2025 00:10:22 +0300
Subject: [PATCH 07/50] First Implementation of Core Project (#2)
* First Implementation of Core Project
* Extensions & Options & Validators
* Errors & Utilities & Multi Tenancy Support & Events
* Fixes
* Add XML Definitions
---
.../Abstractions/.gitkeep | 1 -
.../Abstractions/IUser.cs | 21 +++
.../Abstractions/IUserIdConverter.cs | 45 +++++
.../Abstractions/IUserIdConverterResolver.cs | 23 +++
.../Abstractions/Services/ISessionService.cs | 93 ++++++++++
.../Stores/DefaultSessionStoreFactory.cs | 21 +++
.../Abstractions/Stores/ISessionStore.cs | 140 +++++++++++++++
.../Stores/ISessionStoreFactory.cs | 25 +++
.../Abstractions/Stores/IUserStore.cs | 58 ++++++
.../Abstractions/Stores/IUserStoreFactory.cs | 26 +++
.../CodeBeam.UltimateAuth.Core.csproj | 6 +
.../Domain/Session/AuthSessionId.cs | 61 +++++++
.../Domain/Session/ChainId.cs | 62 +++++++
.../Domain/Session/DeviceInfo.cs | 60 +++++++
.../Domain/Session/ISession.cs | 73 ++++++++
.../Domain/Session/ISessionChain.cs | 59 ++++++
.../Domain/Session/ISessionRoot.cs | 54 ++++++
.../Domain/Session/SessionMetadata.cs | 40 +++++
.../Domain/Session/SessionState.cs | 44 +++++
.../Errors/Base/UAuthDeveloperException.cs | 18 ++
.../Errors/Base/UAuthDomainException.cs | 16 ++
.../Errors/Base/UAuthException.cs | 25 +++
.../Errors/Base/UAuthSessionException.cs | 29 +++
.../Errors/Developer/UAuthConfigException.cs | 18 ++
.../Developer/UAuthInternalException.cs | 20 +++
.../Errors/Developer/UAuthStoreException.cs | 18 ++
.../Errors/UAuthDeviceLimitException.cs | 25 +++
.../UAuthInvalidCredentialsException.cs | 18 ++
.../Errors/UAuthInvalidPkceCodeException.cs | 20 +++
.../Errors/UAuthRootRevokedException.cs | 20 +++
.../UAuthSecurityVersionMismatchException.cs | 37 ++++
.../Errors/UAuthSessionExpiredException.cs | 26 +++
.../Errors/UAuthSessionNotActiveException.cs | 25 +++
.../Errors/UAuthSessionRevokedException.cs | 27 +++
.../Errors/UAuthTokenTamperedException.cs | 26 +++
.../Events/IAuthEventContext.cs | 7 +
.../Events/SessionCreatedContext.cs | 48 +++++
.../Events/SessionRefreshedContext.cs | 60 +++++++
.../Events/SessionRevokedContext.cs | 57 ++++++
.../Events/UAuthEventDispatcher.cs | 53 ++++++
.../Events/UAuthEvents.cs | 57 ++++++
.../Events/UserLoggedInContext.cs | 42 +++++
.../Events/UserLoggedOutContext.cs | 41 +++++
.../Extensions/.gitkeep | 1 -
...UltimateAuthServiceCollectionExtensions.cs | 93 ++++++++++
.../UltimateAuthSessionStoreExtensions.cs | 102 +++++++++++
.../UserIdConverterRegistrationExtensions.cs | 64 +++++++
.../Internal/UAuthSession.cs | 71 ++++++++
.../Internal/UAuthSessionChain.cs | 76 ++++++++
.../Internal/UAuthSessionRoot.cs | 95 ++++++++++
.../Internal/UAuthSessionService.cs | 170 ++++++++++++++++++
.../Models/.gitkeep | 1 -
.../Models/SessionResult.cs | 40 +++++
.../Models/SessionValidationResult.cs | 35 ++++
.../MultiTenancy/CompositeTenantResolver.cs | 37 ++++
.../MultiTenancy/FixedTenantResolver.cs | 27 +++
.../MultiTenancy/HeaderTenantResolver.cs | 38 ++++
.../MultiTenancy/HostTenantResolver.cs | 34 ++++
.../MultiTenancy/ITenantResolver.cs | 16 ++
.../MultiTenancy/PathTenantResolver.cs | 40 +++++
.../MultiTenancy/TenantResolutionContext.cs | 37 ++++
.../Options/.gitkeep | 1 -
.../Options/LoginOptions.cs | 21 +++
.../Options/MultiTenantOptions.cs | 59 ++++++
.../Options/MultiTenantOptionsValidator.cs | 85 +++++++++
.../Options/PkceOptions.cs | 17 ++
.../Options/PkceOptionsValidator.cs | 21 +++
.../Options/SessionOptions.cs | 80 +++++++++
.../Options/SessionOptionsValidator.cs | 100 +++++++++++
.../Options/TokenOptions.cs | 57 ++++++
.../Options/TokenOptionsValidator.cs | 52 ++++++
.../Options/UltimateAuthOptions.cs | 58 ++++++
.../Options/UltimateAuthOptionsValidator.cs | 44 +++++
.../Security/.gitkeep | 1 -
.../Utilities/Base64Url.cs | 49 +++++
.../Utilities/RandomIdGenerator.cs | 54 ++++++
.../Utilities/UAuthUserIdConverter.cs | 83 +++++++++
.../Utilities/UAuthUserIdConverterResolver.cs | 47 +++++
78 files changed, 3446 insertions(+), 5 deletions(-)
delete mode 100644 src/CodeBeam.UltimateAuth.Core/Abstractions/.gitkeep
create mode 100644 src/CodeBeam.UltimateAuth.Core/Abstractions/IUser.cs
create mode 100644 src/CodeBeam.UltimateAuth.Core/Abstractions/IUserIdConverter.cs
create mode 100644 src/CodeBeam.UltimateAuth.Core/Abstractions/IUserIdConverterResolver.cs
create mode 100644 src/CodeBeam.UltimateAuth.Core/Abstractions/Services/ISessionService.cs
create mode 100644 src/CodeBeam.UltimateAuth.Core/Abstractions/Stores/DefaultSessionStoreFactory.cs
create mode 100644 src/CodeBeam.UltimateAuth.Core/Abstractions/Stores/ISessionStore.cs
create mode 100644 src/CodeBeam.UltimateAuth.Core/Abstractions/Stores/ISessionStoreFactory.cs
create mode 100644 src/CodeBeam.UltimateAuth.Core/Abstractions/Stores/IUserStore.cs
create mode 100644 src/CodeBeam.UltimateAuth.Core/Abstractions/Stores/IUserStoreFactory.cs
create mode 100644 src/CodeBeam.UltimateAuth.Core/Domain/Session/AuthSessionId.cs
create mode 100644 src/CodeBeam.UltimateAuth.Core/Domain/Session/ChainId.cs
create mode 100644 src/CodeBeam.UltimateAuth.Core/Domain/Session/DeviceInfo.cs
create mode 100644 src/CodeBeam.UltimateAuth.Core/Domain/Session/ISession.cs
create mode 100644 src/CodeBeam.UltimateAuth.Core/Domain/Session/ISessionChain.cs
create mode 100644 src/CodeBeam.UltimateAuth.Core/Domain/Session/ISessionRoot.cs
create mode 100644 src/CodeBeam.UltimateAuth.Core/Domain/Session/SessionMetadata.cs
create mode 100644 src/CodeBeam.UltimateAuth.Core/Domain/Session/SessionState.cs
create mode 100644 src/CodeBeam.UltimateAuth.Core/Errors/Base/UAuthDeveloperException.cs
create mode 100644 src/CodeBeam.UltimateAuth.Core/Errors/Base/UAuthDomainException.cs
create mode 100644 src/CodeBeam.UltimateAuth.Core/Errors/Base/UAuthException.cs
create mode 100644 src/CodeBeam.UltimateAuth.Core/Errors/Base/UAuthSessionException.cs
create mode 100644 src/CodeBeam.UltimateAuth.Core/Errors/Developer/UAuthConfigException.cs
create mode 100644 src/CodeBeam.UltimateAuth.Core/Errors/Developer/UAuthInternalException.cs
create mode 100644 src/CodeBeam.UltimateAuth.Core/Errors/Developer/UAuthStoreException.cs
create mode 100644 src/CodeBeam.UltimateAuth.Core/Errors/UAuthDeviceLimitException.cs
create mode 100644 src/CodeBeam.UltimateAuth.Core/Errors/UAuthInvalidCredentialsException.cs
create mode 100644 src/CodeBeam.UltimateAuth.Core/Errors/UAuthInvalidPkceCodeException.cs
create mode 100644 src/CodeBeam.UltimateAuth.Core/Errors/UAuthRootRevokedException.cs
create mode 100644 src/CodeBeam.UltimateAuth.Core/Errors/UAuthSecurityVersionMismatchException.cs
create mode 100644 src/CodeBeam.UltimateAuth.Core/Errors/UAuthSessionExpiredException.cs
create mode 100644 src/CodeBeam.UltimateAuth.Core/Errors/UAuthSessionNotActiveException.cs
create mode 100644 src/CodeBeam.UltimateAuth.Core/Errors/UAuthSessionRevokedException.cs
create mode 100644 src/CodeBeam.UltimateAuth.Core/Errors/UAuthTokenTamperedException.cs
create mode 100644 src/CodeBeam.UltimateAuth.Core/Events/IAuthEventContext.cs
create mode 100644 src/CodeBeam.UltimateAuth.Core/Events/SessionCreatedContext.cs
create mode 100644 src/CodeBeam.UltimateAuth.Core/Events/SessionRefreshedContext.cs
create mode 100644 src/CodeBeam.UltimateAuth.Core/Events/SessionRevokedContext.cs
create mode 100644 src/CodeBeam.UltimateAuth.Core/Events/UAuthEventDispatcher.cs
create mode 100644 src/CodeBeam.UltimateAuth.Core/Events/UAuthEvents.cs
create mode 100644 src/CodeBeam.UltimateAuth.Core/Events/UserLoggedInContext.cs
create mode 100644 src/CodeBeam.UltimateAuth.Core/Events/UserLoggedOutContext.cs
delete mode 100644 src/CodeBeam.UltimateAuth.Core/Extensions/.gitkeep
create mode 100644 src/CodeBeam.UltimateAuth.Core/Extensions/UltimateAuthServiceCollectionExtensions.cs
create mode 100644 src/CodeBeam.UltimateAuth.Core/Extensions/UltimateAuthSessionStoreExtensions.cs
create mode 100644 src/CodeBeam.UltimateAuth.Core/Extensions/UserIdConverterRegistrationExtensions.cs
create mode 100644 src/CodeBeam.UltimateAuth.Core/Internal/UAuthSession.cs
create mode 100644 src/CodeBeam.UltimateAuth.Core/Internal/UAuthSessionChain.cs
create mode 100644 src/CodeBeam.UltimateAuth.Core/Internal/UAuthSessionRoot.cs
create mode 100644 src/CodeBeam.UltimateAuth.Core/Internal/UAuthSessionService.cs
delete mode 100644 src/CodeBeam.UltimateAuth.Core/Models/.gitkeep
create mode 100644 src/CodeBeam.UltimateAuth.Core/Models/SessionResult.cs
create mode 100644 src/CodeBeam.UltimateAuth.Core/Models/SessionValidationResult.cs
create mode 100644 src/CodeBeam.UltimateAuth.Core/MultiTenancy/CompositeTenantResolver.cs
create mode 100644 src/CodeBeam.UltimateAuth.Core/MultiTenancy/FixedTenantResolver.cs
create mode 100644 src/CodeBeam.UltimateAuth.Core/MultiTenancy/HeaderTenantResolver.cs
create mode 100644 src/CodeBeam.UltimateAuth.Core/MultiTenancy/HostTenantResolver.cs
create mode 100644 src/CodeBeam.UltimateAuth.Core/MultiTenancy/ITenantResolver.cs
create mode 100644 src/CodeBeam.UltimateAuth.Core/MultiTenancy/PathTenantResolver.cs
create mode 100644 src/CodeBeam.UltimateAuth.Core/MultiTenancy/TenantResolutionContext.cs
delete mode 100644 src/CodeBeam.UltimateAuth.Core/Options/.gitkeep
create mode 100644 src/CodeBeam.UltimateAuth.Core/Options/LoginOptions.cs
create mode 100644 src/CodeBeam.UltimateAuth.Core/Options/MultiTenantOptions.cs
create mode 100644 src/CodeBeam.UltimateAuth.Core/Options/MultiTenantOptionsValidator.cs
create mode 100644 src/CodeBeam.UltimateAuth.Core/Options/PkceOptions.cs
create mode 100644 src/CodeBeam.UltimateAuth.Core/Options/PkceOptionsValidator.cs
create mode 100644 src/CodeBeam.UltimateAuth.Core/Options/SessionOptions.cs
create mode 100644 src/CodeBeam.UltimateAuth.Core/Options/SessionOptionsValidator.cs
create mode 100644 src/CodeBeam.UltimateAuth.Core/Options/TokenOptions.cs
create mode 100644 src/CodeBeam.UltimateAuth.Core/Options/TokenOptionsValidator.cs
create mode 100644 src/CodeBeam.UltimateAuth.Core/Options/UltimateAuthOptions.cs
create mode 100644 src/CodeBeam.UltimateAuth.Core/Options/UltimateAuthOptionsValidator.cs
delete mode 100644 src/CodeBeam.UltimateAuth.Core/Security/.gitkeep
create mode 100644 src/CodeBeam.UltimateAuth.Core/Utilities/Base64Url.cs
create mode 100644 src/CodeBeam.UltimateAuth.Core/Utilities/RandomIdGenerator.cs
create mode 100644 src/CodeBeam.UltimateAuth.Core/Utilities/UAuthUserIdConverter.cs
create mode 100644 src/CodeBeam.UltimateAuth.Core/Utilities/UAuthUserIdConverterResolver.cs
diff --git a/src/CodeBeam.UltimateAuth.Core/Abstractions/.gitkeep b/src/CodeBeam.UltimateAuth.Core/Abstractions/.gitkeep
deleted file mode 100644
index 5f282702..00000000
--- a/src/CodeBeam.UltimateAuth.Core/Abstractions/.gitkeep
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/src/CodeBeam.UltimateAuth.Core/Abstractions/IUser.cs b/src/CodeBeam.UltimateAuth.Core/Abstractions/IUser.cs
new file mode 100644
index 00000000..b3fe9709
--- /dev/null
+++ b/src/CodeBeam.UltimateAuth.Core/Abstractions/IUser.cs
@@ -0,0 +1,21 @@
+namespace CodeBeam.UltimateAuth.Core.Abstractions
+{
+ ///
+ /// Represents the minimal user abstraction required by UltimateAuth.
+ /// Includes the unique user identifier and an optional set of claims that
+ /// may be used during authentication or session creation.
+ ///
+ public interface IUser
+ {
+ ///
+ /// Gets the unique identifier of the user.
+ ///
+ TUserId UserId { get; }
+
+ ///
+ /// Gets an optional collection of user claims that may be used to construct
+ /// session-level claim snapshots. Implementations may return null if no claims are available.
+ ///
+ IReadOnlyDictionary? Claims { get; }
+ }
+}
diff --git a/src/CodeBeam.UltimateAuth.Core/Abstractions/IUserIdConverter.cs b/src/CodeBeam.UltimateAuth.Core/Abstractions/IUserIdConverter.cs
new file mode 100644
index 00000000..a52f3ec9
--- /dev/null
+++ b/src/CodeBeam.UltimateAuth.Core/Abstractions/IUserIdConverter.cs
@@ -0,0 +1,45 @@
+namespace CodeBeam.UltimateAuth.Core.Abstractions
+{
+ ///
+ /// Defines conversion logic for transforming user identifiers between
+ /// strongly typed values, string representations, and binary formats.
+ /// Implementations enable consistent storage, token serialization,
+ /// and multitenant key partitioning.
+ ///
+ public interface IUserIdConverter
+ {
+ ///
+ /// Converts the typed user identifier into its canonical string representation.
+ ///
+ /// The user identifier to convert.
+ /// A stable and reversible string representation of the identifier.
+ string ToString(TUserId id);
+
+ ///
+ /// Converts the typed user identifier into a binary representation suitable for efficient storage or hashing operations.
+ ///
+ /// The user identifier to convert.
+ /// A byte array representing the identifier.
+ byte[] ToBytes(TUserId id);
+
+ ///
+ /// Reconstructs a typed user identifier from its string representation.
+ ///
+ /// The string-encoded identifier.
+ /// The reconstructed user identifier.
+ ///
+ /// Thrown when the input value cannot be parsed into a valid identifier.
+ ///
+ TUserId FromString(string value);
+
+ ///
+ /// Reconstructs a typed user identifier from its binary representation.
+ ///
+ /// The byte array containing the encoded identifier.
+ /// The reconstructed user identifier.
+ ///
+ /// Thrown when the input binary value cannot be parsed into a valid identifier.
+ ///
+ TUserId FromBytes(byte[] binary);
+ }
+}
diff --git a/src/CodeBeam.UltimateAuth.Core/Abstractions/IUserIdConverterResolver.cs b/src/CodeBeam.UltimateAuth.Core/Abstractions/IUserIdConverterResolver.cs
new file mode 100644
index 00000000..6258bb6f
--- /dev/null
+++ b/src/CodeBeam.UltimateAuth.Core/Abstractions/IUserIdConverterResolver.cs
@@ -0,0 +1,23 @@
+namespace CodeBeam.UltimateAuth.Core.Abstractions
+{
+ ///
+ /// Resolves the appropriate instance
+ /// for a given user identifier type. Used internally by UltimateAuth to
+ /// ensure consistent serialization and parsing of user IDs across all components.
+ ///
+ public interface IUserIdConverterResolver
+ {
+ ///
+ /// Retrieves the registered for the specified user ID type.
+ ///
+ /// The type of the user identifier.
+ ///
+ /// A converter capable of transforming the user ID to and from its string
+ /// and binary representations.
+ ///
+ ///
+ /// Thrown if no converter has been registered for the requested user ID type.
+ ///
+ IUserIdConverter GetConverter();
+ }
+}
diff --git a/src/CodeBeam.UltimateAuth.Core/Abstractions/Services/ISessionService.cs b/src/CodeBeam.UltimateAuth.Core/Abstractions/Services/ISessionService.cs
new file mode 100644
index 00000000..c9ba2157
--- /dev/null
+++ b/src/CodeBeam.UltimateAuth.Core/Abstractions/Services/ISessionService.cs
@@ -0,0 +1,93 @@
+using CodeBeam.UltimateAuth.Core.Domain;
+using CodeBeam.UltimateAuth.Core.Models;
+
+namespace CodeBeam.UltimateAuth.Core.Abstractions
+{
+ ///
+ /// Provides high-level session lifecycle operations such as creation, refresh, validation, and revocation.
+ ///
+ /// The type used to uniquely identify the user.
+ public interface ISessionService
+ {
+ ///
+ /// Creates a new login session for the specified user.
+ ///
+ ///
+ /// The tenant identifier. Use null for single-tenant applications.
+ ///
+ /// The user associated with the session.
+ /// Information about the device initiating the session.
+ /// Optional metadata describing the session context.
+ /// The current UTC timestamp.
+ ///
+ /// A result containing the newly created session, chain, and session root.
+ ///
+ Task> CreateLoginSessionAsync(string? tenantId, TUserId userId, DeviceInfo deviceInfo, SessionMetadata? metadata, DateTime now);
+
+ ///
+ /// Rotates the specified session and issues a new one while preserving the session chain.
+ ///
+ /// The tenant identifier, or null.
+ /// The active session identifier to be refreshed.
+ /// The current UTC timestamp.
+ ///
+ /// A result containing the refreshed session and updated chain.
+ ///
+ ///
+ /// Thrown if the session, its chain, or the user's session root is invalid.
+ ///
+ Task> RefreshSessionAsync(string? tenantId, AuthSessionId currentSessionId,DateTime now);
+
+ ///
+ /// Revokes a single session, preventing further use.
+ ///
+ /// The tenant identifier, or null.
+ /// The session identifier to revoke.
+ /// The UTC timestamp of the revocation.
+ Task RevokeSessionAsync(string? tenantId, AuthSessionId sessionId, DateTime at);
+
+ ///
+ /// Revokes an entire session chain (device-level logout).
+ ///
+ /// The tenant identifier, or null.
+ /// The session chain identifier to revoke.
+ /// The UTC timestamp of the revocation.
+ Task RevokeChainAsync(string? tenantId, ChainId chainId, DateTime at);
+
+ ///
+ /// Revokes the user's session root, invalidating all existing sessions across all chains.
+ ///
+ /// The tenant identifier, or null.
+ /// The user whose root should be revoked.
+ /// The UTC timestamp of the revocation.
+ Task RevokeRootAsync(string? tenantId, TUserId userId, DateTime at);
+
+ ///
+ /// Validates a session and evaluates its current state, including expiration, revocation, and security version alignment.
+ ///
+ /// The tenant identifier, or null.
+ /// The session identifier to validate.
+ /// The current UTC timestamp.
+ ///
+ /// A detailed validation result describing the session, chain, root,
+ /// and computed session state.
+ ///
+ Task> ValidateSessionAsync(string? tenantId, AuthSessionId sessionId, DateTime now);
+
+ ///
+ /// Retrieves all session chains belonging to the specified user.
+ ///
+ /// The tenant identifier, or null.
+ /// The user whose session chains are requested.
+ /// A read-only list of session chains.
+ Task>> GetChainsAsync(string? tenantId, TUserId userId);
+
+ ///
+ /// Retrieves all sessions belonging to a specific session chain.
+ ///
+ /// The tenant identifier, or null.
+ /// The session chain identifier.
+ /// A read-only list of sessions contained within the chain.
+ Task>> GetSessionsAsync(string? tenantId, ChainId chainId);
+ }
+}
diff --git a/src/CodeBeam.UltimateAuth.Core/Abstractions/Stores/DefaultSessionStoreFactory.cs b/src/CodeBeam.UltimateAuth.Core/Abstractions/Stores/DefaultSessionStoreFactory.cs
new file mode 100644
index 00000000..8ff089de
--- /dev/null
+++ b/src/CodeBeam.UltimateAuth.Core/Abstractions/Stores/DefaultSessionStoreFactory.cs
@@ -0,0 +1,21 @@
+namespace CodeBeam.UltimateAuth.Core.Abstractions
+{
+ ///
+ /// Default session store factory that throws until a real store implementation is registered.
+ ///
+ public sealed class DefaultSessionStoreFactory : ISessionStoreFactory
+ {
+ /// Creates a session store instance for the given user ID type, but always throws because no store has been registered.
+ /// The tenant identifier, or null in single-tenant mode.
+ /// The type used to uniquely identify the user.
+ /// Never returns; always throws.
+ /// Thrown when no session store implementation has been configured.
+ public ISessionStore Create(string? tenantId)
+ {
+ throw new InvalidOperationException(
+ "No ISessionStore implementation registered. " +
+ "Call AddUltimateAuthServer().AddSessionStore() to provide a real implementation."
+ );
+ }
+ }
+}
diff --git a/src/CodeBeam.UltimateAuth.Core/Abstractions/Stores/ISessionStore.cs b/src/CodeBeam.UltimateAuth.Core/Abstractions/Stores/ISessionStore.cs
new file mode 100644
index 00000000..3729d719
--- /dev/null
+++ b/src/CodeBeam.UltimateAuth.Core/Abstractions/Stores/ISessionStore.cs
@@ -0,0 +1,140 @@
+using CodeBeam.UltimateAuth.Core.Domain;
+
+namespace CodeBeam.UltimateAuth.Core.Abstractions
+{
+ ///
+ /// Defines the low-level persistence operations for sessions, session chains, and session roots in a multi-tenant or single-tenant environment.
+ /// Store implementations provide durable and atomic data access.
+ ///
+ public interface ISessionStore
+ {
+ ///
+ /// Retrieves a session by its identifier within the given tenant context.
+ ///
+ /// The tenant identifier, or null for single-tenant mode.
+ /// The session identifier.
+ /// The session instance or null if not found.
+ Task?> GetSessionAsync(string? tenantId, AuthSessionId sessionId);
+
+ ///
+ /// Persists a new session or updates an existing one within the tenant scope.
+ /// Implementations must ensure atomic writes.
+ ///
+ /// The tenant identifier, or null.
+ /// The session to persist.
+ Task SaveSessionAsync(string? tenantId, ISession session);
+
+ ///
+ /// Marks the specified session as revoked, preventing future authentication.
+ /// Revocation timestamp must be stored reliably.
+ ///
+ /// The tenant identifier, or null.
+ /// The session identifier.
+ /// The UTC timestamp of revocation.
+ Task RevokeSessionAsync(string? tenantId, AuthSessionId sessionId, DateTime at);
+
+ ///
+ /// Returns all sessions belonging to the specified chain, ordered according to store implementation rules.
+ ///
+ /// The tenant identifier, or null.
+ /// The chain identifier.
+ /// A read-only list of sessions.
+ Task>> GetSessionsByChainAsync(string? tenantId, ChainId chainId);
+
+ ///
+ /// Retrieves a session chain by identifier. Returns null if the chain does not exist in the provided tenant context.
+ ///
+ /// The tenant identifier, or null.
+ /// The chain identifier.
+ /// The chain or null.
+ Task?> GetChainAsync(string? tenantId, ChainId chainId);
+
+ ///
+ /// Inserts a new session chain into the store. Implementations must ensure consistency with the related sessions and session root.
+ ///
+ /// The tenant identifier, or null.
+ /// The chain to save.
+ Task SaveChainAsync(string? tenantId, ISessionChain chain);
+
+ ///
+ /// Updates an existing session chain, typically after session rotation or revocation. Implementations must preserve atomicity.
+ ///
+ /// The tenant identifier, or null.
+ /// The updated session chain.
+ Task UpdateChainAsync(string? tenantId, ISessionChain chain);
+
+ ///
+ /// Marks the entire session chain as revoked, invalidating all associated sessions for the device or app family.
+ ///
+ /// The tenant identifier, or null.
+ /// The chain to revoke.
+ /// The UTC timestamp of revocation.
+ Task RevokeChainAsync(string? tenantId, ChainId chainId, DateTime at);
+
+ ///
+ /// Retrieves the active session identifier for the specified chain.
+ /// This is typically an O(1) lookup and used for session rotation.
+ ///
+ /// The tenant identifier, or null.
+ /// The chain whose active session is requested.
+ /// The active session identifier or null.
+ Task GetActiveSessionIdAsync(string? tenantId, ChainId chainId);
+
+ ///
+ /// Sets or replaces the active session identifier for the specified chain.
+ /// Must be atomic to prevent race conditions during refresh.
+ ///
+ /// The tenant identifier, or null.
+ /// The chain whose active session is being set.
+ /// The new active session identifier.
+ Task SetActiveSessionIdAsync(string? tenantId, ChainId chainId, AuthSessionId sessionId);
+
+ ///
+ /// Retrieves all session chains belonging to the specified user within the tenant scope.
+ ///
+ /// The tenant identifier, or null.
+ /// The user whose chains are being retrieved.
+ /// A read-only list of session chains.
+ Task>> GetChainsByUserAsync(string? tenantId, TUserId userId);
+
+ ///
+ /// Retrieves the session root for the user, which represents the full set of chains and their associated security metadata.
+ /// Returns null if the root does not exist.
+ ///
+ /// The tenant identifier, or null.
+ /// The user identifier.
+ /// The session root or null.
+ Task?> GetSessionRootAsync(string? tenantId, TUserId userId);
+
+ ///
+ /// Persists a session root structure, usually after chain creation, rotation, or security operations.
+ ///
+ /// The tenant identifier, or null.
+ /// The session root to save.
+ Task SaveSessionRootAsync(string? tenantId, ISessionRoot root);
+
+ ///
+ /// Revokes the session root, invalidating all chains and sessions belonging to the specified user in the tenant scope.
+ ///
+ /// The tenant identifier, or null.
+ /// The user whose root should be revoked.
+ /// The UTC timestamp of revocation.
+ Task RevokeSessionRootAsync(string? tenantId, TUserId userId, DateTime at);
+
+ ///
+ /// Removes expired sessions from the store while leaving chains and session roots intact. Cleanup strategy is determined by the store implementation.
+ ///
+ /// The tenant identifier, or null.
+ /// The current UTC timestamp.
+ Task DeleteExpiredSessionsAsync(string? tenantId, DateTime now);
+
+ ///
+ /// Retrieves the chain identifier associated with the specified session.
+ ///
+ /// The tenant identifier, or null.
+ /// The session identifier.
+ /// The chain identifier or null.
+ Task GetChainIdBySessionAsync(string? tenantId, AuthSessionId sessionId);
+ }
+
+}
diff --git a/src/CodeBeam.UltimateAuth.Core/Abstractions/Stores/ISessionStoreFactory.cs b/src/CodeBeam.UltimateAuth.Core/Abstractions/Stores/ISessionStoreFactory.cs
new file mode 100644
index 00000000..0a18fe12
--- /dev/null
+++ b/src/CodeBeam.UltimateAuth.Core/Abstractions/Stores/ISessionStoreFactory.cs
@@ -0,0 +1,25 @@
+namespace CodeBeam.UltimateAuth.Core.Abstractions
+{
+ ///
+ /// Provides a factory abstraction for creating tenant-scoped session store
+ /// instances capable of persisting sessions, chains, and session roots.
+ /// Implementations typically resolve concrete types from the dependency injection container.
+ ///
+ public interface ISessionStoreFactory
+ {
+ ///
+ /// Creates and returns a session store instance for the specified user ID type within the given tenant context.
+ ///
+ /// The type used to uniquely identify users.
+ ///
+ /// The tenant identifier for multi-tenant environments, or null for single-tenant mode.
+ ///
+ ///
+ /// An implementation able to perform session persistence operations.
+ ///
+ ///
+ /// Thrown if no compatible session store implementation is registered.
+ ///
+ ISessionStore Create(string? tenantId);
+ }
+}
diff --git a/src/CodeBeam.UltimateAuth.Core/Abstractions/Stores/IUserStore.cs b/src/CodeBeam.UltimateAuth.Core/Abstractions/Stores/IUserStore.cs
new file mode 100644
index 00000000..fcb268b1
--- /dev/null
+++ b/src/CodeBeam.UltimateAuth.Core/Abstractions/Stores/IUserStore.cs
@@ -0,0 +1,58 @@
+namespace CodeBeam.UltimateAuth.Core.Abstractions
+{
+ ///
+ /// Provides minimal user lookup and security metadata required for authentication.
+ /// This store does not manage user creation, claims, or profile data — these belong
+ /// to higher-level application services outside UltimateAuth.
+ ///
+ public interface IUserStore
+ {
+ ///
+ /// Retrieves a user by identifier. Returns null if no such user exists.
+ ///
+ /// The identifier of the user.
+ /// The user instance or null if not found.
+ Task?> FindByIdAsync(TUserId userId);
+
+ ///
+ /// Retrieves a user by a login credential such as username or email.
+ /// Returns null if no matching user exists.
+ ///
+ /// The login value used to locate the user.
+ /// The user instance or null if not found.
+ Task?> FindByLoginAsync(string login);
+
+ ///
+ /// Returns the password hash for the specified user, if the user participates
+ /// in password-based authentication. Returns null for passwordless users
+ /// (e.g., external login or passkey-only accounts).
+ ///
+ /// The user identifier.
+ /// The password hash or null.
+ Task GetPasswordHashAsync(TUserId userId);
+
+ ///
+ /// Updates the password hash for the specified user. This method is invoked by
+ /// password management services and not by .
+ ///
+ /// The user identifier.
+ /// The new password hash value.
+ Task SetPasswordHashAsync(TUserId userId, string passwordHash);
+
+ ///
+ /// Retrieves the security version associated with the user.
+ /// This value increments whenever critical security actions occur, such as:
+ /// password reset, MFA reset, external login removal, or account recovery.
+ ///
+ /// The user identifier.
+ /// The current security version.
+ Task GetSecurityVersionAsync(TUserId userId);
+
+ ///
+ /// Increments the user's security version, invalidating all existing sessions.
+ /// This is typically called after sensitive security events occur.
+ ///
+ /// The user identifier.
+ Task IncrementSecurityVersionAsync(TUserId userId);
+ }
+}
diff --git a/src/CodeBeam.UltimateAuth.Core/Abstractions/Stores/IUserStoreFactory.cs b/src/CodeBeam.UltimateAuth.Core/Abstractions/Stores/IUserStoreFactory.cs
new file mode 100644
index 00000000..06e7a0d7
--- /dev/null
+++ b/src/CodeBeam.UltimateAuth.Core/Abstractions/Stores/IUserStoreFactory.cs
@@ -0,0 +1,26 @@
+namespace CodeBeam.UltimateAuth.Core.Abstractions
+{
+ ///
+ /// Provides a factory abstraction for creating tenant-scoped user store
+ /// instances used for retrieving basic user information required by
+ /// UltimateAuth authentication services.
+ ///
+ public interface IUserStoreFactory
+ {
+ ///
+ /// Creates and returns a user store instance for the specified user ID type within the given tenant context.
+ ///
+ /// The type used to uniquely identify users.
+ ///
+ /// The tenant identifier for multi-tenant environments, or null
+ /// in single-tenant deployments.
+ ///
+ ///
+ /// An implementation capable of user lookup and security metadata retrieval.
+ ///
+ ///
+ /// Thrown if no user store implementation has been registered for the given user ID type.
+ ///
+ IUserStore Create(string tenantId);
+ }
+}
diff --git a/src/CodeBeam.UltimateAuth.Core/CodeBeam.UltimateAuth.Core.csproj b/src/CodeBeam.UltimateAuth.Core/CodeBeam.UltimateAuth.Core.csproj
index 8004a0dd..34c42190 100644
--- a/src/CodeBeam.UltimateAuth.Core/CodeBeam.UltimateAuth.Core.csproj
+++ b/src/CodeBeam.UltimateAuth.Core/CodeBeam.UltimateAuth.Core.csproj
@@ -7,4 +7,10 @@
true
+
+
+
+
+
+
diff --git a/src/CodeBeam.UltimateAuth.Core/Domain/Session/AuthSessionId.cs b/src/CodeBeam.UltimateAuth.Core/Domain/Session/AuthSessionId.cs
new file mode 100644
index 00000000..f5a8ba2e
--- /dev/null
+++ b/src/CodeBeam.UltimateAuth.Core/Domain/Session/AuthSessionId.cs
@@ -0,0 +1,61 @@
+namespace CodeBeam.UltimateAuth.Core.Domain
+{
+ ///
+ /// Represents a strongly typed identifier for an authentication session.
+ /// Wraps a value and provides type safety across the UltimateAuth session management system.
+ ///
+ public readonly struct AuthSessionId : IEquatable
+ {
+ ///
+ /// Initializes a new using the specified GUID value.
+ ///
+ /// The underlying GUID representing the session identifier.
+ public AuthSessionId(Guid value)
+ {
+ Value = value;
+ }
+
+ ///
+ /// Gets the underlying GUID value of the session identifier.
+ ///
+ public Guid Value { get; }
+
+ ///
+ /// Generates a new session identifier using a newly created GUID.
+ ///
+ /// A new instance.
+ public static AuthSessionId New() => new AuthSessionId(Guid.NewGuid());
+
+ ///
+ /// Determines whether the specified is equal to the current instance.
+ ///
+ /// The session identifier to compare with.
+ /// true if the identifiers match; otherwise, false.
+ public bool Equals(AuthSessionId other) => Value.Equals(other.Value);
+
+ ///
+ /// Determines whether the specified object is equal to the current session identifier.
+ ///
+ /// The object to compare with.
+ /// true if the object is an with the same value.
+ public override bool Equals(object? obj) => obj is AuthSessionId other && Equals(other);
+
+ ///
+ /// Returns a hash code based on the underlying GUID value.
+ ///
+ public override int GetHashCode() => Value.GetHashCode();
+
+ ///
+ /// Returns the string representation of the underlying GUID value.
+ ///
+ /// The GUID as a string.
+ public override string ToString() => Value.ToString();
+
+ ///
+ /// Converts the to its underlying .
+ ///
+ /// The session identifier.
+ /// The underlying GUID value.
+ public static implicit operator Guid(AuthSessionId id) => id.Value;
+ }
+}
diff --git a/src/CodeBeam.UltimateAuth.Core/Domain/Session/ChainId.cs b/src/CodeBeam.UltimateAuth.Core/Domain/Session/ChainId.cs
new file mode 100644
index 00000000..967032b3
--- /dev/null
+++ b/src/CodeBeam.UltimateAuth.Core/Domain/Session/ChainId.cs
@@ -0,0 +1,62 @@
+namespace CodeBeam.UltimateAuth.Core.Domain
+{
+ ///
+ /// Represents a strongly typed identifier for a session chain.
+ /// A session chain groups multiple rotated sessions belonging to the same
+ /// device or application family, providing type safety across the UltimateAuth session system.
+ ///
+ public readonly struct ChainId : IEquatable
+ {
+ ///
+ /// Initializes a new with the specified GUID value.
+ ///
+ /// The underlying GUID representing the chain identifier.
+ public ChainId(Guid value)
+ {
+ Value = value;
+ }
+
+ ///
+ /// Gets the underlying GUID value of the chain identifier.
+ ///
+ public Guid Value { get; }
+
+ ///
+ /// Generates a new chain identifier using a newly created GUID.
+ ///
+ /// A new instance.
+ public static ChainId New() => new ChainId(Guid.NewGuid());
+
+ ///
+ /// Determines whether the specified is equal to the current instance.
+ ///
+ /// The chain identifier to compare with.
+ /// true if both identifiers represent the same chain.
+ public bool Equals(ChainId other) => Value.Equals(other.Value);
+
+ ///
+ /// Determines whether the specified object is equal to the current chain identifier.
+ ///
+ /// The object to compare with.
+ /// true if the object is a with the same value.
+ public override bool Equals(object? obj) => obj is ChainId other && Equals(other);
+
+ ///
+ /// Returns a hash code based on the underlying GUID value.
+ ///
+ public override int GetHashCode() => Value.GetHashCode();
+
+ ///
+ /// Returns the string representation of the underlying GUID value.
+ ///
+ /// The GUID as a string.
+ public override string ToString() => Value.ToString();
+
+ ///
+ /// Converts the to its underlying value.
+ ///
+ /// The chain identifier.
+ /// The underlying GUID value.
+ public static implicit operator Guid(ChainId id) => id.Value;
+ }
+}
diff --git a/src/CodeBeam.UltimateAuth.Core/Domain/Session/DeviceInfo.cs b/src/CodeBeam.UltimateAuth.Core/Domain/Session/DeviceInfo.cs
new file mode 100644
index 00000000..ca204c3a
--- /dev/null
+++ b/src/CodeBeam.UltimateAuth.Core/Domain/Session/DeviceInfo.cs
@@ -0,0 +1,60 @@
+namespace CodeBeam.UltimateAuth.Core.Domain
+{
+ ///
+ /// Represents metadata describing the device or client environment initiating
+ /// an authentication session. Used for security analytics, session management,
+ /// fraud detection, and device-specific login policies.
+ ///
+ public sealed class DeviceInfo
+ {
+ ///
+ /// Gets the high-level platform identifier, such as web, mobile,
+ /// tablet or iot.
+ /// Used for platform-based session limits and analytics.
+ ///
+ public string? Platform { get; init; }
+
+ ///
+ /// Gets the operating system of the client device, such as iOS 17,
+ /// Android 14, Windows 11, or macOS Sonoma.
+ ///
+ public string? OperatingSystem { get; init; }
+
+ ///
+ /// Gets the browser name and version when the client is web-based,
+ /// such as Edge, Chrome, Safari, or Firefox.
+ /// May be null for native applications.
+ ///
+ public string? Browser { get; init; }
+
+ ///
+ /// Gets the IP address of the client device.
+ /// Used for IP-binding, geolocation checks, and anomaly detection.
+ ///
+ public string? IpAddress { get; init; }
+
+ ///
+ /// Gets the raw user-agent string for web clients.
+ /// Used when deeper parsing of browser or device details is needed.
+ ///
+ public string? UserAgent { get; init; }
+
+ ///
+ /// Gets a device fingerprint or unique client identifier if provided by the
+ /// application. Useful for advanced session policies or fraud analysis.
+ ///
+ public string? Fingerprint { get; init; }
+
+ ///
+ /// Indicates whether the device is considered trusted by the user or system.
+ /// Applications may update this value when implementing trusted-device flows.
+ ///
+ public bool? IsTrusted { get; init; }
+
+ ///
+ /// Gets optional custom metadata supplied by the application.
+ /// Allows additional device attributes not covered by standard fields.
+ ///
+ public Dictionary? Custom { get; init; }
+ }
+}
diff --git a/src/CodeBeam.UltimateAuth.Core/Domain/Session/ISession.cs b/src/CodeBeam.UltimateAuth.Core/Domain/Session/ISession.cs
new file mode 100644
index 00000000..878d6b23
--- /dev/null
+++ b/src/CodeBeam.UltimateAuth.Core/Domain/Session/ISession.cs
@@ -0,0 +1,73 @@
+namespace CodeBeam.UltimateAuth.Core.Domain
+{
+ ///
+ /// Represents a single authentication session belonging to a user.
+ /// Sessions are immutable, security-critical units used for validation,
+ /// sliding expiration, revocation, and device analytics.
+ ///
+ public interface ISession
+ {
+ ///
+ /// Gets the unique identifier of the session.
+ ///
+ AuthSessionId SessionId { get; }
+
+ ///
+ /// Gets the identifier of the user who owns this session.
+ ///
+ TUserId UserId { get; }
+
+ ///
+ /// Gets the timestamp when this session was originally created.
+ ///
+ DateTime CreatedAt { get; }
+
+ ///
+ /// Gets the timestamp when the session becomes invalid due to expiration.
+ ///
+ DateTime ExpiresAt { get; }
+
+ ///
+ /// Gets the timestamp of the last successful usage.
+ /// Used when evaluating sliding expiration policies.
+ ///
+ DateTime LastSeenAt { get; }
+
+ ///
+ /// Gets a value indicating whether this session has been explicitly revoked.
+ ///
+ bool IsRevoked { get; }
+
+ ///
+ /// Gets the timestamp when the session was revoked, if applicable.
+ ///
+ DateTime? RevokedAt { get; }
+
+ ///
+ /// Gets the user's security version at the moment of session creation.
+ /// If the stored version does not match the user's current version,
+ /// the session becomes invalid (e.g., after password or MFA reset).
+ ///
+ long SecurityVersionAtCreation { get; }
+
+ ///
+ /// Gets metadata describing the client device that created the session.
+ /// Includes platform, OS, IP address, fingerprint, and more.
+ ///
+ DeviceInfo Device { get; }
+
+ ///
+ /// Gets session-scoped metadata used for application-specific extensions,
+ /// such as tenant data, app version, locale, or CSRF tokens.
+ ///
+ SessionMetadata Metadata { get; }
+
+ ///
+ /// Computes the effective runtime state of the session (Active, Expired,
+ /// Revoked, SecurityVersionMismatch, etc.) based on the provided timestamp.
+ ///
+ /// Current timestamp used for comparisons.
+ /// The evaluated of this session.
+ SessionState GetState(DateTime now);
+ }
+}
diff --git a/src/CodeBeam.UltimateAuth.Core/Domain/Session/ISessionChain.cs b/src/CodeBeam.UltimateAuth.Core/Domain/Session/ISessionChain.cs
new file mode 100644
index 00000000..b7034df1
--- /dev/null
+++ b/src/CodeBeam.UltimateAuth.Core/Domain/Session/ISessionChain.cs
@@ -0,0 +1,59 @@
+namespace CodeBeam.UltimateAuth.Core.Domain
+{
+ ///
+ /// Represents a device- or login-scoped session chain.
+ /// A chain groups all rotated sessions belonging to a single logical login
+ /// (e.g., a browser instance, mobile app installation, or device fingerprint).
+ ///
+ public interface ISessionChain
+ {
+ ///
+ /// Gets the unique identifier of the session chain.
+ ///
+ ChainId ChainId { get; }
+
+ ///
+ /// Gets the identifier of the user who owns this chain.
+ /// Each chain represents one device/login family for this user.
+ ///
+ TUserId UserId { get; }
+
+ ///
+ /// Gets the number of refresh token rotations performed within this chain.
+ ///
+ int RotationCount { get; }
+
+ ///
+ /// Gets the user's security version at the time the chain was created.
+ /// If the user's current security version is higher, the entire chain
+ /// becomes invalid (e.g., after password or MFA reset).
+ ///
+ long SecurityVersionAtCreation { get; }
+
+ ///
+ /// Gets an optional snapshot of claims taken at chain creation time.
+ /// Useful for offline clients, WASM apps, and environments where
+ /// full user lookup cannot be performed on each request.
+ ///
+ IReadOnlyDictionary? ClaimsSnapshot { get; }
+
+ ///
+ /// Gets the list of all rotated sessions created within this chain.
+ /// The newest session is always considered the active one.
+ ///
+ IReadOnlyList> Sessions { get; }
+
+ ///
+ /// Gets a value indicating whether this chain has been revoked.
+ /// Revoking a chain performs a device-level logout, invalidating
+ /// all sessions it contains.
+ ///
+ bool IsRevoked { get; }
+
+ ///
+ /// Gets the timestamp when the chain was revoked, if applicable.
+ ///
+ DateTime? RevokedAt { get; }
+ }
+
+}
diff --git a/src/CodeBeam.UltimateAuth.Core/Domain/Session/ISessionRoot.cs b/src/CodeBeam.UltimateAuth.Core/Domain/Session/ISessionRoot.cs
new file mode 100644
index 00000000..c3d3baf5
--- /dev/null
+++ b/src/CodeBeam.UltimateAuth.Core/Domain/Session/ISessionRoot.cs
@@ -0,0 +1,54 @@
+namespace CodeBeam.UltimateAuth.Core.Domain
+{
+ ///
+ /// Represents the root container for all authentication session chains of a user.
+ /// A session root is tenant-scoped and acts as the authoritative security boundary,
+ /// controlling global revocation, security versioning, and device/login families.
+ ///
+ public interface ISessionRoot
+ {
+ ///
+ /// Gets the identifier of the user who owns this session root.
+ /// Each user has one root per tenant.
+ ///
+ TUserId UserId { get; }
+
+ ///
+ /// Gets the tenant identifier associated with this session root.
+ /// Used to isolate authentication domains in multi-tenant systems.
+ ///
+ string? TenantId { get; }
+
+ ///
+ /// Gets a value indicating whether the entire session root is revoked.
+ /// When true, all chains and sessions belonging to this root are invalid,
+ /// regardless of their individual states.
+ ///
+ bool IsRevoked { get; }
+
+ ///
+ /// Gets the timestamp when the session root was revoked, if applicable.
+ ///
+ DateTime? RevokedAt { get; }
+
+ ///
+ /// Gets the current security version of the user within this tenant.
+ /// Incrementing this value invalidates all sessions, even if they are still active.
+ /// Common triggers include password reset, MFA reset, and account recovery.
+ ///
+ long SecurityVersion { get; }
+
+ ///
+ /// Gets the complete set of session chains associated with this root.
+ /// Each chain represents a device or login-family (browser instance, mobile app, etc.).
+ /// The root is immutable; modifications must go through SessionService or SessionStore.
+ ///
+ IReadOnlyList> Chains { get; }
+
+ ///
+ /// Gets the timestamp when this root structure was last updated.
+ /// Useful for caching, concurrency handling, and incremental synchronization.
+ ///
+ DateTime LastUpdatedAt { get; }
+ }
+}
diff --git a/src/CodeBeam.UltimateAuth.Core/Domain/Session/SessionMetadata.cs b/src/CodeBeam.UltimateAuth.Core/Domain/Session/SessionMetadata.cs
new file mode 100644
index 00000000..a4963810
--- /dev/null
+++ b/src/CodeBeam.UltimateAuth.Core/Domain/Session/SessionMetadata.cs
@@ -0,0 +1,40 @@
+namespace CodeBeam.UltimateAuth.Core.Domain
+{
+ ///
+ /// Represents additional metadata attached to an authentication session.
+ /// This information is application-defined and commonly used for analytics,
+ /// UI adaptation, multi-tenant context, and CSRF/session-related security data.
+ ///
+ public sealed class SessionMetadata
+ {
+ ///
+ /// Gets the version of the client application that created the session.
+ /// Useful for enforcing upgrade policies or troubleshooting version-related issues.
+ ///
+ public string? AppVersion { get; init; }
+
+ ///
+ /// Gets the locale or culture identifier associated with the session,
+ /// such as en-US, tr-TR, or fr-FR.
+ ///
+ public string? Locale { get; init; }
+
+ ///
+ /// Gets the tenant identifier attached to this session, if applicable.
+ /// This value may override or complement root-level multi-tenant resolution.
+ ///
+ public string? TenantId { get; init; }
+
+ ///
+ /// Gets a Cross-Site Request Forgery token or other session-scoped secret
+ /// used for request integrity validation in web applications.
+ ///
+ public string? CsrfToken { get; init; }
+
+ ///
+ /// Gets a dictionary for storing arbitrary application-defined metadata.
+ /// Allows extensions without modifying the core authentication model.
+ ///
+ public Dictionary? Custom { get; init; }
+ }
+}
diff --git a/src/CodeBeam.UltimateAuth.Core/Domain/Session/SessionState.cs b/src/CodeBeam.UltimateAuth.Core/Domain/Session/SessionState.cs
new file mode 100644
index 00000000..0faaf751
--- /dev/null
+++ b/src/CodeBeam.UltimateAuth.Core/Domain/Session/SessionState.cs
@@ -0,0 +1,44 @@
+namespace CodeBeam.UltimateAuth.Core.Domain
+{
+ ///
+ /// Represents the effective runtime state of an authentication session.
+ /// Evaluated based on expiration rules, revocation status, and security version checks.
+ ///
+ public enum SessionState
+ {
+ ///
+ /// The session is valid, not expired, not revoked, and its security version
+ /// matches the user's current security version.
+ ///
+ Active = 0,
+
+ ///
+ /// The session has passed its expiration time and is no longer valid.
+ ///
+ Expired = 1,
+
+ ///
+ /// The session was explicitly revoked by user action or administrative control.
+ ///
+ Revoked = 2,
+
+ ///
+ /// The session's parent chain has been revoked, typically representing a
+ /// device-level logout or device ban.
+ ///
+ ChainRevoked = 3,
+
+ ///
+ /// The user's entire session root has been revoked. This invalidates all
+ /// chains and sessions immediately across all devices.
+ ///
+ RootRevoked = 4,
+
+ ///
+ /// The session's stored SecurityVersionAtCreation does not match the user's
+ /// current security version, indicating a password reset, MFA reset,
+ /// or other critical security event.
+ ///
+ SecurityVersionMismatch = 5
+ }
+}
diff --git a/src/CodeBeam.UltimateAuth.Core/Errors/Base/UAuthDeveloperException.cs b/src/CodeBeam.UltimateAuth.Core/Errors/Base/UAuthDeveloperException.cs
new file mode 100644
index 00000000..39b340d4
--- /dev/null
+++ b/src/CodeBeam.UltimateAuth.Core/Errors/Base/UAuthDeveloperException.cs
@@ -0,0 +1,18 @@
+namespace CodeBeam.UltimateAuth.Core.Errors
+{
+ ///
+ /// Represents an exception that indicates a developer integration error
+ /// rather than a runtime or authentication failure.
+ /// These errors typically occur when UltimateAuth is misconfigured,
+ /// required services are not registered, or contracts are violated by the host application.
+ ///
+ public abstract class UAuthDeveloperException : UAuthException
+ {
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message describing the developer mistake.
+ ///
+ /// The error message explaining the incorrect usage.
+ protected UAuthDeveloperException(string message) : base(message) { }
+ }
+}
diff --git a/src/CodeBeam.UltimateAuth.Core/Errors/Base/UAuthDomainException.cs b/src/CodeBeam.UltimateAuth.Core/Errors/Base/UAuthDomainException.cs
new file mode 100644
index 00000000..4894aabf
--- /dev/null
+++ b/src/CodeBeam.UltimateAuth.Core/Errors/Base/UAuthDomainException.cs
@@ -0,0 +1,16 @@
+namespace CodeBeam.UltimateAuth.Core.Errors
+{
+ ///
+ /// Represents an exception triggered by a violation of UltimateAuth's domain rules or invariants.
+ /// These errors indicate that a business rule or authentication domain constraint has been broken (e.g., invalid session state transition,
+ /// illegal revoke action, or inconsistent security version).
+ ///
+ public abstract class UAuthDomainException : UAuthException
+ {
+ ///
+ /// Initializes a new instance of the class with a message describing the violated domain rule.
+ ///
+ /// The descriptive message for the domain error.
+ protected UAuthDomainException(string message) : base(message) { }
+ }
+}
diff --git a/src/CodeBeam.UltimateAuth.Core/Errors/Base/UAuthException.cs b/src/CodeBeam.UltimateAuth.Core/Errors/Base/UAuthException.cs
new file mode 100644
index 00000000..08ea3e15
--- /dev/null
+++ b/src/CodeBeam.UltimateAuth.Core/Errors/Base/UAuthException.cs
@@ -0,0 +1,25 @@
+namespace CodeBeam.UltimateAuth.Core.Errors
+{
+ ///
+ /// Represents the base type for all exceptions thrown by the UltimateAuth framework.
+ /// This class differentiates authentication-domain errors from general system exceptions
+ /// and provides a common abstraction for developer, domain, and runtime error types.
+ ///
+ public abstract class UAuthException : Exception
+ {
+ ///
+ /// Initializes a new instance of the class
+ /// with the specified error message.
+ ///
+ /// The message that describes the error.
+ protected UAuthException(string message) : base(message) { }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with the specified error message and underlying exception.
+ ///
+ /// The message that describes the error.
+ /// The exception that caused the current error.
+ protected UAuthException(string message, Exception? inner) : base(message, inner) { }
+ }
+}
diff --git a/src/CodeBeam.UltimateAuth.Core/Errors/Base/UAuthSessionException.cs b/src/CodeBeam.UltimateAuth.Core/Errors/Base/UAuthSessionException.cs
new file mode 100644
index 00000000..7836b57b
--- /dev/null
+++ b/src/CodeBeam.UltimateAuth.Core/Errors/Base/UAuthSessionException.cs
@@ -0,0 +1,29 @@
+using CodeBeam.UltimateAuth.Core.Domain;
+
+namespace CodeBeam.UltimateAuth.Core.Errors
+{
+ ///
+ /// Represents a domain-level exception associated with a specific authentication session.
+ /// This error indicates that a session-related invariant or rule has been violated,
+ /// such as attempting to refresh a revoked session, using an expired session,
+ /// or performing an operation that conflicts with the session's current state.
+ ///
+ public abstract class UAuthSessionException : UAuthDomainException
+ {
+ ///
+ /// Gets the identifier of the session that triggered the exception.
+ ///
+ public AuthSessionId SessionId { get; }
+
+ ///
+ /// Initializes a new instance of the class with the session identifier and an explanatory error message.
+ ///
+ /// The session identifier associated with the error.
+ /// The message describing the session rule violation.
+ protected UAuthSessionException(AuthSessionId sessionId, string message) : base(message)
+ {
+ SessionId = sessionId;
+ }
+ }
+
+}
diff --git a/src/CodeBeam.UltimateAuth.Core/Errors/Developer/UAuthConfigException.cs b/src/CodeBeam.UltimateAuth.Core/Errors/Developer/UAuthConfigException.cs
new file mode 100644
index 00000000..d247a6b8
--- /dev/null
+++ b/src/CodeBeam.UltimateAuth.Core/Errors/Developer/UAuthConfigException.cs
@@ -0,0 +1,18 @@
+namespace CodeBeam.UltimateAuth.Core.Errors
+{
+ ///
+ /// Represents an exception that is thrown when UltimateAuth is configured
+ /// incorrectly or when required configuration values are missing or invalid.
+ /// This error indicates a developer-side setup issue rather than a runtime
+ /// authentication failure.
+ ///
+ public sealed class UAuthConfigException : UAuthDeveloperException
+ {
+ ///
+ /// Initializes a new instance of the class
+ /// with a descriptive message explaining the configuration problem.
+ ///
+ /// The message describing the configuration error.
+ public UAuthConfigException(string message) : base(message) { }
+ }
+}
diff --git a/src/CodeBeam.UltimateAuth.Core/Errors/Developer/UAuthInternalException.cs b/src/CodeBeam.UltimateAuth.Core/Errors/Developer/UAuthInternalException.cs
new file mode 100644
index 00000000..ad6678a3
--- /dev/null
+++ b/src/CodeBeam.UltimateAuth.Core/Errors/Developer/UAuthInternalException.cs
@@ -0,0 +1,20 @@
+namespace CodeBeam.UltimateAuth.Core.Errors
+{
+ ///
+ /// Represents an unexpected internal error within the UltimateAuth framework.
+ /// This exception indicates a failure in internal logic, invariants, or service
+ /// coordination, rather than a configuration or authentication mistake by the developer.
+ ///
+ /// If this exception occurs, it typically means a bug or unhandled scenario
+ /// exists inside the framework itself.
+ ///
+ public sealed class UAuthInternalException : UAuthDeveloperException
+ {
+ ///
+ /// Initializes a new instance of the class
+ /// with a descriptive message explaining the internal framework error.
+ ///
+ /// The internal error message.
+ public UAuthInternalException(string message) : base(message) { }
+ }
+}
diff --git a/src/CodeBeam.UltimateAuth.Core/Errors/Developer/UAuthStoreException.cs b/src/CodeBeam.UltimateAuth.Core/Errors/Developer/UAuthStoreException.cs
new file mode 100644
index 00000000..13465d93
--- /dev/null
+++ b/src/CodeBeam.UltimateAuth.Core/Errors/Developer/UAuthStoreException.cs
@@ -0,0 +1,18 @@
+namespace CodeBeam.UltimateAuth.Core.Errors
+{
+ ///
+ /// Represents an exception that occurs when a session or user store
+ /// behaves incorrectly or violates the UltimateAuth storage contract.
+ /// This typically indicates an implementation error in the application's
+ /// persistence layer rather than a framework or authentication issue.
+ ///
+ public sealed class UAuthStoreException : UAuthDeveloperException
+ {
+ ///
+ /// Initializes a new instance of the class
+ /// with a descriptive message explaining the store failure.
+ ///
+ /// The message describing the store-related error.
+ public UAuthStoreException(string message) : base(message) { }
+ }
+}
diff --git a/src/CodeBeam.UltimateAuth.Core/Errors/UAuthDeviceLimitException.cs b/src/CodeBeam.UltimateAuth.Core/Errors/UAuthDeviceLimitException.cs
new file mode 100644
index 00000000..16152021
--- /dev/null
+++ b/src/CodeBeam.UltimateAuth.Core/Errors/UAuthDeviceLimitException.cs
@@ -0,0 +1,25 @@
+namespace CodeBeam.UltimateAuth.Core.Errors
+{
+ ///
+ /// Represents a domain-level exception that is thrown when a user exceeds the allowed number of device or platform-specific session chains.
+ /// This typically occurs when UltimateAuth's session policy restricts the
+ /// number of concurrent logins for a given platform (e.g., web, mobile)
+ /// and the user attempts to create an additional session beyond the limit.
+ ///
+ public sealed class UAuthDeviceLimitException : UAuthDomainException
+ {
+ ///
+ /// Gets the platform for which the device or session-chain limit was exceeded.
+ ///
+ public string Platform { get; }
+
+ ///
+ /// Initializes a new instance of the class with the specified platform name.
+ ///
+ /// The platform on which the limit was exceeded.
+ public UAuthDeviceLimitException(string platform) : base($"Device limit exceeded for platform '{platform}'.")
+ {
+ Platform = platform;
+ }
+ }
+}
diff --git a/src/CodeBeam.UltimateAuth.Core/Errors/UAuthInvalidCredentialsException.cs b/src/CodeBeam.UltimateAuth.Core/Errors/UAuthInvalidCredentialsException.cs
new file mode 100644
index 00000000..05941d5d
--- /dev/null
+++ b/src/CodeBeam.UltimateAuth.Core/Errors/UAuthInvalidCredentialsException.cs
@@ -0,0 +1,18 @@
+namespace CodeBeam.UltimateAuth.Core.Errors
+{
+ ///
+ /// Represents an authentication failure caused by invalid user credentials.
+ /// This error is thrown when the supplied username, password, or login
+ /// identifier does not match any valid user account.
+ ///
+ public sealed class UAuthInvalidCredentialsException : UAuthDomainException
+ {
+ ///
+ /// Initializes a new instance of the class
+ /// with a default message indicating incorrect credentials.
+ ///
+ public UAuthInvalidCredentialsException() : base("Invalid username or password.")
+ {
+ }
+ }
+}
diff --git a/src/CodeBeam.UltimateAuth.Core/Errors/UAuthInvalidPkceCodeException.cs b/src/CodeBeam.UltimateAuth.Core/Errors/UAuthInvalidPkceCodeException.cs
new file mode 100644
index 00000000..51905d1d
--- /dev/null
+++ b/src/CodeBeam.UltimateAuth.Core/Errors/UAuthInvalidPkceCodeException.cs
@@ -0,0 +1,20 @@
+namespace CodeBeam.UltimateAuth.Core.Errors
+{
+ ///
+ /// Represents an authentication failure occurring during the PKCE authorization
+ /// flow when the supplied authorization code is invalid, expired, or does not
+ /// match the original code challenge.
+ /// This exception indicates a failed PKCE verification rather than a general
+ /// credential or configuration error.
+ ///
+ public sealed class UAuthInvalidPkceCodeException : UAuthDomainException
+ {
+ ///
+ /// Initializes a new instance of the class
+ /// with a default message indicating an invalid PKCE authorization code.
+ ///
+ public UAuthInvalidPkceCodeException() : base("Invalid PKCE authorization code.")
+ {
+ }
+ }
+}
diff --git a/src/CodeBeam.UltimateAuth.Core/Errors/UAuthRootRevokedException.cs b/src/CodeBeam.UltimateAuth.Core/Errors/UAuthRootRevokedException.cs
new file mode 100644
index 00000000..fc1ad258
--- /dev/null
+++ b/src/CodeBeam.UltimateAuth.Core/Errors/UAuthRootRevokedException.cs
@@ -0,0 +1,20 @@
+namespace CodeBeam.UltimateAuth.Core.Errors
+{
+ ///
+ /// Represents a domain-level authentication failure indicating that the user's
+ /// entire session root has been revoked.
+ /// When a root is revoked, all session chains and all sessions belonging to the
+ /// user become immediately invalid, regardless of their individual expiration
+ /// or revocation state.
+ ///
+ public sealed class UAuthRootRevokedException : UAuthDomainException
+ {
+ ///
+ /// Initializes a new instance of the class
+ /// with a default message indicating that all sessions under the root are invalid.
+ ///
+ public UAuthRootRevokedException() : base("User root has been revoked. All sessions are invalid.")
+ {
+ }
+ }
+}
diff --git a/src/CodeBeam.UltimateAuth.Core/Errors/UAuthSecurityVersionMismatchException.cs b/src/CodeBeam.UltimateAuth.Core/Errors/UAuthSecurityVersionMismatchException.cs
new file mode 100644
index 00000000..b312ddef
--- /dev/null
+++ b/src/CodeBeam.UltimateAuth.Core/Errors/UAuthSecurityVersionMismatchException.cs
@@ -0,0 +1,37 @@
+namespace CodeBeam.UltimateAuth.Core.Errors
+{
+ ///
+ /// Represents a domain-level authentication failure caused by a mismatch
+ /// between the session's stored security version and the user's current
+ /// security version.
+ /// A mismatch indicates that a critical security event has occurred
+ /// after the session was created—such as a password reset, MFA reset,
+ /// account recovery, or other action requiring all prior sessions
+ /// to be invalidated.
+ ///
+ public sealed class UAuthSecurityVersionMismatchException : UAuthDomainException
+ {
+ ///
+ /// Gets the security version captured when the session was created.
+ ///
+ public long SessionVersion { get; }
+
+ ///
+ /// Gets the user's current security version, which has increased
+ /// since the session was issued.
+ ///
+ public long UserVersion { get; }
+
+ ///
+ /// Initializes a new instance of the class
+ /// using the session's stored version and the user's current version.
+ ///
+ /// The security version value stored in the session.
+ /// The user's current security version.
+ public UAuthSecurityVersionMismatchException(long sessionVersion, long userVersion) : base($"Security version mismatch. Session={sessionVersion}, User={userVersion}")
+ {
+ SessionVersion = sessionVersion;
+ UserVersion = userVersion;
+ }
+ }
+}
diff --git a/src/CodeBeam.UltimateAuth.Core/Errors/UAuthSessionExpiredException.cs b/src/CodeBeam.UltimateAuth.Core/Errors/UAuthSessionExpiredException.cs
new file mode 100644
index 00000000..f84fa777
--- /dev/null
+++ b/src/CodeBeam.UltimateAuth.Core/Errors/UAuthSessionExpiredException.cs
@@ -0,0 +1,26 @@
+using CodeBeam.UltimateAuth.Core.Domain;
+
+namespace CodeBeam.UltimateAuth.Core.Errors
+{
+ ///
+ /// Represents an authentication-domain exception thrown when a session
+ /// has passed its expiration time.
+ ///
+ /// This exception is raised during validation or refresh attempts where
+ /// the session's timestamp
+ /// indicates that it is no longer valid.
+ ///
+ /// Once expired, a session cannot be refreshed — the user must log in again.
+ ///
+ public sealed class UAuthSessionExpiredException : UAuthSessionException
+ {
+ ///
+ /// Initializes a new instance of the class
+ /// using the expired session's identifier.
+ ///
+ /// The identifier of the expired session.
+ public UAuthSessionExpiredException(AuthSessionId sessionId) : base(sessionId, $"Session '{sessionId}' has expired.")
+ {
+ }
+ }
+}
diff --git a/src/CodeBeam.UltimateAuth.Core/Errors/UAuthSessionNotActiveException.cs b/src/CodeBeam.UltimateAuth.Core/Errors/UAuthSessionNotActiveException.cs
new file mode 100644
index 00000000..21f5aae7
--- /dev/null
+++ b/src/CodeBeam.UltimateAuth.Core/Errors/UAuthSessionNotActiveException.cs
@@ -0,0 +1,25 @@
+using CodeBeam.UltimateAuth.Core.Domain;
+
+namespace CodeBeam.UltimateAuth.Core.Errors
+{
+ ///
+ /// Represents an authentication-domain exception thrown when a session exists
+ /// but is not in the state.
+ /// This exception typically occurs during validation or refresh operations when:
+ /// - the session is revoked,
+ /// - the session has expired,
+ /// - the session belongs to a revoked chain,
+ /// - or the session is otherwise considered inactive by the runtime state machine.
+ /// Only active sessions are eligible for refresh and token issuance.
+ ///
+ public sealed class UAuthSessionNotActiveException : UAuthSessionException
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The identifier of the session that is not active.
+ public UAuthSessionNotActiveException(AuthSessionId sessionId) : base(sessionId, $"Session '{sessionId}' is not active.")
+ {
+ }
+ }
+}
diff --git a/src/CodeBeam.UltimateAuth.Core/Errors/UAuthSessionRevokedException.cs b/src/CodeBeam.UltimateAuth.Core/Errors/UAuthSessionRevokedException.cs
new file mode 100644
index 00000000..7d7660c3
--- /dev/null
+++ b/src/CodeBeam.UltimateAuth.Core/Errors/UAuthSessionRevokedException.cs
@@ -0,0 +1,27 @@
+using CodeBeam.UltimateAuth.Core.Domain;
+
+namespace CodeBeam.UltimateAuth.Core.Errors
+{
+ ///
+ /// Represents an authentication-domain exception thrown when an operation attempts
+ /// to use a session that has been explicitly revoked by the user, administrator,
+ /// or by system-driven security policies.
+ ///
+ /// A revoked session is permanently invalid and cannot be refreshed, validated,
+ /// or used to obtain new tokens. Revocation typically occurs during actions such as
+ /// logout, device removal, or administrative account lockdown.
+ ///
+ /// This exception is raised in scenarios where a caller assumes the session is active
+ /// but the underlying session state indicates .
+ ///
+ public sealed class UAuthSessionRevokedException : UAuthSessionException
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The identifier of the revoked session.
+ public UAuthSessionRevokedException(AuthSessionId sessionId) : base(sessionId, $"Session '{sessionId}' has been revoked.")
+ {
+ }
+ }
+}
diff --git a/src/CodeBeam.UltimateAuth.Core/Errors/UAuthTokenTamperedException.cs b/src/CodeBeam.UltimateAuth.Core/Errors/UAuthTokenTamperedException.cs
new file mode 100644
index 00000000..0164df5f
--- /dev/null
+++ b/src/CodeBeam.UltimateAuth.Core/Errors/UAuthTokenTamperedException.cs
@@ -0,0 +1,26 @@
+namespace CodeBeam.UltimateAuth.Core.Errors
+{
+ ///
+ /// Represents an authentication-domain exception thrown when a token fails its
+ /// integrity verification checks, indicating that the token may have been altered,
+ /// corrupted, or tampered with after issuance.
+ ///
+ /// This exception is raised during token validation when signature verification fails,
+ /// claims are inconsistent, or protected fields do not match their expected values.
+ /// Such failures generally imply either client-side manipulation or
+ /// man-in-the-middle interference.
+ ///
+ /// Applications catching this exception should treat the associated token as unsafe
+ /// and deny access immediately. Reauthentication or complete session invalidation
+ /// may be required depending on the security policy.
+ ///
+ public sealed class UAuthTokenTamperedException : UAuthDomainException
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public UAuthTokenTamperedException() : base("Token integrity check failed (possible tampering).")
+ {
+ }
+ }
+}
diff --git a/src/CodeBeam.UltimateAuth.Core/Events/IAuthEventContext.cs b/src/CodeBeam.UltimateAuth.Core/Events/IAuthEventContext.cs
new file mode 100644
index 00000000..2c786396
--- /dev/null
+++ b/src/CodeBeam.UltimateAuth.Core/Events/IAuthEventContext.cs
@@ -0,0 +1,7 @@
+namespace CodeBeam.UltimateAuth.Core.Events
+{
+ ///
+ /// Marker interface for all UltimateAuth event context types.
+ ///
+ public interface IAuthEventContext { }
+}
diff --git a/src/CodeBeam.UltimateAuth.Core/Events/SessionCreatedContext.cs b/src/CodeBeam.UltimateAuth.Core/Events/SessionCreatedContext.cs
new file mode 100644
index 00000000..532c86a0
--- /dev/null
+++ b/src/CodeBeam.UltimateAuth.Core/Events/SessionCreatedContext.cs
@@ -0,0 +1,48 @@
+using CodeBeam.UltimateAuth.Core.Domain;
+
+namespace CodeBeam.UltimateAuth.Core.Events
+{
+ ///
+ /// Represents contextual data emitted when a new authentication session is created.
+ ///
+ /// This event is published immediately after a successful login or initial session
+ /// creation within a session chain. It provides the essential identifiers required
+ /// for auditing, monitoring, analytics, and external integrations.
+ ///
+ /// Handlers should treat this event as notification-only; modifying session state
+ /// or performing security-critical actions is not recommended unless explicitly intended.
+ ///
+ public sealed class SessionCreatedContext : IAuthEventContext
+ {
+ ///
+ /// Gets the identifier of the user for whom the new session was created.
+ ///
+ public TUserId UserId { get; }
+
+ ///
+ /// Gets the unique identifier of the newly created session.
+ ///
+ public AuthSessionId SessionId { get; }
+
+ ///
+ /// Gets the identifier of the session chain to which this session belongs.
+ ///
+ public ChainId ChainId { get; }
+
+ ///
+ /// Gets the timestamp on which the session was created.
+ ///
+ public DateTime CreatedAt { get; }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public SessionCreatedContext(TUserId userId, AuthSessionId sessionId, ChainId chainId, DateTime createdAt)
+ {
+ UserId = userId;
+ SessionId = sessionId;
+ ChainId = chainId;
+ CreatedAt = createdAt;
+ }
+ }
+}
diff --git a/src/CodeBeam.UltimateAuth.Core/Events/SessionRefreshedContext.cs b/src/CodeBeam.UltimateAuth.Core/Events/SessionRefreshedContext.cs
new file mode 100644
index 00000000..85ab19e2
--- /dev/null
+++ b/src/CodeBeam.UltimateAuth.Core/Events/SessionRefreshedContext.cs
@@ -0,0 +1,60 @@
+using CodeBeam.UltimateAuth.Core.Domain;
+
+namespace CodeBeam.UltimateAuth.Core.Events
+{
+ ///
+ /// Represents contextual data emitted when an authentication session is refreshed.
+ ///
+ /// This event occurs whenever a valid session performs a rotation — typically during
+ /// a refresh-token exchange or session renewal flow. The old session becomes inactive,
+ /// and a new session inherits updated expiration and security metadata.
+ ///
+ /// This event is primarily used for analytics, auditing, security monitoring, and
+ /// external workflow triggers (e.g., notifying users of new logins, updating dashboards,
+ /// or tracking device activity).
+ ///
+ public sealed class SessionRefreshedContext : IAuthEventContext
+ {
+ ///
+ /// Gets the identifier of the user whose session was refreshed.
+ ///
+ public TUserId UserId { get; }
+
+ ///
+ /// Gets the identifier of the session that was replaced during the refresh operation.
+ ///
+ public AuthSessionId OldSessionId { get; }
+
+ ///
+ /// Gets the identifier of the newly created session that replaces the old session.
+ ///
+ public AuthSessionId NewSessionId { get; }
+
+ ///
+ /// Gets the identifier of the session chain to which both sessions belong.
+ ///
+ public ChainId ChainId { get; }
+
+ ///
+ /// Gets the timestamp at which the refresh occurred.
+ ///
+ public DateTime RefreshedAt { get; }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public SessionRefreshedContext(
+ TUserId userId,
+ AuthSessionId oldSessionId,
+ AuthSessionId newSessionId,
+ ChainId chainId,
+ DateTime refreshedAt)
+ {
+ UserId = userId;
+ OldSessionId = oldSessionId;
+ NewSessionId = newSessionId;
+ ChainId = chainId;
+ RefreshedAt = refreshedAt;
+ }
+ }
+}
diff --git a/src/CodeBeam.UltimateAuth.Core/Events/SessionRevokedContext.cs b/src/CodeBeam.UltimateAuth.Core/Events/SessionRevokedContext.cs
new file mode 100644
index 00000000..bd19b7e2
--- /dev/null
+++ b/src/CodeBeam.UltimateAuth.Core/Events/SessionRevokedContext.cs
@@ -0,0 +1,57 @@
+using CodeBeam.UltimateAuth.Core.Domain;
+
+namespace CodeBeam.UltimateAuth.Core.Events
+{
+ ///
+ /// Represents contextual data emitted when an individual session is revoked.
+ ///
+ /// This event is triggered when a specific session is invalidated — either due to
+ /// explicit logout, administrator action, security enforcement, or anomaly detection.
+ /// Only the targeted session is revoked; other sessions in the same chain or root
+ /// may continue to remain active unless broader revocation policies apply.
+ ///
+ /// Typical use cases include:
+ /// - Auditing and compliance logs
+ /// - User notifications (e.g., “Your session on device X was logged out”)
+ /// - Security automations (SIEM integration, monitoring suspicious activity)
+ /// - Application workflows that must respond to session termination
+ ///
+ public sealed class SessionRevokedContext : IAuthEventContext
+ {
+ ///
+ /// Gets the identifier of the user to whom the revoked session belongs.
+ ///
+ public TUserId UserId { get; }
+
+ ///
+ /// Gets the identifier of the session that has been revoked.
+ ///
+ public AuthSessionId SessionId { get; }
+
+ ///
+ /// Gets the identifier of the session chain containing the revoked session.
+ ///
+ public ChainId ChainId { get; }
+
+ ///
+ /// Gets the timestamp at which the session revocation occurred.
+ ///
+ public DateTime RevokedAt { get; }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public SessionRevokedContext(
+ TUserId userId,
+ AuthSessionId sessionId,
+ ChainId chainId,
+ DateTime revokedAt)
+ {
+ UserId = userId;
+ SessionId = sessionId;
+ ChainId = chainId;
+ RevokedAt = revokedAt;
+ }
+ }
+
+}
diff --git a/src/CodeBeam.UltimateAuth.Core/Events/UAuthEventDispatcher.cs b/src/CodeBeam.UltimateAuth.Core/Events/UAuthEventDispatcher.cs
new file mode 100644
index 00000000..55522192
--- /dev/null
+++ b/src/CodeBeam.UltimateAuth.Core/Events/UAuthEventDispatcher.cs
@@ -0,0 +1,53 @@
+namespace CodeBeam.UltimateAuth.Core.Events
+{
+ internal sealed class UAuthEventDispatcher
+ {
+ private readonly UAuthEvents _events;
+
+ public UAuthEventDispatcher(UAuthEvents events)
+ {
+ _events = events;
+ }
+
+ public async Task DispatchAsync(IAuthEventContext context)
+ {
+ if (_events.OnAnyEvent is not null)
+ await SafeInvoke(() => _events.OnAnyEvent(context));
+
+ switch (context)
+ {
+ case SessionCreatedContext