API reference — HTTP & endpoints

Nuvora.Nexus.Sentinel.AspNetCore

ASP.NET Core surface for Sentinel: the "Sentinel" authentication handler (Bearer + httpOnly cookie with CSRF double-submit), the request principal/context accessor, and mountable minimal-API endpoint groups (MapSentinelAuth, MapSentinelProfile).

dotnet add package Nuvora.Nexus.Sentinel.AspNetCore

Nuvora.Nexus.Sentinel.AspNetCore

ISentinelContextAccessor

public interface ISentinelContextAccessor

DI-friendly view of the current request’s Sentinel identity for services that should not take an HttpContext dependency directly.

Properties

SentinelPrincipal? Principal { get; }
SubjectSnapshot? Snapshot { get; }

SentinelAspNetOptions

public sealed class SentinelAspNetOptions

The single options class for the whole ASP.NET surface (deliverable folds endpoint options in here on purpose — one class to configure, one source of truth): token validation identity for the authentication handler, transport selection, and cookie naming/pathing.

Properties

Guid DefaultRealmId { get; set; }

Realm the auth endpoints serve. This wave hosts a single realm per mounted group; multi-realm routing is the reference server’s job and layers on top later.

List<string> AllowedFederationReturnUrls { get; set; }

Allowlist for the federation return URL: the redirect_uri a caller hands to /auth/idp/{key}/start, where the BROWSER lands after the federated login completes (distinct from the OAuth callback, which is always Sentinel’s own endpoint). Site-relative paths (/app, but never protocol-relative //…) are always allowed; absolute URLs must match an entry here — exact, or a single-* wildcard with WorkloadWildcard semantics (https://app.example.com/*). Empty list = absolute return URLs are refused (fail-closed against open redirects).

SentinelTokenTransport Transport { get; set; }

Response mode for /login, /mfa/verify and /refresh successes.

string AccessTokenCookieName { get; set; }

httpOnly access-token cookie.

string Audience { get; set; }

The aud this host mints into and accepts from access tokens. Audience enforcement is not optional — an empty value fails validation rather than skipping the check.

string CookiePath { get; set; }

Path for the access-token and CSRF cookies, relative to the host’s PathBase. Cookie paths are always combined with the request’s PathBase at set time — never hardcoded — which fixes the Node stack’s hardcoded /auth that broke any host mounted under a sub-path.

string CsrfCookieName { get; set; }

NON-httpOnly CSRF cookie for the double-submit check: client script must be able to read it to echo it in the header.

string CsrfHeaderName { get; set; }

Header that must carry the SentinelAspNetOptions.CsrfCookieName value on unsafe cookie-authenticated requests.

string Issuer { get; set; }

Expected iss of access tokens. Must equal SentinelTokenOptions.Issuer used at minting.

string RefreshTokenCookieName { get; set; }

httpOnly refresh-token cookie; scoped to the refresh endpoint path only, so it never rides along on ordinary requests.

SentinelContextAccessor

public sealed class SentinelContextAccessor : ISentinelContextAccessor

Scoped accessor backed by IHttpContextAccessor; registered by AddSentinelAuthentication.

Constructors

SentinelContextAccessor(IHttpContextAccessor httpContextAccessor)

Scoped accessor backed by IHttpContextAccessor; registered by AddSentinelAuthentication.

Properties

SentinelPrincipal? Principal { get; }
SubjectSnapshot? Snapshot { get; }

SentinelHttpContextExtensions

public static class SentinelHttpContextExtensions

Methods

static SentinelPrincipal? GetSentinelPrincipal(this HttpContext context)

The Sentinel principal for this request, or null when the request is unauthenticated.

static SubjectSnapshot? GetSentinelSnapshot(this HttpContext context)

The subject snapshot resolved at authentication time (grants + team memberships), or null when unauthenticated or no snapshot source is wired.

SentinelHttpContextKeys

public static class SentinelHttpContextKeys

Keys under which the handler stashes per-request state in HttpContext.Items.

Fields

const string Principal = "Nuvora.Nexus.Sentinel.Principal"
const string Snapshot = "Nuvora.Nexus.Sentinel.Snapshot"

The subject’s permission snapshot, teams included (the Node bug of hardcoded-empty team memberships is fixed by populating this at authentication time).

SentinelPrincipal

public sealed record SentinelPrincipal : IEquatable<SentinelPrincipal>

The authenticated request identity Sentinel establishes: who, in which realm/org context, from which session, at what MFA level. Stored in HttpContext.Items by the authentication handler and read through SentinelHttpContextExtensions.GetSentinelPrincipal or ISentinelContextAccessor.

SentinelPrincipal.OwnerUserId is the attribution split: for API-key requests, SentinelPrincipal.SubjectId is the CREDENTIAL (the key’s id — the authorization context), while SentinelPrincipal.OwnerUserId is the HUMAN whose snapshot caps the key — audit and attribution read this one. Null for user principals, where subject and owner coincide.

Constructors

SentinelPrincipal(Guid SubjectId, Guid RealmId, Guid? OrganizationId, Guid? SessionId, SessionMfaLevel MfaLevel, SentinelPrincipalKind Kind, Guid? OwnerUserId = null)

The authenticated request identity Sentinel establishes: who, in which realm/org context, from which session, at what MFA level. Stored in HttpContext.Items by the authentication handler and read through SentinelHttpContextExtensions.GetSentinelPrincipal or ISentinelContextAccessor. SentinelPrincipal.OwnerUserId is the attribution split: for API-key requests, SentinelPrincipal.SubjectId is the CREDENTIAL (the key’s id — the authorization context), while SentinelPrincipal.OwnerUserId is the HUMAN whose snapshot caps the key — audit and attribution read this one. Null for user principals, where subject and owner coincide.

Properties

Guid RealmId { get; init; }
Guid SubjectId { get; init; }
Guid? ImpersonationId { get; init; }

The impersonation record id (imp_id claim) backing the impersonation banner contract; null when not impersonated.

Guid? ImpersonatorId { get; init; }

The impersonating admin’s user id when this request rides an impersonation token — the token’s act.sub. SentinelPrincipal.SubjectId is then the TARGET (authorization runs as the target); this is the true attribution. Null when not impersonated.

Guid? OrganizationId { get; init; }
Guid? OwnerUserId { get; init; }
Guid? SessionId { get; init; }
SentinelPrincipalKind Kind { get; init; }
SessionMfaLevel MfaLevel { get; init; }

SentinelPrincipalKind

public enum SentinelPrincipalKind

What kind of credential established the request identity. SentinelPrincipalKind.User and SentinelPrincipalKind.ApiKey ship now; SentinelPrincipalKind.ServiceAccount principals arrive with the OIDC client-credentials grant (Wave 3).

Values

  • User
  • ApiKey
  • ServiceAccount

SentinelTokenTransport

public enum SentinelTokenTransport

How successful auth-flow responses hand tokens back to the client (both transports are first-class; cookie-first is the guidance for browser apps, Bearer for APIs, machines, and mobile).

Values

  • BearerAndCookie — Set cookies AND return tokens in the JSON body. The permissive default: one host serves browser and API clients alike.
  • Bearer — Tokens only in the JSON body; no cookies are set.
  • Cookie — Tokens only in httpOnly cookies; the JSON body never carries them (browser apps that must keep tokens out of script reach).

Nuvora.Nexus.Sentinel.AspNetCore.Authentication

SentinelAuthenticationDefaults

public static class SentinelAuthenticationDefaults

Fields

const string Scheme = "Sentinel"

The scheme name registered by AddSentinelAuthentication.

SentinelAuthenticationHandler

public sealed class SentinelAuthenticationHandler : AuthenticationHandler<SentinelAuthenticationOptions>

Establishes the Sentinel principal from either transport, in order: (a) Authorization: Bearer header, (b) the access-token cookie. The cookie path additionally enforces CSRF double-submit on unsafe methods. Token validation is the strict Core path — typ at+sentinel, issuer, audience, all mandatory — with keys resolved through the realm’s SigningKeyRing.

Constructors

SentinelAuthenticationHandler(IOptionsMonitor<SentinelAuthenticationOptions> options, ILoggerFactory logger, UrlEncoder encoder, IOptions<SentinelAspNetOptions> sentinelOptions, SigningKeyRing keyRing, ISentinelClock clock)

Establishes the Sentinel principal from either transport, in order: (a) Authorization: Bearer header, (b) the access-token cookie. The cookie path additionally enforces CSRF double-submit on unsafe methods. Token validation is the strict Core path — typ at+sentinel, issuer, audience, all mandatory — with keys resolved through the realm’s SigningKeyRing.

Methods

override Task<AuthenticateResult> HandleAuthenticateAsync()

Allows derived types to handle authentication.

SentinelAuthenticationOptions

public sealed class SentinelAuthenticationOptions : AuthenticationSchemeOptions

Scheme options exist only because AuthenticationHandler requires a per-scheme options type. All real configuration lives in SentinelAspNetOptions — deliberately one options class for the whole surface, not two half-overlapping ones.

Nuvora.Nexus.Sentinel.AspNetCore.Captcha

HCaptchaVerifier

public sealed class HCaptchaVerifier : SiteVerifyCaptchaVerifier

hCaptcha.

Constructors

HCaptchaVerifier(IHttpClientFactory httpClientFactory, SentinelCaptchaOptions options)

hCaptcha.

ReCaptchaVerifier

public sealed class ReCaptchaVerifier : SiteVerifyCaptchaVerifier

Google reCAPTCHA v2/v3. v3 score thresholds are the host’s concern — this adapter honors the boolean verdict.

Constructors

ReCaptchaVerifier(IHttpClientFactory httpClientFactory, SentinelCaptchaOptions options)

Google reCAPTCHA v2/v3. v3 score thresholds are the host’s concern — this adapter honors the boolean verdict.

SiteVerifyCaptchaVerifier

public abstract class SiteVerifyCaptchaVerifier : ICaptchaVerifier

Shared implementation for the three supported CAPTCHA providers — Turnstile, hCaptcha and reCAPTCHA all expose the same “siteverify” contract: form-encoded POST of secret/response/remoteip, JSON reply with a boolean success. Only the endpoint URL differs, so each provider is a one-line subclass.

Failure posture per the ICaptchaVerifier contract: malformed tokens, non-2xx replies, timeouts and provider outages all return false — an unverifiable token is an unverified token. That is safe because the adaptive-captcha band sits BELOW the hard block threshold: the worst a provider outage causes is a challenge loop at elevated traffic, never a lockout.

Constructors

SiteVerifyCaptchaVerifier(IHttpClientFactory httpClientFactory, SentinelCaptchaOptions options, string verifyEndpoint)

Shared implementation for the three supported CAPTCHA providers — Turnstile, hCaptcha and reCAPTCHA all expose the same “siteverify” contract: form-encoded POST of secret/response/remoteip, JSON reply with a boolean success. Only the endpoint URL differs, so each provider is a one-line subclass. Failure posture per the ICaptchaVerifier contract: malformed tokens, non-2xx replies, timeouts and provider outages all return false — an unverifiable token is an unverified token. That is safe because the adaptive-captcha band sits BELOW the hard block threshold: the worst a provider outage causes is a challenge loop at elevated traffic, never a lockout.

Methods

ValueTask<bool> VerifyAsync(string token, string? ip, CancellationToken cancellationToken = default(CancellationToken))

Fields

const string HttpClientName = "sentinel-captcha"

Named client so hosts can attach their own handlers/timeouts via AddHttpClient(HttpClientName).

TurnstileCaptchaVerifier

public sealed class TurnstileCaptchaVerifier : SiteVerifyCaptchaVerifier

Cloudflare Turnstile — the default provider.

Constructors

TurnstileCaptchaVerifier(IHttpClientFactory httpClientFactory, SentinelCaptchaOptions options)

Cloudflare Turnstile — the default provider.

Nuvora.Nexus.Sentinel.AspNetCore.DependencyInjection

SentinelAspNetCoreServiceCollectionExtensions

public static class SentinelAspNetCoreServiceCollectionExtensions

Methods

static IServiceCollection AddSentinelAuthentication(this IServiceCollection services, Action<SentinelAspNetOptions>? configure = null)

Registers the “Sentinel” authentication scheme plus the request-context accessor. The scheme is also made the default authenticate/challenge scheme so app.UseAuthentication() runs the handler without further ceremony; hosts that compose multiple schemes can override the defaults through their own AddAuthentication call (registration is additive, same posture as AddSentinel). The handler resolves SigningKeyRing and ISentinelClock from DI — the host (or the meta package wiring) must have registered an initialized ring for the realm.

SentinelCaptchaServiceCollectionExtensions

public static class SentinelCaptchaServiceCollectionExtensions

Methods

static IServiceCollection AddSentinelCaptcha(this IServiceCollection services, Action<SentinelCaptchaOptions> configure)

Registers the adaptive-CAPTCHA verifier for the configured provider — Turnstile, hCaptcha or reCAPTCHA, all speaking the same siteverify shape. Additive and TryAdd-based like every other AddSentinel* call; a host’s own ICaptchaVerifier registered earlier wins. This wires VERIFICATION only. The escalation itself is switched on the abuse options — set SentinelAbuseOptions.CaptchaEnabled (with its SentinelAbuseOptions.CaptchaFactor band) when registering them, so the challenge is only ever demanded where a provider exists to verify it.

SentinelFederationServiceCollectionExtensions

public static class SentinelFederationServiceCollectionExtensions

Registration for the inbound-federation surface, following the standard Sentinel DI conventions (TryAdd, host wins).

Methods

static IServiceCollection AddSentinelFederation(this IServiceCollection services, Action<FederatedLoginOptions>? configure = null)

Registers FederatedLoginService and its collaborators: IHttpClientFactory-backed implementations of the two HTTP ports (IFederationTokenClient for the code exchange, IRemoteJwksCache for discovery/JWKS — Core stays HTTP-free), the in-memory challenge/state stores (hot state), an in-memory IIdentityProviderStore default (acceptable: an empty provider registry denies every federated login — fail-closed), and the PasskeyLoginCompleter completion seam. What is NOT defaulted: IFederatedIdentityStore — federated links and JIT users are identity data, and the same footgun rationale as the user/session stores applies: register a store adapter (AddSentinelEfFederationStores) or an in-memory instance explicitly, BEFORE this call so the TryAdd defaults yield. Call alongside AddSentinel() + AddSentinelAuthentication(), then mount with MapSentinelFederation().

Fields

const string FederationHttpClientName = "SentinelFederation"

Named HttpClient used for token-endpoint, JWKS and discovery fetches, so hosts can configure it (proxy, timeouts) by name.

SentinelPasskeyServiceCollectionExtensions

public static class SentinelPasskeyServiceCollectionExtensions

Methods

static IServiceCollection AddSentinelPasskeys(this IServiceCollection services, Action<SentinelPasskeyOptions> configure)

Registers the passkey/WebAuthn stack: SentinelPasskeyOptions, the ceremony service, and the framework-free login completer. Additive and idempotent like AddSentinel. What is NOT defaulted: IPasskeyStore — passkeys are identity data, and the same footgun rationale as the user/session stores applies: register a store adapter or opt into InMemoryPasskeyStore explicitly. IChallengeStore gets the in-memory single-node default (hot state, same posture as the rate counters); fleet deployments register the ValKey adapter.

Nuvora.Nexus.Sentinel.AspNetCore.Endpoints

AddDomainRequest

public sealed record AddDomainRequest : IEquatable<AddDomainRequest>

Kind: “emailDomain” (default) or “subdomain”.

Constructors

AddDomainRequest(string? Domain, string? Kind = null, bool Verified = false)

Kind: “emailDomain” (default) or “subdomain”.

Properties

bool Verified { get; init; }
string? Domain { get; init; }
string? Kind { get; init; }

AdminPageResponse<T>

public sealed record AdminPageResponse<T> : IEquatable<AdminPageResponse<T>>

Defines a generalized method that a value type or class implements to create a type-specific method for determining equality of instances.

Constructors

AdminPageResponse(IReadOnlyList<T> Items, int Offset, int Limit, int Total)

Properties

IReadOnlyList<T> Items { get; init; }
int Limit { get; init; }
int Offset { get; init; }
int Total { get; init; }

AdminUserView

public sealed record AdminUserView : IEquatable<AdminUserView>

Defines a generalized method that a value type or class implements to create a type-specific method for determining equality of instances.

Constructors

AdminUserView(Guid Id, string Email, string? DisplayName, string Status, bool EmailVerified, DateTimeOffset CreatedAt, DateTimeOffset? LastLoginAt)

Properties

DateTimeOffset CreatedAt { get; init; }
DateTimeOffset? LastLoginAt { get; init; }
Guid Id { get; init; }
bool EmailVerified { get; init; }
string Email { get; init; }
string Status { get; init; }
string? DisplayName { get; init; }

ApproveImpersonationRequest

public sealed record ApproveImpersonationRequest : IEquatable<ApproveImpersonationRequest>

Defines a generalized method that a value type or class implements to create a type-specific method for determining equality of instances.

Constructors

ApproveImpersonationRequest(string? Token)

Properties

string? Token { get; init; }

AssignRoleRequest

public sealed record AssignRoleRequest : IEquatable<AssignRoleRequest>

Defines a generalized method that a value type or class implements to create a type-specific method for determining equality of instances.

Constructors

AssignRoleRequest(Guid? UserId, Guid? OrganizationId)

Properties

Guid? OrganizationId { get; init; }
Guid? UserId { get; init; }

AuditEntryView

public sealed record AuditEntryView : IEquatable<AuditEntryView>

Defines a generalized method that a value type or class implements to create a type-specific method for determining equality of instances.

Constructors

AuditEntryView(long Sequence, Guid ActorId, string ActorKind, string Action, string TargetKind, Guid? TargetId, Guid? OrganizationId, string? Before, string? After, DateTimeOffset OccurredAt, string PreviousHash, string EntryHash)

Properties

DateTimeOffset OccurredAt { get; init; }
Guid ActorId { get; init; }
Guid? OrganizationId { get; init; }
Guid? TargetId { get; init; }
long Sequence { get; init; }
string Action { get; init; }
string ActorKind { get; init; }
string EntryHash { get; init; }
string PreviousHash { get; init; }
string TargetKind { get; init; }
string? After { get; init; }
string? Before { get; init; }

AuditPageResponse

public sealed record AuditPageResponse : IEquatable<AuditPageResponse>

Defines a generalized method that a value type or class implements to create a type-specific method for determining equality of instances.

Constructors

AuditPageResponse(IReadOnlyList<AuditEntryView> Entries, bool ChainIntact, long? FirstBrokenSequence)

Properties

IReadOnlyList<AuditEntryView> Entries { get; init; }
bool ChainIntact { get; init; }
long? FirstBrokenSequence { get; init; }

BreakGlassStatusView

public sealed record BreakGlassStatusView : IEquatable<BreakGlassStatusView>

Defines a generalized method that a value type or class implements to create a type-specific method for determining equality of instances.

Constructors

BreakGlassStatusView(Guid RealmId, DateTimeOffset? LastDrillAt, DateTimeOffset? LastUseAt, bool DrillStale, int DrillIntervalDays)

Properties

DateTimeOffset? LastDrillAt { get; init; }
DateTimeOffset? LastUseAt { get; init; }
Guid RealmId { get; init; }
bool DrillStale { get; init; }
int DrillIntervalDays { get; init; }

CreateOrganizationRequest

public sealed record CreateOrganizationRequest : IEquatable<CreateOrganizationRequest>

Defines a generalized method that a value type or class implements to create a type-specific method for determining equality of instances.

Constructors

CreateOrganizationRequest(string? Key, string? DisplayName)

Properties

string? DisplayName { get; init; }
string? Key { get; init; }

CreateRealmRequest

public sealed record CreateRealmRequest : IEquatable<CreateRealmRequest>

Defines a generalized method that a value type or class implements to create a type-specific method for determining equality of instances.

Constructors

CreateRealmRequest(string? Key, string? DisplayName, bool IsDefault = false)

Properties

bool IsDefault { get; init; }
string? DisplayName { get; init; }
string? Key { get; init; }

CreateRoleRequest

public sealed record CreateRoleRequest : IEquatable<CreateRoleRequest>

Defines a generalized method that a value type or class implements to create a type-specific method for determining equality of instances.

Constructors

CreateRoleRequest(Guid? OrganizationId, string? Key, string? DisplayName, List<GrantRequest>? Grants)

Properties

Guid? OrganizationId { get; init; }
List<GrantRequest>? Grants { get; init; }
string? DisplayName { get; init; }
string? Key { get; init; }

CreateWebhookEndpointRequest

public sealed record CreateWebhookEndpointRequest : IEquatable<CreateWebhookEndpointRequest>

Defines a generalized method that a value type or class implements to create a type-specific method for determining equality of instances.

Constructors

CreateWebhookEndpointRequest(Guid? OrganizationId, string? Url, List<string>? EventKinds)

Properties

Guid? OrganizationId { get; init; }
List<string>? EventKinds { get; init; }
string? Url { get; init; }

DomainView

public sealed record DomainView : IEquatable<DomainView>

Defines a generalized method that a value type or class implements to create a type-specific method for determining equality of instances.

Constructors

DomainView(Guid Id, Guid OrganizationId, string Value, string Kind, bool Verified)

Properties

Guid Id { get; init; }
Guid OrganizationId { get; init; }
bool Verified { get; init; }
string Kind { get; init; }
string Value { get; init; }

EraseUserRequest

public sealed record EraseUserRequest : IEquatable<EraseUserRequest>

Erasure confirmation body: the literal string "erase", so the irreversible call can never be a stray POST.

Constructors

EraseUserRequest(string? Confirm)

Erasure confirmation body: the literal string "erase", so the irreversible call can never be a stray POST.

Properties

string? Confirm { get; init; }

FederationDiscoverRequest

public sealed record FederationDiscoverRequest : IEquatable<FederationDiscoverRequest>

Defines a generalized method that a value type or class implements to create a type-specific method for determining equality of instances.

Constructors

FederationDiscoverRequest(string? Email)

Properties

string? Email { get; init; }

FederationDiscoverResponse

public sealed record FederationDiscoverResponse : IEquatable<FederationDiscoverResponse>

Discovery hints. ALWAYS 200 with possibly-null fields: an unknown domain, an unrouted one, and a malformed email are indistinguishable on the wire — the endpoint reads only the org-domain registry, never the user table, so it cannot leak who exists.

Constructors

FederationDiscoverResponse(Guid? OrganizationId, string? IdentityProviderKey)

Discovery hints. ALWAYS 200 with possibly-null fields: an unknown domain, an unrouted one, and a malformed email are indistinguishable on the wire — the endpoint reads only the org-domain registry, never the user table, so it cannot leak who exists.

Properties

Guid? OrganizationId { get; init; }
string? IdentityProviderKey { get; init; }

GrantRequest

public sealed record GrantRequest : IEquatable<GrantRequest>

Defines a generalized method that a value type or class implements to create a type-specific method for determining equality of instances.

Constructors

GrantRequest(string? Pattern, string? Effect, Guid? OrganizationId, List<Guid>? TeamIds, string? ConditionJson)

Properties

Guid? OrganizationId { get; init; }
List<Guid>? TeamIds { get; init; }
string? ConditionJson { get; init; }
string? Effect { get; init; }
string? Pattern { get; init; }

GrantView

public sealed record GrantView : IEquatable<GrantView>

Defines a generalized method that a value type or class implements to create a type-specific method for determining equality of instances.

Constructors

GrantView(Guid Id, string Pattern, string Effect, Guid? OrganizationId, IReadOnlyList<Guid>? TeamIds, string? ConditionJson)

Properties

Guid Id { get; init; }
Guid? OrganizationId { get; init; }
IReadOnlyList<Guid>? TeamIds { get; init; }
string Effect { get; init; }
string Pattern { get; init; }
string? ConditionJson { get; init; }

ImpersonationBanner

public sealed record ImpersonationBanner : IEquatable<ImpersonationBanner>

The impersonation banner contract: present on /profile/me exactly when the request rides an impersonation token. Clients render “you are being impersonated / you are impersonating” off this block — actorId is the impersonating admin, expiresAt the time-box end (null only if the impersonation record is no longer resolvable).

Constructors

ImpersonationBanner(Guid ActorId, DateTimeOffset? ExpiresAt)

The impersonation banner contract: present on /profile/me exactly when the request rides an impersonation token. Clients render “you are being impersonated / you are impersonating” off this block — actorId is the impersonating admin, expiresAt the time-box end (null only if the impersonation record is no longer resolvable).

Properties

DateTimeOffset? ExpiresAt { get; init; }
Guid ActorId { get; init; }

ImpersonationResponse

public sealed record ImpersonationResponse : IEquatable<ImpersonationResponse>

AccessToken is the impersonation access token; null while consent is pending.

Constructors

ImpersonationResponse(ImpersonationView Impersonation, string? AccessToken)

AccessToken is the impersonation access token; null while consent is pending.

Properties

ImpersonationView Impersonation { get; init; }
string? AccessToken { get; init; }

ImpersonationView

public sealed record ImpersonationView : IEquatable<ImpersonationView>

Defines a generalized method that a value type or class implements to create a type-specific method for determining equality of instances.

Constructors

ImpersonationView(Guid Id, Guid ActorUserId, Guid TargetUserId, Guid? OrganizationId, string Reason, string Status, DateTimeOffset? StartedAt, DateTimeOffset ExpiresAt, DateTimeOffset? EndedAt)

Properties

DateTimeOffset ExpiresAt { get; init; }
DateTimeOffset? EndedAt { get; init; }
DateTimeOffset? StartedAt { get; init; }
Guid ActorUserId { get; init; }
Guid Id { get; init; }
Guid TargetUserId { get; init; }
Guid? OrganizationId { get; init; }
string Reason { get; init; }
string Status { get; init; }

InspectRequest

public sealed record InspectRequest : IEquatable<InspectRequest>

Defines a generalized method that a value type or class implements to create a type-specific method for determining equality of instances.

Constructors

InspectRequest(Guid? SubjectId, Guid? OrganizationId, string? Permission, Guid? ResourceOrgId, Guid? ResourceOwnerId, List<Guid>? Teams, Dictionary<string, JsonElement>? Attributes)

Properties

Dictionary<string, JsonElement>? Attributes { get; init; }
Guid? OrganizationId { get; init; }
Guid? ResourceOrgId { get; init; }
Guid? ResourceOwnerId { get; init; }
Guid? SubjectId { get; init; }
List<Guid>? Teams { get; init; }
string? Permission { get; init; }

InvitationAcceptRequest

public sealed record InvitationAcceptRequest : IEquatable<InvitationAcceptRequest>

Invitation acceptance: the single-use invite token plus the invitee’s chosen first password.

Constructors

InvitationAcceptRequest(string? Token, string? Password)

Invitation acceptance: the single-use invite token plus the invitee’s chosen first password.

Properties

string? Password { get; init; }
string? Token { get; init; }

InvitationView

public sealed record InvitationView : IEquatable<InvitationView>

Defines a generalized method that a value type or class implements to create a type-specific method for determining equality of instances.

Constructors

InvitationView(Guid UserId, string Token, DateTimeOffset ExpiresAt, string AcceptPath)

Properties

DateTimeOffset ExpiresAt { get; init; }
Guid UserId { get; init; }
string AcceptPath { get; init; }
string Token { get; init; }

LoginRequest

public sealed record LoginRequest : IEquatable<LoginRequest>

DeviceFingerprint is the client’s opaque device hash for the adaptive-risk signals; CaptchaToken carries a solved challenge when a previous attempt answered 429 captcha_required. Both optional and additive — pre-existing clients are untouched.

Constructors

LoginRequest(string? Email, string? Password, Guid? OrganizationId, string? DeviceFingerprint = null, string? CaptchaToken = null)

DeviceFingerprint is the client’s opaque device hash for the adaptive-risk signals; CaptchaToken carries a solved challenge when a previous attempt answered 429 captcha_required. Both optional and additive — pre-existing clients are untouched.

Properties

Guid? OrganizationId { get; init; }
string? CaptchaToken { get; init; }
string? DeviceFingerprint { get; init; }
string? Email { get; init; }
string? Password { get; init; }

MeResponse

public sealed record MeResponse : IEquatable<MeResponse>

Defines a generalized method that a value type or class implements to create a type-specific method for determining equality of instances.

Constructors

MeResponse(Guid Id, string Email, string? DisplayName, bool EmailVerified, IReadOnlyList<Guid> OrganizationIds, ImpersonationBanner? Impersonation = null)

Properties

Guid Id { get; init; }
IReadOnlyList<Guid> OrganizationIds { get; init; }
ImpersonationBanner? Impersonation { get; init; }
bool EmailVerified { get; init; }
string Email { get; init; }
string? DisplayName { get; init; }

MfaRequiredResponse

public sealed record MfaRequiredResponse : IEquatable<MfaRequiredResponse>

First factor passed, second factor pending. Same 200 status as success so proxies/logs can’t fingerprint accounts by MFA enrollment. Factor tells the client what to render: "totp" (authenticator/recovery input) or "email_otp" (a code was mailed — risk step-up fallback).

Constructors

MfaRequiredResponse(string Status, string MfaPendingToken, string? Factor = null)

First factor passed, second factor pending. Same 200 status as success so proxies/logs can’t fingerprint accounts by MFA enrollment. Factor tells the client what to render: "totp" (authenticator/recovery input) or "email_otp" (a code was mailed — risk step-up fallback).

Properties

string MfaPendingToken { get; init; }
string Status { get; init; }
string? Factor { get; init; }

MfaVerifyRequest

public sealed record MfaVerifyRequest : IEquatable<MfaVerifyRequest>

Kind selects the second factor: "totp", "recovery" or "email_otp".

Constructors

MfaVerifyRequest(string? MfaPendingToken, string? Code, string? Kind, string? DeviceFingerprint = null)

Kind selects the second factor: "totp", "recovery" or "email_otp".

Properties

string? Code { get; init; }
string? DeviceFingerprint { get; init; }
string? Kind { get; init; }
string? MfaPendingToken { get; init; }

OidcClientCreatedView

public sealed record OidcClientCreatedView : IEquatable<OidcClientCreatedView>

Secret is present exactly once — on create/rotate of a confidential client.

Constructors

OidcClientCreatedView(OidcClientView Client, string? Secret)

Secret is present exactly once — on create/rotate of a confidential client.

Properties

OidcClientView Client { get; init; }
string? Secret { get; init; }

OidcClientRequest

public sealed record OidcClientRequest : IEquatable<OidcClientRequest>

Type: “confidential” (default) or “public”; Status: “active” (default) or “suspended”.

Constructors

OidcClientRequest(string? ClientId, string? Type, List<string>? RedirectUris = null, List<string>? PostLogoutRedirectUris = null, List<string>? AllowedScopes = null, string? Audience = null, bool RequireConsent = false, bool FirstParty = false, double? AccessTokenLifetimeSeconds = null, string? BackChannelLogoutUri = null, string? Status = null)

Type: “confidential” (default) or “public”; Status: “active” (default) or “suspended”.

Properties

List<string>? AllowedScopes { get; init; }
List<string>? PostLogoutRedirectUris { get; init; }
List<string>? RedirectUris { get; init; }
bool FirstParty { get; init; }
bool RequireConsent { get; init; }
double? AccessTokenLifetimeSeconds { get; init; }
string? Audience { get; init; }
string? BackChannelLogoutUri { get; init; }
string? ClientId { get; init; }
string? Status { get; init; }
string? Type { get; init; }

OidcClientView

public sealed record OidcClientView : IEquatable<OidcClientView>

Defines a generalized method that a value type or class implements to create a type-specific method for determining equality of instances.

Constructors

OidcClientView(Guid Id, string ClientId, string Type, IReadOnlyList<string> RedirectUris, IReadOnlyList<string> PostLogoutRedirectUris, IReadOnlyList<string> AllowedScopes, string? Audience, bool RequireConsent, bool FirstParty, double? AccessTokenLifetimeSeconds, string? BackChannelLogoutUri, string Status, bool HasSecret, DateTimeOffset CreatedAt)

Properties

DateTimeOffset CreatedAt { get; init; }
Guid Id { get; init; }
IReadOnlyList<string> AllowedScopes { get; init; }
IReadOnlyList<string> PostLogoutRedirectUris { get; init; }
IReadOnlyList<string> RedirectUris { get; init; }
bool FirstParty { get; init; }
bool HasSecret { get; init; }
bool RequireConsent { get; init; }
double? AccessTokenLifetimeSeconds { get; init; }
string ClientId { get; init; }
string Status { get; init; }
string Type { get; init; }
string? Audience { get; init; }
string? BackChannelLogoutUri { get; init; }

OrgSwitchRequest

public sealed record OrgSwitchRequest : IEquatable<OrgSwitchRequest>

Org switch: the organization to mint the next token context for.

Constructors

OrgSwitchRequest(Guid? OrganizationId)

Org switch: the organization to mint the next token context for.

Properties

Guid? OrganizationId { get; init; }

OrganizationView

public sealed record OrganizationView : IEquatable<OrganizationView>

Defines a generalized method that a value type or class implements to create a type-specific method for determining equality of instances.

Constructors

OrganizationView(Guid Id, string Key, string DisplayName, string Status, DateTimeOffset CreatedAt)

Properties

DateTimeOffset CreatedAt { get; init; }
Guid Id { get; init; }
string DisplayName { get; init; }
string Key { get; init; }
string Status { get; init; }

PasskeyCredentialView

public sealed record PasskeyCredentialView : IEquatable<PasskeyCredentialView>

Defines a generalized method that a value type or class implements to create a type-specific method for determining equality of instances.

Constructors

PasskeyCredentialView(Guid Id, string CredentialId, string? Label, bool UvCapable, DateTimeOffset CreatedAt, DateTimeOffset? LastUsedAt)

Properties

DateTimeOffset CreatedAt { get; init; }
DateTimeOffset? LastUsedAt { get; init; }
Guid Id { get; init; }
bool UvCapable { get; init; }
string CredentialId { get; init; }
string? Label { get; init; }

PasskeyLoginRequest

public sealed record PasskeyLoginRequest : IEquatable<PasskeyLoginRequest>

Defines a generalized method that a value type or class implements to create a type-specific method for determining equality of instances.

Constructors

PasskeyLoginRequest(string? CeremonyId, JsonElement? Response, Guid? OrganizationId)

Properties

Guid? OrganizationId { get; init; }
JsonElement? Response { get; init; }
string? CeremonyId { get; init; }

PasskeyMfaVerifyRequest

public sealed record PasskeyMfaVerifyRequest : IEquatable<PasskeyMfaVerifyRequest>

Passkey as second factor: the pending token from the password login plus an assertion.

Constructors

PasskeyMfaVerifyRequest(string? MfaPendingToken, string? CeremonyId, JsonElement? Response)

Passkey as second factor: the pending token from the password login plus an assertion.

Properties

JsonElement? Response { get; init; }
string? CeremonyId { get; init; }
string? MfaPendingToken { get; init; }

PasskeyOptionsResponse

public sealed record PasskeyOptionsResponse : IEquatable<PasskeyOptionsResponse>

Ceremony start response: pass Options to the WebAuthn API, return CeremonyId with the result.

Constructors

PasskeyOptionsResponse(string CeremonyId, JsonElement Options)

Ceremony start response: pass Options to the WebAuthn API, return CeremonyId with the result.

Properties

JsonElement Options { get; init; }
string CeremonyId { get; init; }

PasskeyRegisterRequest

public sealed record PasskeyRegisterRequest : IEquatable<PasskeyRegisterRequest>

Defines a generalized method that a value type or class implements to create a type-specific method for determining equality of instances.

Constructors

PasskeyRegisterRequest(string? CeremonyId, string? Label, JsonElement? Response)

Properties

JsonElement? Response { get; init; }
string? CeremonyId { get; init; }
string? Label { get; init; }

PasskeyRegisteredResponse

public sealed record PasskeyRegisteredResponse : IEquatable<PasskeyRegisteredResponse>

Defines a generalized method that a value type or class implements to create a type-specific method for determining equality of instances.

Constructors

PasskeyRegisteredResponse(string Status, Guid Id, string CredentialId, bool UvCapable, string? Label)

Properties

Guid Id { get; init; }
bool UvCapable { get; init; }
string CredentialId { get; init; }
string Status { get; init; }
string? Label { get; init; }

PatternView

public sealed record PatternView : IEquatable<PatternView>

Defines a generalized method that a value type or class implements to create a type-specific method for determining equality of instances.

Constructors

PatternView(string Pattern, string Effect)

Properties

string Effect { get; init; }
string Pattern { get; init; }

PermissionsResponse

public sealed record PermissionsResponse : IEquatable<PermissionsResponse>

Redacted client view of the permission snapshot: allow patterns and team ids only.

Constructors

PermissionsResponse(IReadOnlyList<PatternView> Patterns, IReadOnlyList<Guid> Teams)

Redacted client view of the permission snapshot: allow patterns and team ids only.

Properties

IReadOnlyList<Guid> Teams { get; init; }
IReadOnlyList<PatternView> Patterns { get; init; }

RealmView

public sealed record RealmView : IEquatable<RealmView>

Defines a generalized method that a value type or class implements to create a type-specific method for determining equality of instances.

Constructors

RealmView(Guid Id, string Key, string DisplayName, bool IsDefault, DateTimeOffset CreatedAt)

Properties

DateTimeOffset CreatedAt { get; init; }
Guid Id { get; init; }
bool IsDefault { get; init; }
string DisplayName { get; init; }
string Key { get; init; }

RefreshRequest

public sealed record RefreshRequest : IEquatable<RefreshRequest>

Defines a generalized method that a value type or class implements to create a type-specific method for determining equality of instances.

Constructors

RefreshRequest(string? RefreshToken)

Properties

string? RefreshToken { get; init; }

RoleView

public sealed record RoleView : IEquatable<RoleView>

Defines a generalized method that a value type or class implements to create a type-specific method for determining equality of instances.

Constructors

RoleView(Guid Id, Guid? OrganizationId, string Key, string DisplayName, bool IsBuiltIn, IReadOnlyList<GrantView>? Grants)

Properties

Guid Id { get; init; }
Guid? OrganizationId { get; init; }
IReadOnlyList<GrantView>? Grants { get; init; }
bool IsBuiltIn { get; init; }
string DisplayName { get; init; }
string Key { get; init; }

SentinelAdminEndpoints

public static class SentinelAdminEndpoints

The delegated-administration endpoint group: realm/org/user/role management, the authz inspector, and the audit ledger.

This layer is deliberately thin: it resolves the CALLER’s snapshot (stashed by the authentication handler) and hands every decision to SentinelAdminService — org fencing lives in the domain layer where the evaluator is the single authority, never in routing. A typed AdminDenied maps to 403 problem+json with the stable code admin_scope; missing targets map to 404; validation failures to 400.

Methods

static RouteGroupBuilder MapSentinelAdmin(this IEndpointRouteBuilder endpoints, string prefix = "/sentinel-admin")

Fields

const string AdminScopeErrorCode = "admin_scope"

Stable machine code for admin scope denials; clients switch on it, renaming is breaking.

SentinelAuthEndpoints

public static class SentinelAuthEndpoints

The authentication flow endpoint group: login, MFA step-up, refresh rotation, logout. Mountable and opt-in — hosts call SentinelAuthEndpoints.MapSentinelAuth where they want the group, nothing is auto-mapped.

Methods

static RouteGroupBuilder MapSentinelAuth(this IEndpointRouteBuilder endpoints, string prefix = "/auth")

Maps POST {prefix}/login, {prefix}/mfa/verify, {prefix}/refresh, {prefix}/org/switch, {prefix}/logout, {prefix}/logout-all. Requires the Core login stack in DI (LoginService, RefreshTokenService, AccessTokenMinter, SigningKeyRing, stores, clock) plus AddSentinelAuthentication for the logout endpoints’ principal.

SentinelBreakGlassEndpoints

public static class SentinelBreakGlassEndpoints

The break-glass admin endpoint group: drill status and the drill marker. Both require sentinel:global:manage — which the break-glass account’s own capped grants (sentinel:global:* by default) satisfy, so the drill can be stamped from the break-glass session itself.

Methods

static RouteGroupBuilder MapSentinelBreakGlass(this IEndpointRouteBuilder endpoints, string prefix = "/sentinel-admin/break-glass")

SentinelEndpointPermissions

public static class SentinelEndpointPermissions

The route-discovery surface: attach metadata at mapping time, validate the mounted set at startup.

Methods

static TBuilder WithSentinelPermission<TBuilder>(this TBuilder builder, string permissionId)

Declares the permission guarding this endpoint (or every endpoint of a group). The id is parsed by the permission-id grammar HERE — a malformed id fails at mapping time, not at scan time.

static Task<IReadOnlyList<string>> ValidateSentinelEndpointPermissionsAsync(this IEndpointRouteBuilder endpoints, CancellationToken cancellationToken = default(CancellationToken))

Route discovery: scans every mounted endpoint for SentinelPermissionMetadata and checks the ids against the definition catalog via DefinitionSyncService.ValidateUsageAsync. Call at startup AFTER definition sync and after mapping — an unknown id throws, so the host dies before serving a route whose guard was never published (fail-closed). Returns the distinct ids it validated, for logging.

SentinelFederationEndpoints

public static class SentinelFederationEndpoints

The inbound-federation endpoint group: “login with Google/Entra/Okta” with Sentinel as the relying party. Mountable and opt-in like the other groups; requires AddSentinelFederation plus an IFederatedIdentityStore in DI.

Methods

static RouteGroupBuilder MapSentinelFederation(this IEndpointRouteBuilder endpoints, string prefix = "/auth/idp", string authPrefix = "/auth")

Maps GET {prefix}/{{idpKey}}/start, GET {prefix}/callback, POST {prefix}/discover.

SentinelImpersonationEndpoints

public static class SentinelImpersonationEndpoints

The impersonation endpoint group. Thin like the admin endpoints: the caller’s snapshot goes to ImpersonationService, which owns the admin-scope fencing, the one-active rule, and both audit ledgers. /consent/approve is the one PUBLIC route — the target proves themselves with the single-purpose mailed token, not with a session.

Methods

static RouteGroupBuilder MapSentinelImpersonation(this IEndpointRouteBuilder endpoints, string prefix = "/sentinel-admin/impersonation")

SentinelPasskeyEndpoints

public static class SentinelPasskeyEndpoints

The passkey/WebAuthn endpoint group: registration ceremonies, passwordless first-factor login, passkey-as-second-factor MFA verification, and credential management. Mountable and opt-in like the other groups; requires AddSentinelPasskeys plus an IPasskeyStore in DI.

Methods

static RouteGroupBuilder MapSentinelPasskeys(this IEndpointRouteBuilder endpoints, string prefix = "/auth/passkey", string authPrefix = "/auth")

Maps POST {prefix}/register/options, {prefix}/register, GET+POST {prefix}/login/options, POST {prefix}/login, {prefix}/mfa/verify, GET {prefix}, DELETE {prefix}/{{credentialId}}.

SentinelPermissionMetadata

public sealed record SentinelPermissionMetadata : IEquatable<SentinelPermissionMetadata>

Endpoint metadata declaring “this route is guarded by SentinelPermissionMetadata.PermissionId”. Declarative, not enforcing: authorization stays with the domain layer / endpoint filters — the metadata exists so route discovery can fail the BOOT when a mounted endpoint names a permission id absent from the definition catalog (fail-closed, Relay spirit).

Constructors

SentinelPermissionMetadata(string PermissionId)

Endpoint metadata declaring “this route is guarded by SentinelPermissionMetadata.PermissionId”. Declarative, not enforcing: authorization stays with the domain layer / endpoint filters — the metadata exists so route discovery can fail the BOOT when a mounted endpoint names a permission id absent from the definition catalog (fail-closed, Relay spirit).

Properties

string PermissionId { get; init; }

SentinelPrivacyEndpoints

public static class SentinelPrivacyEndpoints

The privacy/GDPR endpoint group: Art. 20 export and Art. 17 erasure. Both POST (they act, and the export is heavy), both fenced to sentinel:global:manage inside PersonalDataService — this layer only shapes HTTP.

Methods

static RouteGroupBuilder MapSentinelPrivacy(this IEndpointRouteBuilder endpoints, string prefix = "/sentinel-admin/privacy")

SentinelProfileEndpoints

public static class SentinelProfileEndpoints

The self-service profile endpoint group: identity, device/session management, and the redacted permission snapshot. Every endpoint requires an authenticated Sentinel principal — unauthenticated requests get a 401 problem, never a partial response.

Methods

static RouteGroupBuilder MapSentinelProfile(this IEndpointRouteBuilder endpoints, string prefix = "/profile")

SentinelWebhookAdminEndpoints

public static class SentinelWebhookAdminEndpoints

The webhook administration endpoint group: endpoint CRUD, secret rotation, test deliveries, and the delivery/dead-letter log. Thin like SentinelAdminEndpoints: the caller’s snapshot is resolved here, every decision (including the realm/org fencing) belongs to WebhookAdminService.

Methods

static RouteGroupBuilder MapSentinelWebhookAdmin(this IEndpointRouteBuilder endpoints, string prefix = "/sentinel-admin/webhooks")

SessionView

public sealed record SessionView : IEquatable<SessionView>

Defines a generalized method that a value type or class implements to create a type-specific method for determining equality of instances.

Constructors

SessionView(Guid Id, DateTimeOffset CreatedAt, DateTimeOffset LastSeenAt, string? Device, bool Current)

Properties

DateTimeOffset CreatedAt { get; init; }
DateTimeOffset LastSeenAt { get; init; }
Guid Id { get; init; }
bool Current { get; init; }
string? Device { get; init; }

SetDomainVerifiedRequest

public sealed record SetDomainVerifiedRequest : IEquatable<SetDomainVerifiedRequest>

Defines a generalized method that a value type or class implements to create a type-specific method for determining equality of instances.

Constructors

SetDomainVerifiedRequest(bool Verified)

Properties

bool Verified { get; init; }

StartImpersonationRequest

public sealed record StartImpersonationRequest : IEquatable<StartImpersonationRequest>

Defines a generalized method that a value type or class implements to create a type-specific method for determining equality of instances.

Constructors

StartImpersonationRequest(Guid? TargetUserId, string? Reason, Guid? OrganizationId)

Properties

Guid? OrganizationId { get; init; }
Guid? TargetUserId { get; init; }
string? Reason { get; init; }

TokenResponse

public sealed record TokenResponse : IEquatable<TokenResponse>

Success body for /login, /mfa/verify and /refresh. Token properties are omitted (not nulled) in Cookie transport — the whole point of cookie mode is that script never sees tokens.

Constructors

TokenResponse(string Status, string? AccessToken, string? RefreshToken, IReadOnlyList<Guid>? OrganizationIds)

Success body for /login, /mfa/verify and /refresh. Token properties are omitted (not nulled) in Cookie transport — the whole point of cookie mode is that script never sees tokens.

Properties

IReadOnlyList<Guid>? OrganizationIds { get; init; }
string Status { get; init; }
string? AccessToken { get; init; }
string? RefreshToken { get; init; }

UpdateOrganizationRequest

public sealed record UpdateOrganizationRequest : IEquatable<UpdateOrganizationRequest>

Defines a generalized method that a value type or class implements to create a type-specific method for determining equality of instances.

Constructors

UpdateOrganizationRequest(string? DisplayName)

Properties

string? DisplayName { get; init; }

UpdateRealmRequest

public sealed record UpdateRealmRequest : IEquatable<UpdateRealmRequest>

Defines a generalized method that a value type or class implements to create a type-specific method for determining equality of instances.

Constructors

UpdateRealmRequest(string? DisplayName)

Properties

string? DisplayName { get; init; }

UpdateRoleRequest

public sealed record UpdateRoleRequest : IEquatable<UpdateRoleRequest>

Defines a generalized method that a value type or class implements to create a type-specific method for determining equality of instances.

Constructors

UpdateRoleRequest(string? DisplayName)

Properties

string? DisplayName { get; init; }

UpdateWebhookEndpointRequest

public sealed record UpdateWebhookEndpointRequest : IEquatable<UpdateWebhookEndpointRequest>

Patch semantics: omitted (null) members keep their current value; Status is "active" or "disabled".

Constructors

UpdateWebhookEndpointRequest(string? Url, List<string>? EventKinds, string? Status)

Patch semantics: omitted (null) members keep their current value; Status is "active" or "disabled".

Properties

List<string>? EventKinds { get; init; }
string? Status { get; init; }
string? Url { get; init; }

WebhookDeliveryView

public sealed record WebhookDeliveryView : IEquatable<WebhookDeliveryView>

Defines a generalized method that a value type or class implements to create a type-specific method for determining equality of instances.

Constructors

WebhookDeliveryView(Guid Id, Guid EndpointId, string EventKind, string PayloadJson, DateTimeOffset CreatedAt, int AttemptCount, DateTimeOffset NextAttemptAt, DateTimeOffset? DeliveredAt, string? LastError, bool Abandoned)

Properties

DateTimeOffset CreatedAt { get; init; }
DateTimeOffset NextAttemptAt { get; init; }
DateTimeOffset? DeliveredAt { get; init; }
Guid EndpointId { get; init; }
Guid Id { get; init; }
bool Abandoned { get; init; }
int AttemptCount { get; init; }
string EventKind { get; init; }
string PayloadJson { get; init; }
string? LastError { get; init; }

WebhookEndpointCreatedView

public sealed record WebhookEndpointCreatedView : IEquatable<WebhookEndpointCreatedView>

Create response: the endpoint view plus the raw secret, shown this once.

Constructors

WebhookEndpointCreatedView(WebhookEndpointView Endpoint, string Secret)

Create response: the endpoint view plus the raw secret, shown this once.

Properties

WebhookEndpointView Endpoint { get; init; }
string Secret { get; init; }

WebhookEndpointView

public sealed record WebhookEndpointView : IEquatable<WebhookEndpointView>

Defines a generalized method that a value type or class implements to create a type-specific method for determining equality of instances.

Constructors

WebhookEndpointView(Guid Id, Guid? OrganizationId, string Url, IReadOnlyList<string> EventKinds, string Status, int ConsecutiveFailures, DateTimeOffset CreatedAt, DateTimeOffset? DisabledAt, DateTimeOffset? LastSuccessAt, DateTimeOffset? LastFailureAt)

Properties

DateTimeOffset CreatedAt { get; init; }
DateTimeOffset? DisabledAt { get; init; }
DateTimeOffset? LastFailureAt { get; init; }
DateTimeOffset? LastSuccessAt { get; init; }
Guid Id { get; init; }
Guid? OrganizationId { get; init; }
IReadOnlyList<string> EventKinds { get; init; }
int ConsecutiveFailures { get; init; }
string Status { get; init; }
string Url { get; init; }

WebhookSecretView

public sealed record WebhookSecretView : IEquatable<WebhookSecretView>

Rotate response: the new secret, shown this once.

Constructors

WebhookSecretView(Guid EndpointId, string Secret)

Rotate response: the new secret, shown this once.

Properties

Guid EndpointId { get; init; }
string Secret { get; init; }

Nuvora.Nexus.Sentinel.AspNetCore.HealthChecks

SentinelBreakGlassHealthCheck

public sealed class SentinelBreakGlassHealthCheck : IHealthCheck

The break-glass drill health check: DEGRADED when the realm’s break-glass access has not been exercised (drilled) within BreakGlassPolicy.DrillIntervalDays — an emergency door nobody has opened in months must be assumed rusted shut. Healthy otherwise; never Unhealthy (a stale drill is an operational smell, not an outage).

Constructors

SentinelBreakGlassHealthCheck(BreakGlassService breakGlass, IOptions<SentinelAspNetOptions> options)

The break-glass drill health check: DEGRADED when the realm’s break-glass access has not been exercised (drilled) within BreakGlassPolicy.DrillIntervalDays — an emergency door nobody has opened in months must be assumed rusted shut. Healthy otherwise; never Unhealthy (a stale drill is an operational smell, not an outage).

Methods

Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default(CancellationToken))

