API reference — Protocols

Nuvora.Nexus.Sentinel.Scim

SCIM 2.0 provisioning server for Sentinel: per-organization bearer-token auth, Users AND Groups (fixes the Node version's users-only gap), the standard filter subset, and RFC 7644 PATCH — mounted with MapSentinelScim().

dotnet add package Nuvora.Nexus.Sentinel.Scim

Nuvora.Nexus.Sentinel.Scim

ScimConstants

public static class ScimConstants

Schema URNs and media type of the SCIM 2.0 wire format (RFC 7643, RFC 7644).

Fields

const string ErrorSchema = "urn:ietf:params:scim:api:messages:2.0:Error"
const string GroupSchema = "urn:ietf:params:scim:schemas:core:2.0:Group"
const string ListResponseSchema = "urn:ietf:params:scim:api:messages:2.0:ListResponse"
const string MediaType = "application/scim+json"

RFC 7644 §3.1 media type; responses carry it, and requests may send it or plain application/json.

const string PatchOpSchema = "urn:ietf:params:scim:api:messages:2.0:PatchOp"
const string ResourceTypeSchema = "urn:ietf:params:scim:schemas:core:2.0:ResourceType"
const string SchemaSchema = "urn:ietf:params:scim:schemas:core:2.0:Schema"
const string ServiceProviderConfigSchema = "urn:ietf:params:scim:schemas:core:2.0:ServiceProviderConfig"
const string UserSchema = "urn:ietf:params:scim:schemas:core:2.0:User"

ScimEmail

public sealed record ScimEmail : IEquatable<ScimEmail>

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

Constructors

ScimEmail(string Value, bool Primary, string? Type = null)

Properties

bool Primary { get; init; }
string Value { get; init; }
string? Type { get; init; }

ScimEmailRequest

public sealed class ScimEmailRequest

Properties

bool? Primary { get; set; }
string? Type { get; set; }
string? Value { get; set; }

ScimErrorBody

public sealed record ScimErrorBody : IEquatable<ScimErrorBody>

RFC 7644 §3.12 error shape. ScimErrorBody.Status is the HTTP status as a STRING, per the RFC.

Constructors

ScimErrorBody(IReadOnlyList<string> Schemas, string Status, string? ScimType, string? Detail)

RFC 7644 §3.12 error shape. ScimErrorBody.Status is the HTTP status as a STRING, per the RFC.

Properties

IReadOnlyList<string> Schemas { get; init; }
string Status { get; init; }
string? Detail { get; init; }
string? ScimType { get; init; }

ScimGroupRequest

public sealed class ScimGroupRequest

Properties

List<ScimMemberRequest>? Members { get; set; }
string? DisplayName { get; set; }
string? ExternalId { get; set; }

ScimGroupResource

public sealed record ScimGroupResource : IEquatable<ScimGroupResource>

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

Constructors

ScimGroupResource(IReadOnlyList<string> Schemas, string Id, string DisplayName, IReadOnlyList<ScimMemberRef> Members, ScimMeta Meta)

Properties

IReadOnlyList<ScimMemberRef> Members { get; init; }
IReadOnlyList<string> Schemas { get; init; }
ScimMeta Meta { get; init; }
string DisplayName { get; init; }
string Id { get; init; }

ScimListResponse

public sealed record ScimListResponse : IEquatable<ScimListResponse>

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

Constructors

ScimListResponse(IReadOnlyList<string> Schemas, int TotalResults, int StartIndex, int ItemsPerPage, IReadOnlyList<object> Resources)

Properties

IReadOnlyList<object> Resources { get; init; }
IReadOnlyList<string> Schemas { get; init; }
int ItemsPerPage { get; init; }
int StartIndex { get; init; }
int TotalResults { get; init; }

Methods

static ScimListResponse Of(IReadOnlyList<object> resources, int totalResults, int startIndex)

ScimMemberRef

public sealed record ScimMemberRef : IEquatable<ScimMemberRef>

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

Constructors

ScimMemberRef(string Value, string? Display = null)

Properties

string Value { get; init; }
string? Display { get; init; }

ScimMemberRequest

public sealed class ScimMemberRequest

Properties

string? Display { get; set; }
string? Value { get; set; }

ScimMeta

public sealed record ScimMeta : IEquatable<ScimMeta>

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

Constructors

ScimMeta(string ResourceType, DateTimeOffset Created, DateTimeOffset LastModified, string Location)

Properties

DateTimeOffset Created { get; init; }
DateTimeOffset LastModified { get; init; }
string Location { get; init; }
string ResourceType { get; init; }

ScimName

public sealed record ScimName : IEquatable<ScimName>

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

Constructors

ScimName(string? Formatted, string? GivenName, string? FamilyName)

Properties

string? FamilyName { get; init; }
string? Formatted { get; init; }
string? GivenName { get; init; }

ScimNameRequest

public sealed class ScimNameRequest

Properties

string? FamilyName { get; set; }
string? Formatted { get; set; }
string? GivenName { get; set; }

ScimPatchOperation

public sealed class ScimPatchOperation

Properties

JsonElement Value { get; set; }

Raw JSON: SCIM patch values are polymorphic (bool, string, object, array — and Azure AD sends booleans as “True”/“False” strings).

string? Op { get; set; }
string? Path { get; set; }

ScimPatchRequest

public sealed class ScimPatchRequest

Properties

List<ScimPatchOperation>? Operations { get; set; }
List<string>? Schemas { get; set; }

ScimServerOptions

public sealed class ScimServerOptions

Options for the SCIM 2.0 server surface.

Properties

int MaxPageSize { get; set; }

Hard cap on page size (RFC 7644 §3.4.2.4 and the ServiceProviderConfig filter.maxResults): a request’s count is clamped to this, and it is also the default page size when count is omitted.

string BaseUrl { get; set; }

Path prefix the SCIM endpoint group mounts under. The conventional SCIM base path; combined with the host’s PathBase at request time when building meta.location.

ScimTokenCreated

public sealed record ScimTokenCreated : IEquatable<ScimTokenCreated>

Creation result: ScimTokenCreated.Secret is the full sct_ token, returned ONCE — only its hash is stored (same posture as snt_ API keys).

Constructors

ScimTokenCreated(ScimToken Token, string Secret)

Creation result: ScimTokenCreated.Secret is the full sct_ token, returned ONCE — only its hash is stored (same posture as snt_ API keys).

Properties

ScimToken Token { get; init; }
string Secret { get; init; }

ScimTokenService

public sealed class ScimTokenService

SCIM token lifecycle + authentication. Admin endpoint wiring for mint/revoke arrives with the admin UI wave; until then hosts call this service directly.

Constructors

ScimTokenService(IScimStore store, ISentinelClock clock)

SCIM token lifecycle + authentication. Admin endpoint wiring for mint/revoke arrives with the admin UI wave; until then hosts call this service directly.

Methods

ValueTask RevokeAsync(Guid tokenId, CancellationToken cancellationToken = default(CancellationToken))
ValueTask<ScimToken?> AuthenticateAsync(string secret, CancellationToken cancellationToken = default(CancellationToken))

Null for malformed, unknown, revoked and expired tokens alike — a probing caller learns nothing (anti-enumeration).

ValueTask<ScimTokenCreated> CreateAsync(Guid realmId, Guid organizationId, string label, DateTimeOffset? expiresAt = null, CancellationToken cancellationToken = default(CancellationToken))

Mints a SCIM bearer token for one organization. The returned secret (sct_ + 43 base64url chars = 256 bits of entropy) is shown exactly once; only its SHA-256 lands in the store.

Fields

const string TokenPrefix = "sct_"

SCIM-token prefix: greppable in leaked logs, fingerprintable by secret scanners — same rationale as snt_ API keys.

ScimUserRequest

public sealed class ScimUserRequest

Properties

List<ScimEmailRequest>? Emails { get; set; }
ScimNameRequest? Name { get; set; }
bool? Active { get; set; }

Nullable so “absent” (default true on create) is distinguishable from an explicit false.

string? DisplayName { get; set; }
string? ExternalId { get; set; }
string? UserName { get; set; }

ScimUserResource

public sealed record ScimUserResource : IEquatable<ScimUserResource>

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

Constructors

ScimUserResource(IReadOnlyList<string> Schemas, string Id, string? ExternalId, string UserName, ScimName? Name, string? DisplayName, bool Active, IReadOnlyList<ScimEmail> Emails, ScimMeta Meta)

Properties

IReadOnlyList<ScimEmail> Emails { get; init; }
IReadOnlyList<string> Schemas { get; init; }
ScimMeta Meta { get; init; }
ScimName? Name { get; init; }
bool Active { get; init; }
string Id { get; init; }
string UserName { get; init; }
string? DisplayName { get; init; }
string? ExternalId { get; init; }

SentinelScimEndpoints

public static class SentinelScimEndpoints

The SCIM 2.0 endpoint group: /ServiceProviderConfig, /Schemas, /ResourceTypes, and full Users AND Groups resources (fixing the Node version’s users-only gap) behind per-org sct_ bearer auth.

The RFC subset is deliberate and honest: single-clause eq filters on userName/externalId (Users) and displayName (Groups); PATCH replace on user active/name/emails/displayName/userName/externalId; PATCH add/remove/replace on group members and displayName. Everything else in RFC 7644 answers 501 in the §3.12 error shape — never a silently wrong 200. Bulk, sorting, ETags and attribute projection are not implemented and say so in ServiceProviderConfig.

SCIM DELETE of a user deactivates (soft, UserStatus.Deactivated): user rows anchor audit chains and security events, so provisioning-driven deletes must never destroy them — PII erasure is the crypto-shredding flow, not SCIM’s. Group DELETE is hard: groups are aggregation edges, not audit anchors.

Methods

static RouteGroupBuilder MapSentinelScim(this IEndpointRouteBuilder endpoints, ScimServerOptions? options = null)

Nuvora.Nexus.Sentinel.Scim.DependencyInjection

ScimServiceCollectionExtensions

public static class ScimServiceCollectionExtensions

Registration for the SCIM server, Relay DI style: TryAdd idempotency, working in-memory default for the store port.

Methods

static IServiceCollection AddSentinelScim(this IServiceCollection services, Action<ScimServerOptions>? configure = null)

Registers ScimServerOptions, ScimTokenService and — unless the host registered one (e.g. the EF adapter’s EfScimStore) — the in-memory IScimStore. Pair with MapSentinelScim() on the endpoint builder.