API reference — Protocols
Nuvora.Nexus.Sentinel.OidcServer
In-house OAuth2/OIDC authorization server for Sentinel (no OpenIddict): discovery, JWKS, authorization code + PKCE, client credentials, refresh token grants, userinfo, revocation (RFC 7009), introspection (RFC 7662), RP-initiated + back-channel logout, client registry and consent — with a host-app interaction contract for login and consent UI.
dotnet add package Nuvora.Nexus.Sentinel.OidcServer
Nuvora.Nexus.Sentinel.OidcServer
BackChannelLogoutService
public sealed class BackChannelLogoutService : ISessionEndNotifier
OIDC Back-Channel Logout 1.0 delivery, wired into Core’s ISessionEndNotifier port so EVERY session-ending flow triggers it: RP-initiated logout (/oidc/logout), the first-party /auth/logout, and admin user-suspend.
Which clients get told: the ones that actually SAW the session (or user), derived from the persisted token grants (OidcTokenGrant) — never a blast to every registered client — and then only those whose registration carries a OidcClient.BackChannelLogoutUri.
Delivery is best-effort fire-and-forget: all POSTs run concurrently, each bounded by OidcServerOptions.BackChannelLogoutTimeout, and a failed delivery emits oidc.backchannel_logout_failed instead of failing the logout — a relying party’s outage must never hold the user’s logout hostage (same posture as ISentinelEventSink).
Constructors
BackChannelLogoutService(IOidcStore oidcStore, SigningKeyRing keyRing, SentinelTokenOptions tokenOptions, IHttpClientFactory httpClientFactory, ISentinelClock clock, ISentinelEventSink events, IOptions<OidcServerOptions> options)
OIDC Back-Channel Logout 1.0 delivery, wired into Core’s ISessionEndNotifier port so EVERY session-ending flow triggers it: RP-initiated logout (/oidc/logout), the first-party /auth/logout, and admin user-suspend. Which clients get told: the ones that actually SAW the session (or user), derived from the persisted token grants (OidcTokenGrant) — never a blast to every registered client — and then only those whose registration carries a OidcClient.BackChannelLogoutUri. Delivery is best-effort fire-and-forget: all POSTs run concurrently, each bounded by OidcServerOptions.BackChannelLogoutTimeout, and a failed delivery emits oidc.backchannel_logout_failed instead of failing the logout — a relying party’s outage must never hold the user’s logout hostage (same posture as ISentinelEventSink).
Methods
ValueTask NotifySessionEndAsync(Guid realmId, Guid userId, Guid sessionId, CancellationToken cancellationToken = default(CancellationToken))
One session ended (user logout, RP-initiated logout, remote device logout).
ValueTask NotifySubjectSessionsEndAsync(Guid realmId, Guid userId, CancellationToken cancellationToken = default(CancellationToken))
Every session of the subject is (being) ended — admin suspend, logout-all.
Fields
const string HttpClientName = "Nuvora.Nexus.Sentinel.OidcServer.BackChannelLogout"
Named IHttpClientFactory client used for deliveries — hosts (and tests) can configure handlers/proxies under this name.
const string LogoutTokenType = "logout+jwt"
The logout token’s protected-header typ (Back-Channel Logout 1.0 §2.4).
OidcScopes
public static class OidcScopes
The OIDC standard scope identifiers the server understands natively.
Fields
const string Email = "email"
const string OfflineAccess = "offline_access"
Requesting this is what makes the token endpoint issue a refresh token.
const string OpenId = "openid"
const string Profile = "profile"
OidcServerOptions
public sealed class OidcServerOptions
Options for the OIDC authorization-server surface. One options class for the whole package, same posture as SentinelAspNetOptions.
Properties
HashSet<string> SupportedScopes { get; }
The scope registry: every scope any client may be granted. Starts with the OIDC standard set; hosts append their own API scopes. An authorize request for a scope outside this set fails with invalid_scope even if a client registration lists it — the registry is the outer bound, the client’s AllowedScopes the inner one.
TimeSpan AuthorizationCodeLifetime { get; set; }
Authorization codes are one network round-trip long; RFC 6749 recommends ≤ 10 minutes, Sentinel uses 2.
TimeSpan BackChannelLogoutTimeout { get; set; }
Per-request timeout for back-channel logout deliveries. Deliberately short: logout-token delivery is best-effort — a slow relying party must not hold the user’s logout hostage.
TimeSpan IdTokenLifetime { get; set; }
ID tokens are proof-of-authentication, not access credentials — short by design.
TimeSpan LogoutTokenLifetime { get; set; }
Lifetime of the exp in logout tokens (OIDC Back-Channel Logout 1.0 §2.4); they are consumed immediately, so 2 minutes is generous.
string AuthPathPrefix { get; set; }
The auth endpoint-group prefix the host passed to MapSentinelAuth (default /auth). RP-initiated logout needs it to delete the refresh cookie, whose Path is scoped to {prefix}/refresh — deletion must repeat the exact path.
string ConsentPath { get; set; }
HOST-app consent page: authorize 302s to {ConsentPath}?returnUrl={authorize URL}&client_id=…&scope=… when consent is required. The page renders the decision UI and POSTs it to /oidc/consent, which records the grant and bounces back to returnUrl.
string Issuer { get; set; }
The iss of the discovery document and of ID tokens. Empty (the default) falls back to SentinelTokenOptions.Issuer — one issuer identity for the whole host unless deliberately split.
string LoginPath { get; set; }
HOST-app login page (the interaction contract): an unauthenticated authorize request 302s to {LoginPath}?returnUrl={url-encoded authorize URL}. The host page authenticates the user (establishing the Sentinel cookie) and then redirects back to returnUrl, which re-enters the authorize endpoint with a principal present.
Nuvora.Nexus.Sentinel.OidcServer.DependencyInjection
SentinelOidcServerServiceCollectionExtensions
public static class SentinelOidcServerServiceCollectionExtensions
Methods
static IServiceCollection AddSentinelOidcServer(this IServiceCollection services, Action<OidcServerOptions>? configure = null)
Configures the OIDC authorization-server options and wires back-channel logout: BackChannelLogoutService becomes the host’s ISessionEndNotifier, so RP-initiated logout, /auth/logout and admin user-suspend all deliver logout tokens. Call alongside AddSentinel() + AddSentinelAuthentication(), then mount with MapSentinelOidc(). What is deliberately NOT defaulted (same posture as the identity stores): the IOidcStore. An authorization server silently running on an in-memory client registry is a footgun — register the EF adapter (AddSentinelEfCoreStores) or your own implementation; InMemoryOidcStore is exported for tests and samples to opt into explicitly.
WorkloadFederationServiceCollectionExtensions
public static class WorkloadFederationServiceCollectionExtensions
Registration for the workload federation surface, following the Sentinel DI conventions (TryAdd, host wins).
Methods
static IServiceCollection AddSentinelWorkloadFederation(this IServiceCollection services, Action<WorkloadFederationOptions>? configure = null)
Registers WorkloadFederationService and its collaborators: an IHttpClientFactory-backed JWKS fetcher behind the IRemoteJwksCache port (Core stays HTTP-free) and an in-memory IWorkloadTrustStore default. Everything is TryAdd: register the EF trust store (AddSentinelEfWorkloadTrustStore) or your own cache implementation BEFORE this call to override. Call alongside AddSentinel() + AddSentinelAuthentication(), then mount with MapSentinelWorkloadFederation(). The in-memory trust store default is acceptable here (unlike the identity stores): an empty trust registry denies every exchange — fail-closed, not silently permissive.
Fields
const string JwksHttpClientName = "SentinelWorkloadJwks"
Named HttpClient used for JWKS/discovery fetches, so hosts can configure it (proxy, timeouts) by name.
Nuvora.Nexus.Sentinel.OidcServer.Endpoints
ConsentDecisionRequest
public sealed record ConsentDecisionRequest : IEquatable<ConsentDecisionRequest>
Wire contract for the host consent page’s decision POST. Public so client SDKs can share the shape.
Constructors
ConsentDecisionRequest(string? ClientId, List<string>? Scopes, bool Approve, string? ReturnUrl)
Wire contract for the host consent page’s decision POST. Public so client SDKs can share the shape.
Properties
List<string>? Scopes { get; init; }
bool Approve { get; init; }
string? ClientId { get; init; }
string? ReturnUrl { get; init; }
SentinelOidcEndpoints
public static class SentinelOidcEndpoints
The OAuth2/OIDC authorization-server endpoint group — implemented in-house, no OpenIddict. Mountable and opt-in like every Sentinel surface: hosts call SentinelOidcEndpoints.MapSentinelOidc; nothing is auto-mapped.
Methods
static IEndpointRouteBuilder MapSentinelOidc(this IEndpointRouteBuilder endpoints)
Maps the v1 surface: discovery, JWKS, authorize (code + PKCE), consent decision, token (authorization_code / refresh_token / client_credentials), userinfo, revocation (RFC 7009), introspection (RFC 7662), and RP-initiated logout. Requires the Core login stack in DI (AddSentinel + identity stores + SigningKeyRing) plus an IOidcStore registration, AddSentinelOidcServer (back-channel logout wiring) and AddSentinelAuthentication (the authorize endpoint establishes the user through the Sentinel scheme).
WorkloadFederationEndpoints
public static class WorkloadFederationEndpoints
The workload identity federation surface: a DEDICATED token-exchange endpoint rather than a new grant_type inside /oidc/token — the exchange authenticates with the subject token itself (no client credentials), so it shares none of the token endpoint’s client authentication machinery, and a workload caller gets one purpose-built URL. Wire shape follows RFC 8693 (grant_type urn:ietf:params:oauth:grant-type:token-exchange, subject_token, audience) so standard tooling can call it unchanged. Mountable and opt-in like every Sentinel surface: hosts call WorkloadFederationEndpoints.MapSentinelWorkloadFederation; nothing is auto-mapped.
Methods
static IEndpointRouteBuilder MapSentinelWorkloadFederation(this IEndpointRouteBuilder endpoints, string prefix = "/oidc/workload")
Maps POST {prefix}/token: the exchange of an external workload OIDC token for a Sentinel access token. Requires AddSentinelWorkloadFederation (plus the Core login stack and a machine identity store) in DI.
Fields
const string AccessTokenTypeUrn = "urn:ietf:params:oauth:token-type:access_token"
RFC 8693 §3: what the endpoint issues (and the default assumption for what it receives).
const string TokenExchangeGrantType = "urn:ietf:params:oauth:grant-type:token-exchange"
RFC 8693 §2.1 grant type identifier.