Runs the health check, returning the status of the component being checked.

SentinelBreakGlassHealthCheckExtensions

public static class SentinelBreakGlassHealthCheckExtensions

Methods

static IHealthChecksBuilder AddSentinelBreakGlassHealthCheck(this IHealthChecksBuilder builder, string name = "sentinel_break_glass")

Registers the break-glass drill health check. Requires AddSentinelBreakGlass() (the check resolves BreakGlassService); the realm checked is SentinelAspNetOptions.DefaultRealmId.

Nuvora.Nexus.Sentinel.AspNetCore.Passkeys

PasskeyAssertionResult

public sealed record PasskeyAssertionResult : IEquatable<PasskeyAssertionResult>

Defines a generalized method that a value type or class implements to create a type-specific method for determining equality of instances.

Constructors

PasskeyAssertionResult(PasskeyAssertionStatus Status, PasskeyCredential? Credential = null, bool UserVerified = false)

Properties

PasskeyAssertionStatus Status { get; init; }
PasskeyCredential? Credential { get; init; }
bool UserVerified { get; init; }

PasskeyAssertionStatus

public enum PasskeyAssertionStatus

Outcome of a verified (or rejected) passkey assertion ceremony.

Values

  • Success
  • SignCountRegression — The signature counter went backwards: the strongest available signal that the credential was cloned. The assertion is rejected and callers emit passkey.signcount_regression.
  • Failed — Unknown ceremony, expired challenge, unknown credential, or failed cryptographic verification — one bucket on purpose (anti-enumeration posture).

