Registrations that cannot work
New in 0.117.0, Duende's configuration validators. A client registration is checked where it is read — every time,
because a store may be a database that answers differently tomorrow — and one that cannot work reads as no client:
the token endpoint answers invalid_client, the authorize endpoint shows its error page. The reason goes to the log and
to InvalidClientConfigurationEvent (an error event; EventsOptions.RaiseErrorEvents). The code on this page is
included from samples/Server.Hosting,
which registers a client its own rule refuses, and a test asks for a token as it.
The default rules
DefaultClientConfigurationValidator refuses
- an OpenID Connect client with no grant type;
- a token lifetime of zero or less, or a negative refresh lifetime;
- an interactive grant and no redirect URI;
- a CORS origin that is not an origin (
https://app.example.com, no path, no trailing slash); - a redirect URI whose scheme
ValidationOptions.InvalidRedirectUriPrefixesrefuses (ValidationOptions); - a secret required but none configured, or client credentials without a secret required.
The messages are Duende's. With automatic key management,
ClientConfigurationValidator also
refuses a client whose access tokens would outlive their signing key — an AccessTokenLifetime beyond
KeyManagementOptions.KeyRetirementAge, the rotation interval plus the retention duration (signing keys).
Clients registered with AddInMemoryClients, AddClientStore<T> or the Dapper configuration store are read through
ValidatingClientStore<T>, and a cached client has been through it
before it is held. A store registered as IClientStore by hand is not, as in Duende.
A rule of your own
Derive from the default and override the check the rule belongs to — each is a protected virtual step:
/// <summary>The default checks, and one of this deployment's own: every client names the team that owns it.</summary>
public sealed class OwnedClientConfigurationValidator(IOptions<IdentityServerOptions> options)
: DefaultClientConfigurationValidator(options)
{
/// <inheritdoc />
protected override Task ValidatePropertiesAsync(ClientConfigurationValidationContext context)
{
if (!context.Client.Properties.TryGetValue("owner", out var owner) || string.IsNullOrWhiteSpace(owner))
{
context.SetError("Every client names its owning team in Properties[\"owner\"].");
}
return Task.CompletedTask;
}
}
// Every client is checked by these rules where it is read; one that breaks a rule reads as no client at all.
identityServer.AddClientConfigurationValidator<OwnedClientConfigurationValidator>();
To turn the checks off, register Duende's NopClientConfigurationValidator the same way.
SAML service providers
SAML service providers are checked the same way since 0.71.0; the check is a seam,
ISamlServiceProviderConfigurationValidator, and a refusal raises
InvalidSamlServiceProviderConfigurationEvent. A blank entity id — whitespace too, since 0.117.0 — is refused. What a
service provider registration must be is on the SAML identity provider page.