Table of Contents

Orleans.Lattice.Membership.Oidc configuration

This page documents Orleans.Lattice.Membership.Oidc 9.9.0, in the documentation for Orleans.Lattice 9.9.0 (release line 9.9), built 2026-10-04. It is also published as markdown, with every table and list, at configuration.md, and llms.txt lists every page.

The package has one public options type, LatticeOidcAuthenticatorOptions, which configures a single generic OpenID Connect credential authenticator: the authority its discovery document is fetched from, the exact issuer and the audiences it accepts, and the claim names it reads the subject and group membership out of. It is bound per issuer by the AddLatticeOidc registration extension, so a silo can trust several OIDC providers at once alongside the Entra, JWT, and anonymous authenticators.

LatticeOidcAuthenticatorOptions

Bind it through AddLatticeOidc(configure).

Properties

Property Type Default Meaning
Authority string "" (empty) The OpenID Connect authority the discovery document is fetched from, for example https://dev-123456.okta.com/oauth2/default or https://keycloak.example.com/realms/lattice. Must be set. When MetadataAddress is unset the discovery document address is derived from this value.
MetadataAddress string? null The explicit OIDC discovery document address. When it is null or blank it is derived from Authority by appending /.well-known/openid-configuration. Set it explicitly for a provider that publishes its metadata somewhere other than the conventional path.
Issuer string "" (empty) The exact issuer (the token iss claim) this authenticator accepts. Must be set. Matching is ordinal and exact - there is no prefix, wildcard, or catch-all form - so a token from any other issuer is not handled and resolution falls through to the next authenticator.
Audiences IList<string> empty list The audiences accepted (the token aud claim), typically the OAuth client id or an API identifier registered with the provider. Must contain at least one entry: audience validation is always enforced, so an empty list throws at construction rather than silently accepting every audience. Populate the collection in place.
Algorithms IList<string> empty list The token signature algorithms accepted (the JWT header alg). Empty - the default - pins the algorithms the provider advertises in its discovery document's id_token_signing_alg_values_supported. Populate it to pin an explicit, narrower set. Pinning is always enforced: an empty list never means "accept any algorithm", and a provider that advertises no algorithms at all rejects every token. Populate the collection in place.
SubjectClaimTypes IList<string> ["sub"] The claim types, in priority order, read for the subject identifier. The first claim present on the validated token wins, falling back to the standard name-identifier claim (ClaimTypes.NameIdentifier) when none is present. Matched by exact claim name, not by JSON path - a dotted entry such as realm_access.roles matches nothing, because nested JSON objects are not flattened into dotted claim names. Populate the collection in place.
GroupClaimTypes IList<string> ["groups", "roles", "role"] The claim types read for group membership. Every value found across every listed claim type is asserted. Matched by exact claim name, not by JSON path (see SubjectClaimTypes); configure the provider to emit memberships as a top-level claim. Clear the collection to disable token-asserted groups entirely. Populate the collection in place.
SchemeHint string? null Optional scheme hint. When set, a credential whose scheme equals this value (compared case-insensitively) selects this authenticator without the token being parsed. null selects solely by exact issuer match. A credential whose scheme is set but does not match still falls through to the issuer match rather than being declined outright, because the credential bridges stamp the scheme from operator configuration rather than from the caller - so a hint that did not match is not evidence the token belongs elsewhere.
ValidateLifetime bool true Whether to validate the token lifetime (exp / nbf).
ClockSkew TimeSpan 5 minutes The permitted clock skew during lifetime validation.
AutomaticRefreshInterval TimeSpan 12 hours How often the discovered JWKS metadata is proactively refreshed.
RefreshInterval TimeSpan 5 minutes The minimum interval between forced JWKS refreshes.

Methods

Method Returns Meaning
ResolveMetadataAddress() string The OIDC discovery document address: MetadataAddress when it holds a non-blank value, otherwise Authority with /.well-known/openid-configuration appended (any trailing / characters on the authority are trimmed first). A blank MetadataAddress falls through to Authority rather than being used verbatim. Returns an empty string when both are blank, which the authenticator rejects at construction.

OidcClaimNames

The standard OpenID Connect claim names the defaults are built from. Use them instead of string literals when overriding SubjectClaimTypes or GroupClaimTypes.

Constant Value Meaning
Subject "sub" The subject identifier: the locally unique, never-reassigned identifier the provider asserts for the end user.
Groups "groups" The group memberships claim, the de facto convention across Okta, Auth0, Keycloak, and Ping.
Roles "roles" The plural roles claim, emitted by providers that model roles separately from groups.
Role "role" The singular role claim, emitted by providers that repeat a single-valued claim per role.

Validation

Options are validated when the authenticator is first resolved from the container - AddLatticeOidc registers a singleton factory and the factory validates before constructing - so an invalid configuration throws an OptionsValidationException with an aggregated message listing every violation at once. (Registration ordering is checked eagerly: calling AddLatticeOidc before AddLatticeMembership throws from AddLatticeOidc itself.) The rules are:

Rule Failure message contains
Authority must be set Authority
Issuer must be set Issuer
Audiences must contain at least one entry at least one audience
No entry in Audiences may be blank null or empty audience
SubjectClaimTypes must contain at least one entry, none blank SubjectClaimTypes
No entry in GroupClaimTypes may be blank (an empty list is allowed) GroupClaimTypes
No entry in Algorithms may be blank (an empty list is allowed and means "pin from discovery") Algorithms
AutomaticRefreshInterval must be strictly positive AutomaticRefreshInterval
RefreshInterval must be strictly positive RefreshInterval
ClockSkew must not be negative ClockSkew

Worked example

A single Okta issuer whose group claim is namespaced, pinned to RS256 explicitly rather than to whatever the discovery document advertises:

using Orleans.Lattice.Membership;
using Orleans.Lattice.Membership.Oidc;

siloBuilder.AddLatticeMembership();

siloBuilder.AddLatticeOidc(options =>
{
    options.Authority = "https://dev-123456.okta.com/oauth2/default";
    options.Issuer = "https://dev-123456.okta.com/oauth2/default";
    options.Audiences.Add("api://lattice");

    // Narrower than the discovery document: only RS256 is accepted, even if the
    // provider also advertises RS384 or ES256.
    options.Algorithms.Add("RS256");

    // This tenant emits membership under a namespaced claim.
    options.GroupClaimTypes.Clear();
    options.GroupClaimTypes.Add("https://example.com/claims/teams");

    // Tighten lifetime validation for a low-skew fleet.
    options.ClockSkew = TimeSpan.FromSeconds(30);
});

See also