PasskeyService

public sealed class PasskeyService

WebAuthn ceremony orchestration over Fido2NetLib. This is the only place in the codebase that touches the FIDO2 protocol — Core holds just the credential entity and the store port.

Challenge round-trip: each ceremony gets a random ceremony id; the SHA-256 of the issued challenge is stored in IChallengeStore under that id (ttl SentinelPasskeyOptions.CeremonyLifetime, single attempt). At completion the challenge is read back out of the client’s clientDataJSON and hash-verified against the store — success proves this exact challenge was server-issued, unexpired, and unused (single-use consumption is the store’s contract), after which the original options are reconstructed deterministically from configuration for Fido2NetLib’s verification. No server-side options blob needs to survive the round-trip, so the existing challenge port works fleet-wide unchanged.

Constructors

PasskeyService(IOptions<SentinelPasskeyOptions> options, IChallengeStore challenges, IPasskeyStore passkeys, ISentinelClock clock)

Methods

ValueTask<(string CeremonyId, AssertionOptions Options)> BeginAssertionAsync(CancellationToken ct = default(CancellationToken))

Starts an assertion ceremony. Usernameless by design (passkeys-first): no allowCredentials, so the browser offers whatever discoverable credentials it holds for the RP and the server learns the user from the verified assertion — the options leak nothing about who exists. The same ceremony feeds both passwordless login and the MFA step-up path.

