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 clients — plain is rejected.
Clients and consent
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_credentialsmaps onto service accounts — the same §15 machine-identity principals used everywhere else, with secret rotation and overlap windows. It honors anaudienceparameter 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 emitoidc.backchannel_logout_failedrather 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.