Added in Unreleased.
This guide shows how to register ZeeKayDa.Auth in an ASP.NET Core application and configure the minimum required options to get a running authorization server.
For the full list of available options and their validation rules, see AuthorizationServerOptions reference. For next steps after basic setup, see Configure discovery.
Before you start
- Target framework: .NET 10 or later
- NuGet packages:
ZeeKayDa.AuthandZeeKayDa.Auth.AspNetCore
1. Add the minimum viable configuration
Call AddZeeKayDaAuth(...) on IServiceCollection with at least Issuer set, then call app.MapZeeKayDaAuth() to register the endpoints.
using ZeeKayDa.Auth;
using ZeeKayDa.Auth.AspNetCore.Extensions;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddZeeKayDaAuth(options =>
{
options.Issuer = "https://id.example.com";
});
var app = builder.Build();
app.UseRouting();
app.MapZeeKayDaAuth();
app.Run();
Issuer is the only property you must set. All other properties have defaults that are valid for a standard authorization code flow server.
Note:
Issuermust be an absolute HTTPS URI with no query string and no fragment. These requirements come from RFC 8414 §2 and OpenID Connect Discovery 1.0 §1.2.
2. Understand what the defaults give you
With only Issuer configured, ZeeKayDa.Auth registers the following defaults:
| Option | Default value |
|---|---|
Response.TypesSupported | ["code"] |
Response.ModesSupported | ["query"] |
GrantTypesSupported | ["authorization_code"] |
TokenEndpoint.AuthMethodsSupported | ["client_secret_basic"] |
IdToken.SigningAlgValuesSupported | ["RS256"] |
These defaults are a safe starting point for a standard authorization code flow with a confidential client.
3. Understand startup validation
AddZeeKayDaAuth wires ValidateOnStart() so that any misconfiguration causes the host to fail immediately at startup, before it starts accepting requests. You will see an OptionsValidationException in the startup output.
Common startup failures and their causes:
| Failure | Cause |
|---|---|
Issuer validation error | Issuer is not set, not an absolute URI, is not canonical (uppercase scheme/host or explicit default port), uses HTTP without AllowInsecureIssuer, uses HTTP on a non-loopback host, or contains query, fragment, or user information |
Response.TypesSupported validation error | The collection was set to null or emptied |
IdToken.SigningAlgValuesSupported validation error | The collection was set to null or emptied |
| Other collection validation errors | Any of the remaining ICollection properties was set to null |
The full validation rule set is in the AuthorizationServerOptions reference.
4. Use an HTTP issuer for local development
If you are developing locally without TLS, enable AllowInsecureIssuer to allow an HTTP loopback issuer.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddZeeKayDaAuth(options =>
{
options.Issuer = "http://localhost:5000";
options.AllowInsecureIssuer = true;
});
var app = builder.Build();
app.UseRouting();
app.MapZeeKayDaAuth();
app.Run();
When AllowInsecureIssuer = true, ZeeKayDa.Auth emits a warning via ILogger on every startup as a reminder that this setting is active.
Warning: Never set
AllowInsecureIssuer = truein a production environment. It only permits HTTP loopback issuers for local development and tests. HTTP issuers allow token responses to be intercepted and identity documents to be forged. ZeeKayDa.Auth also rejects non-HTTPS non-loopback protocol requests with421 Misdirected Request.
5. Use the builder for optional features
AddZeeKayDaAuth returns a ZeeKayDaAuthBuilder. Use it to register optional features such as custom scope repositories.
var builder = WebApplication.CreateBuilder(args);
var auth = builder.Services.AddZeeKayDaAuth(options =>
{
options.Issuer = "https://id.example.com";
});
// Register optional features on the builder:
// auth.AddInMemoryScopes([...]);
var app = builder.Build();
app.UseRouting();
app.MapZeeKayDaAuth();
app.Run();
💡 Development opt-out for exception message sanitization: Set
AuthorizationServerOptions.Logging.DisableExceptionSanitizingtotrueinappsettings.Development.jsonto turn off the unconditional exception message redaction performed bySecretSanitizingLogger. This is a development-only setting — never enable it in production. See Configure host-level log hygiene for full guidance.
6. Register client secret hashers
Client secrets are hashed before storage using a pluggable IClientSecretHasher. Use AddPbkdf2SecretsHasher() to register the built-in PBKDF2-HMAC-SHA256 hasher in one call:
using ZeeKayDa.Auth.AspNetCore.Extensions;
var auth = builder.Services.AddZeeKayDaAuth(options =>
{
options.Issuer = "https://id.example.com";
});
auth.AddPbkdf2SecretsHasher();
Configure the iteration count
Pass an optional configure delegate to override the default iteration count of 600,000:
auth.AddPbkdf2SecretsHasher(options => options.Iterations = 1_200_000);
Multiple hashers (credential rotation)
When migrating from one algorithm to another, register both hashers and mark the new one as default. The composite verifier dispatches each credential to the correct hasher; isDefault: true controls which hasher creates new secrets:
auth.AddSecretsHasher<Pbkdf2ClientSecretHasher>(isDefault: true); // creates new secrets
auth.AddSecretsHasher<BcryptClientSecretHasher>(isDefault: false); // verifies old secrets
Startup validation fails at host startup if:
- No hashers are registered
- Multiple hashers are registered but zero or more than one has
isDefault: true
For the full Pbkdf2ClientSecretHasherOptions property reference, see Client secrets reference. To implement a custom hasher, see Implement a custom extension point.
7. Enable extended error codes per client (EnableZkdErrorCodes)
EnableZkdErrorCodes is a per-client flag on ClientRegistration (and IClientRegistration). When true, the server may include a zkd_error field in token endpoint error responses for that client, surfacing machine-readable diagnostic codes beyond what RFC 6749 defines.
var customClient = ClientRegistration.CreateConfidential(/* ... */)
with { EnableZkdErrorCodes = true };
Operator guidance
Extended error codes improve diagnostics for legitimate callers, but they give an attacker more signal in aggregate — every additional code is a distinguisher. Follow these guidelines:
- Enable
EnableZkdErrorCodesonly for confidential clients with a demonstrated diagnostic need, such as a trusted first-party backend that requires machine-readable error routing. - Do not enable it for public clients (single-page applications, native apps) or for clients operated by third parties you do not fully trust.
- If you operate a multi-tenant deployment, treat the flag as trusted-tenant-only by default.
Rate limiting is load-bearing for public-client identification
⚠️ Rate limiting is load-bearing for public-client identification. When
IsPublic == true, the framework intentionally accepts a response-timing distinguishability between public and confidential clients. An observer can infer client type from timing differences. Rate limiting on the token endpoint is not optional — it is the primary mitigation for this accepted distinguishability.This residual is documented in ADR 0007 §3.4. Regardless of
EnableZkdErrorCodes, operators must apply rate limiting to the token endpoint. Timing uniformity alone is not sufficient to defeat a sustained enumeration attempt.
zkd_error non-disclosure constraint
Even with EnableZkdErrorCodes = true, the zkd_error value for invalid_client must not distinguish “unknown client_id” from “wrong credential”. The framework enforces this constraint internally; no configuration is required. See ADR 0007 §7 for the binding constraint details.
Next steps
- Configure discovery — customise the OpenID Connect discovery document, override endpoint URLs, and manage published scopes
- AuthorizationServerOptions reference — complete property reference with types, defaults, and validation rules
- Client secrets reference —
Pbkdf2ClientSecretHasherOptionsproperty reference