ValueTask<(string CeremonyId, CredentialCreateOptions Options)> BeginRegistrationAsync(User user, CancellationToken ct = default(CancellationToken))

Starts a registration ceremony for an authenticated user. Existing credentials are excluded so an authenticator can’t double-enroll; resident key is preferred and user verification is preferred — the ceremony records what the authenticator actually did, and first-factor eligibility is decided from that record, not demanded up front (demanding UV would lock out second-factor-only security keys).

ValueTask<PasskeyAssertionResult> CompleteAssertionAsync(string ceremonyId, AuthenticatorAssertionRawResponse response, CancellationToken ct = default(CancellationToken))

Verifies an assertion: challenge round-trip, credential lookup, signature verification, sign-count advance. On success the stored counter and last-used stamp are updated.

ValueTask<PasskeyCredential?> CompleteRegistrationAsync(string ceremonyId, User user, AuthenticatorAttestationRawResponse response, string? label, CancellationToken ct = default(CancellationToken))

Verifies an attestation response and stores the credential. Attestation conveyance is none (the default posture: we authenticate users, not authenticator supply chains), so the AAGUID is informational. Returns null on any verification failure.

SentinelPasskeyOptions

public sealed class SentinelPasskeyOptions

WebAuthn relying-party configuration. Kept as its own small POCO — this is protocol identity (what the browser scopes credentials to), not HTTP plumbing, so it does not fold into SentinelAspNetOptions.

