JWT bearer
Use AddJwtAuthentication for an API called by other applications or by a front end that already holds an
access token. Each request carries Authorization: Bearer <token>; the token is validated by the ASP.NET Core
JWT bearer handler and turned into an AppPrincipal. Start with
the overview for the services every scenario registers.
Configuration
AddJwtAuthentication reads the Authentication
section (parameter authenticationSectionName) into JwtAuthenticationSectionOptions,
then the OAuth2 settings section into OAuth2SettingsOption.
appsettings.json
{
"Caching": {
"Default": "Volatile",
"Caches": [
{
"Name": "Volatile",
"Kind": "Memory",
"IsAutoStart": true,
"Settings": { "SizeLimitInMB": 10 }
}
]
},
"Authentication": {
"DefaultAuthority": { "Url": "https://login.example.com/my-tenant/v2.0" },
"OAuth2.Settings": { "Audiences": [ "api://my-api" ] },
"TokenCache": { "CacheName": "Volatile" }
}
}
The OAuth2.Settings section (Authentication:OAuth2.Settings) describes the tokens the API accepts:
| Key | Type | Default | Description |
|---|---|---|---|
Audiences |
string array | empty | Accepted aud values. At least one is required while ValidateAudience is true, otherwise the registration throws ConfigurationException: Audiences field is not defined. |
ValidateAudience |
bool | true |
Set to false to accept tokens without a matching audience (for example a Keycloak realm without an audience mapper). |
Authority |
object | none (the default authority) | Authority given to the JWT bearer handler as Authority. The metadata and signing keys are still read from DefaultAuthority. |
ProviderId |
string | Bootstrap |
Key of the token provider that returns the token of the current request (see Claims and authorization). |
Scopes |
string array | empty | Scopes stored with these settings, for token providers that exchange the token (on-behalf-of). |
The keys of the Authentication section itself:
| Key | Type | Default | Description |
|---|---|---|---|
DefaultAuthority |
object | none (required) | The identity provider, see Configuration. |
OAuth2SettingsSectionPath |
string | Authentication:OAuth2.Settings |
Section holding the OAuth2 settings above. |
ValidateAuthority |
bool | true |
Validates the issuer (iss claim) of the token, see What is validated. |
NameClaimType |
string | name |
Claim type of HttpContext.User.Identity.Name. |
RoleClaimType |
string | role |
Claim type of the roles of the identity. |
CertSecurityKeyPath |
string | none | Section describing a certificate (Store or File, as for the data protection certificate) whose key is used as issuer signing key. |
TokenCacheSectionPath |
string | Authentication:TokenCache |
Token cache options. Optional: when the section does not exist, the default cache is used for 50 minutes. |
ClaimsIdentifierSectionPath |
string | Authentication:ClaimsIdentifier |
Claim types that identify a user. |
ClaimsFillerSectionPath |
string | Authentication:ClaimsMiddleWare:ClaimsFiller |
Options of the extra claims. |
DomainMappingsSectionPath |
string | Authentication:DomainsMapping |
Domain mapping of the user profile. |
ClientTokensSectionPath |
string | Authentication:ClientTokens |
Tokens for outgoing calls, see Client authentication. Optional. |
RemoteSecretSectionPath |
string | Authentication:RemoteSecrets |
Secrets for outgoing calls, see Client authentication. Optional. |
The audience validation is not set on this section: use OAuth2.Settings:ValidateAudience.
The Authentication:TokenCache section is optional: an API that makes no outgoing call can omit it. The extra
claims of the users and the tokens are then cached in the default cache.
Code
using Arc4u.OAuth2.Extensions;
builder.Services.AddJwtAuthentication(builder.Configuration);
builder.Services.AddAuthorization();
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
Register the Arc4u services listed in Code as well.
What is validated
The handler is configured with:
- the metadata of
DefaultAuthority, from which the signing keys are downloaded; ValidAudiencesfromOAuth2.Settings:AudienceswhenValidateAudienceistrue;- the token lifetime (ASP.NET Core default);
ValidateIssuerfromValidateAuthority(trueby default). Whentrue, theissclaim must beDefaultAuthority:Issuerwhen it is set, or theissuerof the authority's metadata otherwise. Whenfalse, a token is accepted when it is signed by one of the keys of the authority's metadata, whatever its issuer.MapInboundClaims = false: claim types keep the names of the token (oid,name,upn, ...).NameClaimTypeandRoleClaimTypefrom the keys of the same name (nameandroleby default), soHttpContext.User.Identity.Nameis thenameclaim of the token.
The token is kept in the BootstrapContext of the identity, whose authentication type is OAuth2.
Caution
With ValidateAuthority: false, the issuer is not validated and the audience check is what ties a token
to your API. Keep ValidateAuthority and ValidateAudience enabled whenever your identity provider allows it.
Responses
StandardBearerEvents answers a failed or missing authentication with a 401 and a
ProblemDetails body (application/problem+json, "status": 401). For an expired token, the
x-token-expired response header holds the expiration date and the detail reads
The token expired on <date>. To change the response, derive from StandardBearerEvents as shown in
Extensibility points.
A principal that is authenticated but does not satisfy a policy gets a 403 from ASP.NET Core.
Common scenarios
Configure the API from code
The AddJwtAuthentication(IConfiguration, Action<JwtAuthenticationOptions>) overload takes the options
directly. The IConfiguration parameter is not read by this overload.
using Arc4u.OAuth2.Extensions;
using Arc4u.OAuth2.Options;
builder.Services.AddTokenCache(options => options.CacheName = "Volatile");
builder.Services.AddClaimsFiller(options => options.LoadClaimsFromClaimsFillerProvider = false);
builder.Services.AddJwtAuthentication(builder.Configuration, options =>
{
options.DefaultAuthority = new AuthorityOptions(new Uri("https://login.example.com/my-tenant/v2.0"), null, null, null);
options.OAuth2SettingsOptions = settings => settings.Audiences.Add("api://my-api");
options.ClaimsIdentifierOptions = identifiers => identifiers.Add("oid");
});
Unlike the configuration overload, this overload does not register the token cache, the claims filler, the domain mapping, the client tokens and the remote secrets: register those you need yourself, as above.
Accept Basic credentials
Some clients (scripts, legacy tools) can only send a user name and a password. The Basic authentication
middleware exchanges them for an access token with the resource owner password grant of your identity
provider, and replaces the Authorization header with Bearer <token> before the JWT bearer handler runs.
The token is cached until one minute before it expires.
{
"Authentication": {
"Basic": {
"Settings": {
"ClientId": "my-client-id",
"Scopes": [ "api://my-api/access" ]
},
"DefaultUpn": "@contoso.com"
}
}
}
using Arc4u.OAuth2.Middleware;
using Arc4u.OAuth2.Token;
using Arc4u.OAuth2.TokenProvider;
builder.Services.AddBasicAuthenticationSettings(builder.Configuration);
builder.Services.AddKeyedTransient<ITokenProvider, CredentialTokenCacheTokenProvider>(CredentialTokenCacheTokenProvider.ProviderName);
builder.Services.AddKeyedSingleton<ICredentialTokenProvider, CredentialTokenProvider>(CredentialTokenProvider.ProviderName);
builder.Services.AddSingleton<ITokenCache, ApplicationCache>();
// ...
app.UseBasicAuthentication();
app.UseAuthentication();
app.UseAuthorization();
| Key | Type | Default | Description |
|---|---|---|---|
Authentication:Basic:Settings:ClientId |
string | none (required) | Client id used for the password grant. |
Authentication:Basic:Settings:Scopes |
string array | none (required) | Scopes requested. |
Authentication:Basic:Settings:ClientSecret |
string | none | Client secret, when the provider requires one for this grant. |
Authentication:Basic:Settings:Authority |
object | the default authority | Another authority for the password grant. |
Authentication:Basic:Settings:ProviderId |
string | Credential |
Key of the token provider that requests the token. |
Authentication:Basic:DefaultUpn |
string | none | Suffix added to a user name that has no domain (user@domain or DOMAIN\user), for example @contoso.com. It must start with @ followed by a domain name. |
Authentication:Basic:Certificates |
object | empty | Header (or query string parameter) names mapped to a certificate (Store or File) that decrypts a user:password pair sent in that header instead of a Basic header. |
AddBasicAuthenticationSettings throws ConfigurationException when the Authentication:Basic section is
missing. When the password grant fails, the request continues with its Basic header and the JWT bearer handler
answers 401.
Caution
The password grant sends the user's password to your API. Many providers (Azure AD B2C, accounts with multi-factor authentication) do not support it. Use it only for clients that cannot do better, over HTTPS.
Troubleshooting
401 with an invalid audience in the response detail
The aud claim of the token is not in OAuth2.Settings:Audiences. Decode the token and add its audience, or
request a token for the right scope.
401 with a signature or metadata error in the logs
The handler could not download the metadata or the token was not signed by the keys it lists. Check that
DefaultAuthority:Url (or MetaDataAddress) is reachable from the server and that the token comes from that
authority. For a private certificate authority, see Custom root CA.
401 on every authenticated request, with a valid token
See also
- Server authentication
- Hybrid
- Claims and authorization
- JwtAuthenticationSectionOptions and OAuth2SettingsOption in the API reference