Running more than one authentication scheme in one application, and choosing which one a given endpoint accepts.
AddSchemeSelector() registers a policy scheme that forwards each request to the scheme matching the credential
it carries, and makes itself the default authenticate and challenge scheme.
var services = new ServiceCollection();
IConfiguration configuration = new ConfigurationManager();
services.AddApiKeyAuthentication()
.AddInMemoryApiKeyAuthentication(configuration.GetSection("Authentication:ApiKeys").ToApiKeyOwners());
services.AddJwtAuthentication(o => configuration.GetSection("Authentication:Jwt").Bind(o))
.AddSchemeSelector(); // last
Call it last. Every Add…Authentication sets its own default scheme, so without it the registration order
decides which handler an unattributed [Authorize] uses — and the symptom is a 401 for a caller holding a
perfectly good credential of the other kind. Rules naming an unregistered scheme are skipped, so the order stops
mattering. No AddAuthorization default policy is needed to name the schemes.
Built-in rules, first match wins: Authorization: Bearer … → the bearer scheme; a non-empty API-key header →
the API-key scheme; the default-named authentication cookie → the cookie scheme (SchemeForwardRules.Cookie(),
skipped when that scheme is not registered). A blank API-key header deliberately does not match, so it cannot
capture a request that the bearer scheme could have served.
SchemeSelectorOptions |
Type | Default | Description |
|---|---|---|---|
AuthenticationScheme |
string |
"Smart" |
Name of the policy scheme |
DisplayName |
string |
(descriptive) | Display name of the policy scheme |
FallbackScheme |
string? |
null |
Scheme for a request with no recognised credential; defaults to the lowest-ordered registered rule |
ChallengeScheme |
string? |
null |
Scheme that answers a challenge; when unset, a registered sign-in scheme (see below) or the forwarding rules decide |
ForwardChallengeToSignInScheme |
bool |
true |
Whether a registered interactive sign-in scheme answers challenges when ChallengeScheme is unset |
Rules |
IList<SchemeForwardRule> |
[] |
Extra rules, added before the built-in ones |
UseDefaultRules |
bool |
true |
Whether to include the bearer, API-key, and cookie rules |
Build rules with SchemeForwardRules.Bearer(scheme), .Basic(scheme), .ApiKey(scheme),
.Cookie(scheme, cookieName), or new SchemeForwardRule(order, scheme, context => …).
Because the policy scheme authenticates nothing, no document transformer declares it — register the operation transformer so guarded operations name the real schemes instead, plus a document transformer per scheme.
Apache License 2.0 — this package contains no license validation and no runtime limits. See LICENSE. A few companion packages are commercially licensed with a free tier; see the licensing overview.