Properties

ISet<string> Origins { get; set; }

Web origins allowed to perform ceremonies (e.g. https://app.example.com). Assertions from any other origin fail verification — this is the phishing-resistance anchor of the whole passkey design, never leave it empty in production.

TimeSpan CeremonyLifetime { get; set; }

How long an issued challenge stays redeemable. Short by design: a ceremony is an interactive browser round-trip, not a durable token.

string RpId { get; set; }

The relying-party id — the registrable domain credentials are scoped to (e.g. example.com). Changing it orphans every previously registered credential, so treat it as immutable once users have enrolled.

string RpName { get; set; }

Human-readable relying-party name shown in authenticator prompts.

Nuvora.Nexus.Sentinel.AspNetCore.Privacy

SentinelRetentionService

public sealed class SentinelRetentionService : BackgroundService

Hosted retention sweep: runs RetentionService.RunOnceAsync on SentinelRetentionOptions.SweepInterval (daily by default), each pass in its own DI scope (the retention store is DbContext-bound in persistent setups). A failing sweep logs and waits for the next tick — retention is idempotent, so missed work is simply picked up later.

Constructors

SentinelRetentionService(IServiceScopeFactory scopeFactory, SentinelRetentionOptions options, ILogger<SentinelRetentionService> logger)

Hosted retention sweep: runs RetentionService.RunOnceAsync on SentinelRetentionOptions.SweepInterval (daily by default), each pass in its own DI scope (the retention store is DbContext-bound in persistent setups). A failing sweep logs and waits for the next tick — retention is idempotent, so missed work is simply picked up later.

Methods

override Task ExecuteAsync(CancellationToken stoppingToken)

This method is called when the IHostedService starts. The implementation should return a task that represents the lifetime of the long running operation(s) being performed.

SentinelRetentionServiceCollectionExtensions

public static class SentinelRetentionServiceCollectionExtensions

Methods

static IServiceCollection AddSentinelRetentionService(this IServiceCollection services)

Registers the daily retention sweep. Requires AddSentinelPrivacy().

Nuvora.Nexus.Sentinel.AspNetCore.Webhooks

SentinelWebhookDispatcherServiceCollectionExtensions

public static class SentinelWebhookDispatcherServiceCollectionExtensions

Registration for the webhook delivery side in ASP.NET Core hosts: the IHttpClientFactory-backed HTTP transport for WebhookDispatcher and the polling hosted service. Call ALONGSIDE the meta package’s AddSentinelWebhooks(), which registers the store, the enqueue sink composition, and the dispatcher itself.

Methods

static IServiceCollection AddSentinelWebhookDispatcher(this IServiceCollection services)

Registers the HTTP transport (TryAdd — a host’s or a test’s own Func<WebhookRequest, CancellationToken, Task<int>> registered earlier wins) and WebhookDispatcherService. Idempotent.

Fields

const string HttpClientName = "sentinel-webhooks"

Named client so hosts can attach their own handlers/policies (proxies, extra telemetry).

WebhookDispatcherService

public sealed class WebhookDispatcherService : BackgroundService

The delivery poll loop: every WebhookDispatcherOptions.PollInterval, opens a service scope (the store may be DbContext-bound), resolves WebhookDispatcher, and runs one claim-and-deliver pass. Multiple nodes can run this concurrently — the claim lease (IWebhookStore.ClaimDueAsync) prevents double-sends. Failures are logged and the loop continues; delivery machinery must never take the host down.

Constructors

WebhookDispatcherService(IServiceScopeFactory scopeFactory, WebhookDispatcherOptions options, ILogger<WebhookDispatcherService> logger)

The delivery poll loop: every WebhookDispatcherOptions.PollInterval, opens a service scope (the store may be DbContext-bound), resolves WebhookDispatcher, and runs one claim-and-deliver pass. Multiple nodes can run this concurrently — the claim lease (IWebhookStore.ClaimDueAsync) prevents double-sends. Failures are logged and the loop continues; delivery machinery must never take the host down.

Methods

override Task ExecuteAsync(CancellationToken stoppingToken)

This method is called when the IHostedService starts. The implementation should return a task that represents the lifetime of the long running operation(s) being performed.