OAuth 2.0 / OpenID Connect Security Token Service (STS) built on .NET 10 and OpenIddict 7.6.
MIT-0 licensed — free for any use, no attribution required.
A self-hostable, .NET-native identity provider that goes well beyond stock OpenIddict. It hand-implements modern protocol features OpenIddict 7.6 does not have — DPoP, CIBA, FAPI 2.0 enforcement, JARM, JAR, SSF/CAEP — on top of the OpenIddict server/validation core, with ASP.NET Core Identity and MySQL/MariaDB storage.
| Protocol | RFC / Spec | Status |
|---|---|---|
| OAuth 2.0 / OIDC Core | RFC 6749 / OpenID Connect 1.0 | ✅ |
| OAuth 2.1 baseline (PKCE mandatory, implicit/hybrid off) | draft-ietf-oauth-v2-1 | ✅ |
| DPoP (sender-constrained tokens) | RFC 9449 | ✅ Hand-implemented |
| CIBA (decoupled authentication) | OpenID Connect CIBA Core 1.0 | ✅ Hand-implemented |
| PAR (Pushed Authorization Request) | RFC 9126 | ✅ |
| JAR (JWT-Secured Authorization Request) | RFC 9101 | ✅ Hand-implemented |
| JARM (JWT response mode, signed + encrypted) | JARM | ✅ |
| FAPI 2.0 Security Profile enforcement | FAPI 2.0 Final | ✅ Opt-in boundary — OIDF certification pending sponsorship |
| mTLS (client auth + sender-constrained tokens) | RFC 8705 | ✅ |
| Token Exchange (delegation) | RFC 8693 | ✅ |
| Device Authorization Grant | RFC 8628 | ✅ |
| OIDC Back-Channel Logout | OIDC BC Logout 1.0 | ✅ |
| OIDC Front-Channel Logout | OIDC FC Logout 1.0 | ✅ |
| SSF / CAEP (Shared Signals) | RFC 8933/8934/8935 + CAEP 1.0 | ✅ Stream mgmt + push + poll |
| SCIM 2.0 (user/group provisioning) | RFC 7643/7644 | ✅ |
| WebAuthn / Passkeys (.NET 10 native) | FIDO2 | ✅ |
| MCP Authorization (resource metadata + DCR) | RFC 9728 + RFC 7591 | ✅ |
| Identity MCP tools (Vault + user self-service) | MCP streamable HTTP / JSON-RPC | ✅ Opt-in with Management |
| Dynamic Client Registration | RFC 7591 | ✅ Opt-in, token-gated |
There is no internal multi-tenant system, by design. Because spinning up a complete new application (e.g. a Docker container with its own database) and sharing hardware resources is trivially cheap today, an in-process tenant boundary adds complexity without adding safety: external isolation (one deployment per tenant) is far more secure and more viable than row-level separation inside a shared database. The tenant machinery that existed in early builds (subject-to-tenant mappings, tenant-scoped object authorization, tenant claims projection) was removed accordingly. Object-level authorization protects principals; vault-secret contexts/namespaces remain as pure data organization.
10 projects, ~46k LOC, clean layering enforced by CI tests:
Application.Abstractions ← implementation-neutral contracts (NuGet-packable)
▲
Core ───────────────────── AppDbContext, entities, Identity lifecycle, branding
▲ ▲
│ └─ (referenced by every module)
STS ─────────────────────── OpenIddict server+validation, /connect/* controllers,
│ DPoP/CIBA/FAPI2/JARM/JAR/SSF hand-rolled on top
Management ──────────────── optional REST API (clients/scopes/users/...), capability authz
SCIM ────────────────────── optional RFC 7643/7644 Users/Groups
UI (Blazor Server) ──────── public: login/consent/logout/register/manage/device/2FA/passkeys
UI.Management (Blazor) ──── admin dashboard
Server ──────────────────── composition host (the only runnable project / Docker entrypoint)
Tests ───────────────────── 318 integration + unit tests (xUnit, WebApplicationFactory)
- OAuth 2.1 secure-by-default: PKCE mandatory for all code clients,
plainremoved, implicit/hybrid/password/none grants default-off, token exchange default-off with client allowlist, refresh-token rotation always on - Production cert enforcement: missing signing/encryption PFX is a fatal startup error outside Development
- Cookie
Secure=Always, issuer pinning, antiforgery on every state-changing endpoint - Account lockout on both interactive login and password grant, plus per-IP rate limiting across every credential surface (
POST /connect/*,/bc-authorize, interactive/account/*) and the administrative APIs (management + SCIM), with whole-collection commands in their own bucket so a provisioning run and ordinary calls cannot starve each other - Bounded audit growth: management audit history is pruned past
Management.AuditRetentionDays(15 by default), and repeated identical refusals collapse to one row per operator/capability/resource per window - DCR disabled by default, constant-time-compared initial access token, fail-closed
- CSP (tightened
connect-src, no ws/wss wildcard), Permissions-Policy (deny-all), COOP, CORP - Data Protection keys encrypted at rest with the signing certificate
- Breached-password validator (HIBP k-anonymity range API, opt-in)
- Distributed stores for CIBA, DPoP nonce, DPoP jti replay (IDistributedCache / Redis-ready)
- Non-root Docker, digest-pinned images, multi-stage build, separate liveness/readiness probes
All the TECHNICAL prerequisites for the FAPI 2.0 Security Profile are already enforced in code: mandatory PAR, DPoP sender-constrained tokens (with the RFC 9449 nonce dance), JAR request objects, mTLS sender constraining, and PS256/ES256 signing agility for vault-managed keys.
The one missing step is the official OpenID Foundation certification — an external, paid conformance process (submission fees + the formal run against the OIDF test suite). We are seeking sponsorship to fund it. If this project is useful to you or your organization, you can support the certification effort:
Every contribution goes straight to the certification submission — and sponsors will be credited here once the profile is certified.
# Clone
git clone https://github.com/sufficit/sufficit-identity.git
cd sufficit-identity
# Copy the template and fill in your values
cp src/server/appsettings.json.template src/server/appsettings.Development.json
# Run (Development auto-creates the schema with ephemeral certs)
dotnet run --project src/server/Sufficit.Identity.Server.csproj→ ONBOARD.md for the full setup guide (Docker, first client, certificates, etc.)
Every option is documented in src/server/appsettings.json.template with inline explanations. Key sections:
- Certificates — PFX paths for token signing/encryption (required in production)
- TrustedProxies — CIDR list for reverse-proxy forwarded headers
- SSF/CAEP — stream management, push/poll delivery, event types
- PAR / JAR / JARM / FAPI 2.0 — opt-in protocol enforcement
- Management / SCIM / Identity MCP — optional REST and agent surfaces with Bearer, scope and capability-gated authorization
318 tests covering every OAuth/OIDC grant type end-to-end over real HTTP, plus unit tests for protocol internals, distributed stores, security hardening, and architecture enforcement.
dotnet testCI runs on GitHub Actions with real MariaDB 10.4.34, -warnaserror, SHA-verified NuGet feed, and migration rehearsal.
MIT-0 (MIT No Attribution). The Sufficit.Identity.* code is MIT-0; OpenIddict (Apache 2.0) and other third-party dependencies retain their own licenses.
See the documentation index for architecture, design, runbooks, active plans, and completed work records.