Added in Unreleased.
AuthorizationServerOptions is the central configuration type for ZeeKayDa.Auth. It controls the issuer identity, published endpoint URLs, and the capability sets advertised in the OpenID Connect discovery document.
Pass an Action<AuthorizationServerOptions> delegate to AddZeeKayDaAuth(...) at service registration time. For step-by-step setup instructions, see Configure ZeeKayDa.Auth. For the discovery document that these options feed, see Discovery endpoint.
Registration
using ZeeKayDa.Auth;
using ZeeKayDa.Auth.AspNetCore.Extensions;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddZeeKayDaAuth(options =>
{
options.Issuer = "https://id.example.com";
});
AddZeeKayDaAuth registers all ZeeKayDa.Auth services and wires ValidateOnStart() so that misconfigured options cause the host to fail fast on startup rather than at request time. It returns a ZeeKayDaAuthBuilder for registering optional features.
Properties
Issuer
| Attribute | Value |
|---|---|
| Type | string? |
| Default | null |
| Required | Yes |
The issuer identifier for this authorization server. Published verbatim as the issuer field in the OpenID Connect discovery document.
The value must be an absolute HTTPS URI with no query string, fragment, or user information. The /.well-known/openid-configuration discovery endpoint is derived from this value.
options.Issuer = "https://id.example.com";
// or, for a path-bearing issuer:
options.Issuer = "https://id.example.com/tenant-a";
Issuer syntax requirements are defined by RFC 8414 §2 and OpenID Connect Discovery 1.0 §1.2.
AllowInsecureIssuer
| Attribute | Value |
|---|---|
| Type | bool |
| Default | false |
| Required | No |
When true, relaxes the HTTPS requirement on Issuer to allow HTTP loopback issuers only. Intended for local development and automated testing only.
Warning: Never set
AllowInsecureIssuer = truein production. An HTTP issuer allows token responses to be intercepted and identity documents to be forged. When this flag is enabled,InsecureIssuerWarningServiceemits a warning viaILoggerat every startup.Request-time enforcement also applies: ZeeKayDa.Auth protocol endpoints reject non-HTTPS non-loopback requests with
421 Misdirected Request. Loopback is determined from the TCP-levelHttpContext.Connection.RemoteIpAddress, not from theHostheader.Reverse-proxy caution: If the application runs behind a reverse proxy with
UseForwardedHeaders()configured to trust an unbounded set of proxies,RemoteIpAddresscan be overwritten from a client-controlledX-Forwarded-Forvalue. An attacker could then forgeX-Forwarded-For: 127.0.0.1to appear loopback. Always scopeKnownProxiesorKnownNetworksto the actual proxy addresses and ensureUseForwardedHeaders()runs beforeapp.UseEndpoints(); never combineAllowInsecureIssuer = truewith a wildcard trusted-proxy configuration.
// Local development only
options.Issuer = "http://localhost:5000";
options.AllowInsecureIssuer = true;
AuthorizationEndpoint
| Attribute | Value |
|---|---|
| Type | AuthorizationEndpointOptions |
| Default | new AuthorizationEndpointOptions() |
| Required | No |
Group for authorization endpoint settings.
AuthorizationEndpoint.Uri overrides the authorization_endpoint value published in the discovery document. When null, ZeeKayDa.Auth derives the URL from Issuer as {issuer}/connect/authorize.
Set this when the URL your clients should use differs from the issuer-derived default — for example, when a reverse proxy rewrites paths under the same authority. The value must be an absolute HTTPS URI without user information or fragment. Query strings are permitted by RFC 6749 Section 3.1. The override must use the same authority as Issuer.
options.AuthorizationEndpoint.Uri = "https://id.example.com/tenant-a/custom/authorize";
AuthorizationEndpoint.CodeChallengeMethodsSupported
| Attribute | Value |
|---|---|
| Type | ICollection<CodeChallengeMethod>? |
| Default | null |
| Required | No |
The PKCE code challenge methods advertised in the discovery document. When null (the default), the code_challenge_methods_supported field is omitted entirely from the discovery document.
⚠️ Do not set this property until PKCE challenge verification is enforced at the token endpoint. Advertising methods the server cannot verify gives clients a false assurance — a PKCE-aware client will include a
code_verifierat the token endpoint, and if the server silently ignores it, an authorization-code interception attack can succeed undetected.
Once PKCE enforcement is implemented, set this property to advertise S256 support:
options.AuthorizationEndpoint.CodeChallengeMethodsSupported = [CodeChallengeMethod.S256];
| Enum value | JSON serialization |
|---|---|
CodeChallengeMethod.S256 | "S256" |
The plain challenge method is intentionally absent from CodeChallengeMethod. RFC 9700 §2.1.1 explicitly prohibits its use: advertising plain would negate PKCE’s security benefit because the challenge is identical to the verifier and provides no protection against interception.
Startup validation rejects a non-null empty collection (e.g. = []), which would publish "code_challenge_methods_supported": [] — advertising PKCE support with no usable method.
Maps to code_challenge_methods_supported as defined in RFC 7636 §4.3 and RFC 8414 §2.
TokenEndpoint
| Attribute | Value |
|---|---|
| Type | TokenEndpointOptions |
| Default | new TokenEndpointOptions() |
| Required | No |
Group for token endpoint settings.
TokenEndpoint.Uri overrides the token_endpoint value published in the discovery document. When null, ZeeKayDa.Auth derives the URL from Issuer as {issuer}/connect/token.
The value must be an absolute HTTPS URI without user information or fragment. The override must use the same authority as Issuer.
options.TokenEndpoint.Uri = "https://id.example.com/tenant-a/custom/token";
JwksEndpoint
| Attribute | Value |
|---|---|
| Type | JwksEndpointOptions |
| Default | new JwksEndpointOptions() |
| Required | No |
Group for JSON Web Key Set endpoint settings.
JwksEndpoint.Uri overrides the jwks_uri value published in the discovery document. When null, ZeeKayDa.Auth derives the URL from Issuer as {issuer}/connect/jwks.
The value must be an absolute HTTPS URI without user information, query, or fragment. The override must use the same authority as Issuer.
options.JwksEndpoint.Uri = "https://id.example.com/tenant-a/custom/jwks";
Response.TypesSupported
| Attribute | Value |
|---|---|
| Type | ICollection<ResponseType> |
| Default | [ResponseType.Code] |
| Required | Yes (must not be null or empty) |
The response types this server supports. Published as response_types_supported in the discovery document. This value lives in the Response options group.
| Enum value | JSON serialization |
|---|---|
ResponseType.Code | "code" |
Hybrid and implicit response types are not supported by ZeeKayDa.Auth. The library intentionally publishes code flow only, aligned with OAuth 2.1 §3.3 and RFC 9700 §2.1.2.
response_types_supported is a required field in the discovery document per OpenID Connect Discovery 1.0 §3.
Response.ModesSupported
| Attribute | Value |
|---|---|
| Type | ICollection<ResponseMode> |
| Default | [ResponseMode.Query] |
| Required | Yes (must not be null) |
The response modes this server supports. Published as response_modes_supported in the discovery document. This value lives in the Response options group.
| Enum value | JSON serialization |
|---|---|
ResponseMode.Query | "query" |
GrantTypesSupported
| Attribute | Value |
|---|---|
| Type | ICollection<GrantType> |
| Default | [GrantType.AuthorizationCode] |
| Required | Yes (must not be null) |
The grant types this server supports. Published as grant_types_supported in the discovery document.
| Enum value | JSON serialization |
|---|---|
GrantType.AuthorizationCode | "authorization_code" |
grant_types_supported is an authorization server metadata field defined by RFC 8414 §2.
TokenEndpoint.AuthMethodsSupported
| Attribute | Value |
|---|---|
| Type | ICollection<string> |
| Default | ["client_secret_basic"] (see TokenEndpointAuthMethods.ClientSecretBasic) |
| Required | Yes (must not be null or empty) |
The client authentication methods supported at the token endpoint. Published as token_endpoint_auth_methods_supported in the discovery document.
Well-known method string constants are available on TokenEndpointAuthMethods. Custom authentication methods (e.g. "tls_client_auth" from RFC 8705) may also be included as plain strings alongside those constants.
⚠️ Note: Every method listed in
AuthMethodsSupported(except"none") must be present in exactly one registeredIClientAuthenticator’sAuthenticationMethods. Advertising a method with no covering authenticator — or with more than one — will be caught by startup validation.
Each entry must be a non-empty, non-whitespace string with no leading or trailing whitespace and no control characters. If GrantTypesSupported includes GrantType.ClientCredentials, the collection must contain at least one method other than TokenEndpointAuthMethods.None ("none").
This cross-group validator rule is defined in ADR 0002 §4 Rule 2 and is grounded in RFC 6749 §4.4 and RFC 9700 §2.6. If violated, startup validation emits:
GrantTypesSupported includes 'client_credentials', which requires confidential clients. TokenEndpoint.AuthMethodsSupported must contain at least one method other than 'none'. See RFC 6749 §4.4 and OAuth 2.0 Security BCP §2.6 (RFC 9700).
TokenEndpointAuthMethods constant | String value |
|---|---|
ClientSecretBasic | "client_secret_basic" |
ClientSecretPost | "client_secret_post" |
None | "none" |
Custom methods not listed above (e.g. "tls_client_auth", "private_key_jwt") are expressed as plain strings alongside these constants.
token_endpoint_auth_methods_supported is defined by RFC 8414 §2.
"none" and PKCE
TokenEndpointAuthMethods.None ("none") represents public clients — clients with no client secret. Public clients cannot securely transmit credentials at the token endpoint.
⚠️ Warning: Public clients MUST use PKCE (Proof Key for Public OAuth 2.0 Clients) as the sole protection mechanism for the authorization code. This is mandated by RFC 9700 §2.1.1 (OAuth 2.0 Security Best Current Practice).
PKCE is defined for the authorization code grant per RFC 7636. Therefore:
- Public clients using the authorization code flow with
"none"must use PKCE and present a validcode_verifierat the token endpoint. "none"may be advertised alongside confidential-client methods such as"client_secret_basic"; this supports deployments that serve both public clients and confidential clients.- Startup validation does not reject
"none"just becauseGrantTypesSupportedomitsGrantType.AuthorizationCode. ADR 0002 rejects only theclient_credentials+none-only combination above. - When the token endpoint is implemented, it must enforce each registered client’s
token_endpoint_auth_methodat request time (tracked by issue #64). Without per-client enforcement, a confidential client could downgrade to public-client behavior by omitting credentials.
Attempting to support ClientCredentials with only public-client authentication will fail at host startup with the ADR 0002 error message shown above.
// ✓ Valid: public clients with authorization code grant + PKCE
options.TokenEndpoint.AuthMethodsSupported = [TokenEndpointAuthMethods.None];
options.GrantTypesSupported = [GrantType.AuthorizationCode];
// ✗ Invalid: client_credentials with only public-client authentication
// This will fail startup validation
options.TokenEndpoint.AuthMethodsSupported = [TokenEndpointAuthMethods.None];
options.GrantTypesSupported = [GrantType.ClientCredentials];
Authorization-code clients that use "none" must perform the token exchange with a PKCE challenge and verifier. Consult your OAuth client library’s documentation for PKCE implementation details.
IdToken.SigningAlgValuesSupported
| Attribute | Value |
|---|---|
| Type | ICollection<SigningAlgorithm> |
| Default | [SigningAlgorithm.RS256] |
| Required | Yes (must not be null or empty) |
The signing algorithms supported for ID tokens. Published as id_token_signing_alg_values_supported in the discovery document.
| Enum value | JSON serialization |
|---|---|
SigningAlgorithm.RS256 | "RS256" |
SigningAlgorithm.RS384 | "RS384" |
SigningAlgorithm.RS512 | "RS512" |
SigningAlgorithm.ES256 | "ES256" |
SigningAlgorithm.ES384 | "ES384" |
SigningAlgorithm.ES512 | "ES512" |
SigningAlgorithm.PS256 | "PS256" |
SigningAlgorithm.PS384 | "PS384" |
SigningAlgorithm.PS512 | "PS512" |
id_token_signing_alg_values_supported is a required field in the discovery document per OpenID Connect Discovery 1.0 §3.
DiscoveryDocument.CacheMaxAgeSeconds
| Attribute | Value |
|---|---|
| Type | int |
| Default | 3600 |
| Required | No |
The max-age value, in seconds, for the discovery endpoint’s Cache-Control header. The default response is:
Cache-Control: public, max-age=3600, must-revalidate
Set the value to 0 to disable public caching:
Cache-Control: no-store
Negative values fail startup validation.
DiscoveryDocument.CorsOrigins
| Attribute | Value |
|---|---|
| Type | IList<string> |
| Default | [] (empty) |
| Required | No |
The list of origins permitted to fetch the discovery document from a browser via CORS. When empty (the default), the endpoint returns Access-Control-Allow-Origin: *. When non-empty, only requests whose Origin header matches an allowlist entry receive an Access-Control-Allow-Origin response header.
Each entry must be an absolute origin in the form scheme://host[:port] with no path, query, fragment, user information, wildcards, or the literal string null. Entries are canonicalized (lowercased), deduplicated, and frozen into an immutable startup snapshot. Invalid entries cause the host to fail fast.
https:// origins are always accepted. http:// origins are rejected unless AllowInsecureIssuer = true; when enabled, HTTP origins must still target loopback hosts only.
options.DiscoveryDocument.CorsOrigins.Add("https://app.example.com");
options.DiscoveryDocument.CorsOrigins.Add("https://admin.example.com");
See Discovery endpoint — CORS configuration for the full CORS behaviour and an OPTIONS preflight note.
SecurityHeaders.ContentTypeOptionsNoSniff
| Attribute | Value |
|---|---|
| Type | bool |
| Default | true |
| Required | No |
When true (the default), every ZeeKayDa.Auth protocol endpoint response includes X-Content-Type-Options: nosniff. Set to false to suppress this header if your application already sets it globally via a security-headers middleware.
SecurityHeaders.ReferrerPolicy
| Attribute | Value |
|---|---|
| Type | ReferrerPolicy |
| Default | ReferrerPolicy.NoReferrer |
| Required | No |
Controls the Referrer-Policy response header emitted by all ZeeKayDa.Auth protocol endpoints. The default no-referrer suppresses the Referer request header entirely, which is appropriate for OAuth/OIDC endpoints that should not leak token URLs to third-party origins.
| Enum value | Header value |
|---|---|
ReferrerPolicy.NoReferrer | no-referrer |
ReferrerPolicy.NoReferrerWhenDowngrade | no-referrer-when-downgrade |
ReferrerPolicy.Origin | origin |
ReferrerPolicy.OriginWhenCrossOrigin | origin-when-cross-origin |
ReferrerPolicy.SameOrigin | same-origin |
ReferrerPolicy.StrictOrigin | strict-origin |
ReferrerPolicy.StrictOriginWhenCrossOrigin | strict-origin-when-cross-origin |
ReferrerPolicy.UnsafeUrl | unsafe-url |
SecurityHeaders.CrossOriginResourcePolicy
| Attribute | Value |
|---|---|
| Type | CrossOriginResourcePolicy |
| Default | CrossOriginResourcePolicy.CrossOrigin |
| Required | No |
Controls the Cross-Origin-Resource-Policy response header emitted by all ZeeKayDa.Auth protocol endpoints. The default cross-origin permits cross-origin subresource fetches (required for browser-based relying parties reading the discovery document). Set to same-origin or same-site only when all relying parties are co-hosted on the same origin or site as the authorization server.
Note: If your application already applies a security-headers middleware that sets
Cross-Origin-Resource-Policy, the header will be duplicated in ZeeKayDa.Auth responses. SinceCrossOriginResourcePolicyis an enum (not a boolean), there is no way to suppress the header entirely. To avoid duplication, configure only one side: either exclude ZeeKayDa.Auth routes from your middleware’s header policy, or rely solely on ZeeKayDa.Auth’s built-in header. For example, with ASP.NET Core’sUseSecurityHeaders()(NWebSec or similar), scope the middleware to non-ZeeKayDa routes only.
| Enum value | Header value |
|---|---|
CrossOriginResourcePolicy.SameSite | same-site |
CrossOriginResourcePolicy.SameOrigin | same-origin |
CrossOriginResourcePolicy.CrossOrigin | cross-origin |
Startup validation
AuthorizationServerOptionsValidator validates AuthorizationServerOptions at host startup via ValidateOnStart(). The host will not start if any rule below is violated.
| Rule | Condition that causes failure |
|---|---|
Issuer is required | Issuer is null, empty, or whitespace |
Issuer must be absolute | Issuer is not an absolute URI |
Issuer must not have a query string | Issuer contains a ? component |
Issuer must not have a fragment | Issuer contains a # component |
Issuer must use HTTPS | Issuer uses HTTP and AllowInsecureIssuer is false |
| HTTP issuer must be loopback | Issuer uses HTTP with a non-loopback host |
Issuer must not have user information | Issuer contains user:password@host userinfo |
Issuer must be canonical | Issuer uses uppercase scheme or host, or explicitly specifies a default port (:443 for HTTPS, :80 for HTTP loopback) |
| Endpoint overrides must be absolute HTTPS URIs | an override is relative, uses an unsupported scheme, or uses HTTP without AllowInsecureIssuer |
| HTTP endpoint overrides must be loopback | an override uses HTTP with a non-loopback host |
| Endpoint overrides must share issuer authority | an override host/port differs from Issuer |
| Endpoint overrides must not have user information | an override contains user:password@host userinfo |
| Endpoint fragments are rejected | AuthorizationEndpoint.Uri, TokenEndpoint.Uri, or JwksEndpoint.Uri contains # |
JwksEndpoint.Uri must not have a query string | JwksEndpoint.Uri contains ? |
Response.TypesSupported is required | Response.TypesSupported is null or empty |
Response.ModesSupported is required | Response.ModesSupported is null |
GrantTypesSupported is required | GrantTypesSupported is null |
TokenEndpoint.AuthMethodsSupported is required | TokenEndpoint.AuthMethodsSupported is null or empty |
client_credentials requires non-none token auth method | GrantTypesSupported includes ClientCredentials and every TokenEndpoint.AuthMethodsSupported value is None |
IdToken.SigningAlgValuesSupported is required | IdToken.SigningAlgValuesSupported is null or empty |
IScopeRepository must include openid | the configured scope repository does not include a scope named openid |
| Cache max-age must not be negative | DiscoveryDocument.CacheMaxAgeSeconds is less than 0 |
AuthorizationEndpoint.CodeChallengeMethodsSupported must not be empty | AuthorizationEndpoint.CodeChallengeMethodsSupported is a non-null empty collection |
| CORS origins must use HTTPS by default | a DiscoveryDocument.CorsOrigins entry uses HTTP while AllowInsecureIssuer is false |
| HTTP CORS origins must be loopback when allowed | a DiscoveryDocument.CorsOrigins entry uses HTTP with a non-loopback host |
SecurityHeaders.ReferrerPolicy must be a defined enum value | SecurityHeaders.ReferrerPolicy is set via an out-of-range cast |
SecurityHeaders.CrossOriginResourcePolicy must be a defined enum value | SecurityHeaders.CrossOriginResourcePolicy is set via an out-of-range cast |
For the exact failure text of the client_credentials + none-only token auth combination, see TokenEndpoint.AuthMethodsSupported above.
Validation errors are reported as OptionsValidationException and prevent the host from starting. They are visible in the startup output and host logs.
Note: Startup validation checks
AuthorizationServerOptionsand verifies thatIScopeRepositoryincludesopenid. Scope repositories still enforce their own validation rules (for example, blank or duplicate scope names).
Related pages
- Configure ZeeKayDa.Auth — step-by-step setup guide
- Configure discovery — how to tune the discovery document
- Configure token stores — step-by-step token store setup
- Token stores — reference for
IAuthorizationCodeStore,IRefreshTokenStore, lifetime options, andZeeKayDaStoreException - Discovery endpoint — full contract for the discovery endpoint