Capabilities

OIDC provider

An in-house OAuth2/OIDC authorization server inside your app — authorization code + PKCE, client credentials, refresh tokens, discovery, JWKS, introspection, revocation, RP-initiated and back-channel logout, with app-owned login UI.

Nuvora.Nexus.Sentinel.OidcServer turns your application into a standards-compliant OAuth2 / OpenID Connect authorization server. It is implemented in-house — no OpenIddict, no Duende — and phased deliberately: v1 ships the flows that carry modern systems, and the discovery document only advertises what is actually implemented.

Mounting

builder.Services.AddSentinelOidcServer(o =>
{
    o.Issuer = "https://id.example.com";
});

app.MapSentinelOidc();

The endpoint set (fixed, standards-mandated paths):

Route Purpose
GET /.well-known/openid-configuration Discovery
GET /oidc/jwks The key set (from the signing key ring)
GET /oidc/authorize Authorization endpoint (code + PKCE)
POST /oidc/consent Consent decision callback
POST /oidc/token Token endpoint — all three grants
GET /oidc/userinfo UserInfo
POST /oidc/introspect RFC 7662 introspection
POST /oidc/revoke RFC 7009 revocation
GET /oidc/logout RP-initiated logout

What v1 implements — and what it doesn’t

From the discovery document itself: response_types_supported = ["code"], grant_types_supported = ["authorization_code", "refresh_token", "client_credentials"], code_challenge_methods_supported = ["S256"], RS256 ID tokens, client auth via client_secret_basic / client_secret_post, and back-channel logout with session support.

Not in v1, on purpose: implicit and hybrid flows (dead ends), ROPC (an anti-pattern), device flow, PAR, DPoP and token exchange (designed-for, v1.x), and dynamic client registration. PKCE with S256 is mandatory for public clientsplain is rejected.

Clients live in the registry (OidcClient): confidential or public, exact-match redirect URIs (ordinal string equality — no wildcard redirect bugs), per-client scopes, audience, token lifetimes, and a consent policy. Secrets are hashed, and rotation keeps a previous secret valid through an expiry window so deploys don’t race credential rollouts.

Consent is per-client: FirstParty clients skip the prompt; third-party clients get a persisted OidcConsentGrant that is revocable later. The RequireConsent flag forces the prompt regardless.

Clients are created through declarative config or the admin surface — by design there is no anonymous registration endpoint.

The interaction contract: your UI, Sentinel’s protocol

Sentinel ships no hosted login page. When /oidc/authorize needs a login or a consent decision, it redirects to your paths (LoginPath, ConsentPath/login and /consent by default) with the pending request carried along. Your app authenticates the user with the normal Sentinel endpoints (password, passkey, federated — your choice of UI), posts the consent decision to POST /oidc/consent, and the flow resumes. The Sentinel Server and the demo SaaS both carry a reference implementation of this contract.

Token semantics worth knowing

  • Authorization codes are single-use with teeth: a code lives two minutes, and consuming it twice doesn’t just fail — it revokes the refresh-token family that the first consumption produced and emits oidc.code_reuse. A replayed code is treated as the incident it is.
  • Refresh grants remember their scopes: re-minting reads the exact granted scopes from the persisted grant; a refresh request can narrow scopes but never widen them.
  • client_credentials maps onto service accounts — the same §15 machine-identity principals used everywhere else, with secret rotation and overlap windows. It honors an audience parameter and never issues refresh or ID tokens.
  • Back-channel logout delivers signed logout tokens only to clients that actually hold a live grant for the session and declare a BackChannelLogoutUri; failures emit oidc.backchannel_logout_failed rather than silently dropping.

Keys

Signing keys come from the same SigningKeyRing that backs first-party tokens: one primary signs, retired keys keep verifying through the overlap window (7 days by default), and the JWKS rolls over without a flag day. Outside Development, unconfigured keys fail the boot — a production IdP with ephemeral keys is an outage that hasn’t happened yet, so Sentinel refuses to be one.

Conformance

An internal OIDC conformance suite runs in CI (Tests.Oidc), and the official OpenID Foundation conformance run — with certification as the goal — is committed before 1.0, alongside an external pentest. The protocol surface is treated as the attack surface it is.

Learn by building

Article 006 — An OIDC server inside your app takes a working host from getting started and adds discovery, a confidential client, the code + PKCE dance and introspection, end to end. For exchanging external workload tokens for Sentinel tokens, see machine identity.