diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml
index 5321d385e2f9..2691385519be 100644
--- a/eng/Version.Details.xml
+++ b/eng/Version.Details.xml
@@ -185,9 +185,9 @@
https://github.com/dotnet/runtime40480e8e82b734a54c210f656361ff073353ffbe
-
+ https://github.com/dotnet/source-build-externals
- 76026f9224bd83ede7b2f494912694a30169c233
+ 844e2cd86e7525d7eb32358e63a0c554187eb26b
diff --git a/eng/Versions.props b/eng/Versions.props
index 17702f1b2145..20e7d209e43a 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -11,6 +11,7 @@
07true
+ 7.0.0-preview
@@ -162,7 +163,7 @@
8.0.0-beta.23361.2
- 8.0.0-alpha.1.23362.1
+ 8.0.0-alpha.1.23368.18.0.0-alpha.1.23362.3
@@ -254,15 +255,15 @@
1.1.2-beta1.22531.11.1.2-beta1.22531.11.0.0-20230414.1
- 6.15.1
- 6.15.1
- 6.15.1
+ $(IdentityModelVersion)
+ $(IdentityModelVersion)
+ $(IdentityModelVersion)2.2.11.0.13.0.13.0.111.1.0
- 6.21.0
+ $(IdentityModelVersion)5.0.05.0.0-alpha.20560.65.0.0
diff --git a/src/Security/Authentication/JwtBearer/src/AuthenticateResults.cs b/src/Security/Authentication/JwtBearer/src/AuthenticateResults.cs
index 5f0e8bb6b914..c66ea3304f1d 100644
--- a/src/Security/Authentication/JwtBearer/src/AuthenticateResults.cs
+++ b/src/Security/Authentication/JwtBearer/src/AuthenticateResults.cs
@@ -6,4 +6,5 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer;
internal static class AuthenticateResults
{
internal static AuthenticateResult ValidatorNotFound = AuthenticateResult.Fail("No SecurityTokenValidator available for token.");
+ internal static AuthenticateResult TokenHandlerUnableToValidate = AuthenticateResult.Fail("No TokenHandler was able to validate the token.");
}
diff --git a/src/Security/Authentication/JwtBearer/src/JwtBearerHandler.cs b/src/Security/Authentication/JwtBearer/src/JwtBearerHandler.cs
index ff4767430d57..c880cb555380 100644
--- a/src/Security/Authentication/JwtBearer/src/JwtBearerHandler.cs
+++ b/src/Security/Authentication/JwtBearer/src/JwtBearerHandler.cs
@@ -9,7 +9,6 @@
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
-using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Microsoft.IdentityModel.Tokens;
using Microsoft.Net.Http.Headers;
@@ -20,8 +19,6 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer;
///
public class JwtBearerHandler : AuthenticationHandler
{
- private OpenIdConnectConfiguration? _configuration;
-
///
/// Initializes a new instance of .
///
@@ -96,79 +93,88 @@ protected override async Task HandleAuthenticateAsync()
}
}
- if (_configuration == null && Options.ConfigurationManager != null)
- {
- _configuration = await Options.ConfigurationManager.GetConfigurationAsync(Context.RequestAborted);
- }
-
- var validationParameters = Options.TokenValidationParameters.Clone();
- if (_configuration != null)
- {
- var issuers = new[] { _configuration.Issuer };
- validationParameters.ValidIssuers = validationParameters.ValidIssuers?.Concat(issuers) ?? issuers;
-
- validationParameters.IssuerSigningKeys = validationParameters.IssuerSigningKeys?.Concat(_configuration.SigningKeys)
- ?? _configuration.SigningKeys;
- }
-
+ var tvp = await SetupTokenValidationParametersAsync();
List? validationFailures = null;
SecurityToken? validatedToken = null;
- foreach (var validator in Options.SecurityTokenValidators)
+ ClaimsPrincipal? principal = null;
+
+ if (!Options.UseSecurityTokenValidators)
{
- if (validator.CanReadToken(token))
+ foreach (var tokenHandler in Options.TokenHandlers)
{
- ClaimsPrincipal principal;
try
{
- principal = validator.ValidateToken(token, validationParameters, out validatedToken);
+ var tokenValidationResult = await tokenHandler.ValidateTokenAsync(token, tvp);
+ if (tokenValidationResult.IsValid)
+ {
+ principal = new ClaimsPrincipal(tokenValidationResult.ClaimsIdentity);
+ validatedToken = tokenValidationResult.SecurityToken;
+ break;
+ }
+ else
+ {
+ validationFailures ??= new List(1);
+ RecordTokenValidationError(tokenValidationResult.Exception ?? new SecurityTokenValidationException($"The TokenHandler: '{tokenHandler}', was unable to validate the Token."), validationFailures);
+ }
}
catch (Exception ex)
{
- Logger.TokenValidationFailed(ex);
-
- // Refresh the configuration for exceptions that may be caused by key rollovers. The user can also request a refresh in the event.
- if (Options.RefreshOnIssuerKeyNotFound && Options.ConfigurationManager != null
- && ex is SecurityTokenSignatureKeyNotFoundException)
+ validationFailures ??= new List(1);
+ RecordTokenValidationError(ex, validationFailures);
+ }
+ }
+ }
+ else
+ {
+#pragma warning disable CS0618 // Type or member is obsolete
+ foreach (var validator in Options.SecurityTokenValidators)
+ {
+ if (validator.CanReadToken(token))
+ {
+ try
{
- Options.ConfigurationManager.RequestRefresh();
+ principal = validator.ValidateToken(token, tvp, out validatedToken);
}
-
- if (validationFailures == null)
+ catch (Exception ex)
{
- validationFailures = new List(1);
+ validationFailures ??= new List(1);
+ RecordTokenValidationError(ex, validationFailures);
+ continue;
}
- validationFailures.Add(ex);
- continue;
}
+ }
+#pragma warning restore CS0618 // Type or member is obsolete
+ }
- Logger.TokenValidationSucceeded();
+ if (principal != null && validatedToken != null)
+ {
+ Logger.TokenValidationSucceeded();
- var tokenValidatedContext = new TokenValidatedContext(Context, Scheme, Options)
- {
- Principal = principal,
- SecurityToken = validatedToken
- };
+ var tokenValidatedContext = new TokenValidatedContext(Context, Scheme, Options)
+ {
+ Principal = principal
+ };
- tokenValidatedContext.Properties.ExpiresUtc = GetSafeDateTime(validatedToken.ValidTo);
- tokenValidatedContext.Properties.IssuedUtc = GetSafeDateTime(validatedToken.ValidFrom);
+ tokenValidatedContext.SecurityToken = validatedToken;
+ tokenValidatedContext.Properties.ExpiresUtc = GetSafeDateTime(validatedToken.ValidTo);
+ tokenValidatedContext.Properties.IssuedUtc = GetSafeDateTime(validatedToken.ValidFrom);
- await Events.TokenValidated(tokenValidatedContext);
- if (tokenValidatedContext.Result != null)
- {
- return tokenValidatedContext.Result;
- }
+ await Events.TokenValidated(tokenValidatedContext);
+ if (tokenValidatedContext.Result != null)
+ {
+ return tokenValidatedContext.Result;
+ }
- if (Options.SaveToken)
+ if (Options.SaveToken)
+ {
+ tokenValidatedContext.Properties.StoreTokens(new[]
{
- tokenValidatedContext.Properties.StoreTokens(new[]
- {
- new AuthenticationToken { Name = "access_token", Value = token }
- });
- }
-
- tokenValidatedContext.Success();
- return tokenValidatedContext.Result!;
+ new AuthenticationToken { Name = "access_token", Value = token }
+ });
}
+
+ tokenValidatedContext.Success();
+ return tokenValidatedContext.Result!;
}
if (validationFailures != null)
@@ -187,6 +193,11 @@ protected override async Task HandleAuthenticateAsync()
return AuthenticateResult.Fail(authenticationFailedContext.Exception);
}
+ if (!Options.UseSecurityTokenValidators)
+ {
+ return AuthenticateResults.TokenHandlerUnableToValidate;
+ }
+
return AuthenticateResults.ValidatorNotFound;
}
catch (Exception ex)
@@ -208,6 +219,47 @@ protected override async Task HandleAuthenticateAsync()
}
}
+ private void RecordTokenValidationError(Exception exception, List exceptions)
+ {
+ if (exception != null)
+ {
+ Logger.TokenValidationFailed(exception);
+ exceptions.Add(exception);
+ }
+
+ // Refresh the configuration for exceptions that may be caused by key rollovers. The user can also request a refresh in the event.
+ // Refreshing on SecurityTokenSignatureKeyNotFound may be redundant if Last-Known-Good is enabled, it won't do much harm, most likely will be a nop.
+ if (Options.RefreshOnIssuerKeyNotFound && Options.ConfigurationManager != null
+ && exception is SecurityTokenSignatureKeyNotFoundException)
+ {
+ Options.ConfigurationManager.RequestRefresh();
+ }
+ }
+
+ private async Task SetupTokenValidationParametersAsync()
+ {
+ // Clone to avoid cross request race conditions for updated configurations.
+ var tokenValidationParameters = Options.TokenValidationParameters.Clone();
+
+ if (Options.ConfigurationManager is BaseConfigurationManager baseConfigurationManager)
+ {
+ tokenValidationParameters.ConfigurationManager = baseConfigurationManager;
+ }
+ else
+ {
+ if (Options.ConfigurationManager != null)
+ {
+ // GetConfigurationAsync has a time interval that must pass before new http request will be issued.
+ var configuration = await Options.ConfigurationManager.GetConfigurationAsync(Context.RequestAborted);
+ var issuers = new[] { configuration.Issuer };
+ tokenValidationParameters.ValidIssuers = (tokenValidationParameters.ValidIssuers == null ? issuers : tokenValidationParameters.ValidIssuers.Concat(issuers));
+ tokenValidationParameters.IssuerSigningKeys = (tokenValidationParameters.IssuerSigningKeys == null ? configuration.SigningKeys : tokenValidationParameters.IssuerSigningKeys.Concat(configuration.SigningKeys));
+ }
+ }
+
+ return tokenValidationParameters;
+ }
+
private static DateTime? GetSafeDateTime(DateTime dateTime)
{
// Assigning DateTime.MinValue or default(DateTime) to a DateTimeOffset when in a UTC+X timezone will throw
diff --git a/src/Security/Authentication/JwtBearer/src/JwtBearerOptions.cs b/src/Security/Authentication/JwtBearer/src/JwtBearerOptions.cs
index db007691f3b9..3ef2893b6fef 100644
--- a/src/Security/Authentication/JwtBearer/src/JwtBearerOptions.cs
+++ b/src/Security/Authentication/JwtBearer/src/JwtBearerOptions.cs
@@ -5,6 +5,7 @@
using System.Net.Http;
using Microsoft.IdentityModel.Protocols;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
+using Microsoft.IdentityModel.JsonWebTokens;
using Microsoft.IdentityModel.Tokens;
namespace Microsoft.AspNetCore.Authentication.JwtBearer;
@@ -15,13 +16,22 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer;
public class JwtBearerOptions : AuthenticationSchemeOptions
{
private readonly JwtSecurityTokenHandler _defaultHandler = new JwtSecurityTokenHandler();
+ private readonly JsonWebTokenHandler _defaultTokenHandler = new JsonWebTokenHandler
+ {
+ MapInboundClaims = JwtSecurityTokenHandler.DefaultMapInboundClaims
+ };
+
+ private bool _mapInboundClaims = JwtSecurityTokenHandler.DefaultMapInboundClaims;
///
/// Initializes a new instance of .
///
public JwtBearerOptions()
{
+#pragma warning disable CS0618 // Type or member is obsolete
SecurityTokenValidators = new List { _defaultHandler };
+#pragma warning restore CS0618 // Type or member is obsolete
+ TokenHandlers = new List { _defaultTokenHandler };
}
///
@@ -103,8 +113,14 @@ public JwtBearerOptions()
///
/// Gets the ordered list of used to validate access tokens.
///
+ [Obsolete("SecurityTokenValidators is no longer used by default. Use TokenHandlers instead. To continue using SecurityTokenValidators, set UseSecurityTokenValidators to true. See https://aka.ms/aspnetcore8/security-token-changes")]
public IList SecurityTokenValidators { get; private set; }
+ ///
+ /// Gets the ordered list of used to validate access tokens.
+ ///
+ public IList TokenHandlers { get; private set; }
+
///
/// Gets or sets the parameters used to validate identity tokens.
///
@@ -126,15 +142,20 @@ public JwtBearerOptions()
public bool IncludeErrorDetails { get; set; } = true;
///
- /// Gets or sets the property on the default instance of in SecurityTokenValidators, which is used when determining
- /// whether or not to map claim types that are extracted when validating a .
+ /// Gets or sets the property on the default instance of in SecurityTokenValidators, or in TokenHandlers which is used when determining
+ /// whether or not to map claim types that are extracted when validating a or a .
/// If this is set to true, the Claim Type is set to the JSON claim 'name' after translating using this mapping. Otherwise, no mapping occurs.
/// The default value is true.
///
public bool MapInboundClaims
{
- get => _defaultHandler.MapInboundClaims;
- set => _defaultHandler.MapInboundClaims = value;
+ get => _mapInboundClaims;
+ set
+ {
+ _mapInboundClaims = value;
+ _defaultHandler.MapInboundClaims = value;
+ _defaultTokenHandler.MapInboundClaims = value;
+ }
}
///
@@ -152,4 +173,15 @@ public bool MapInboundClaims
/// Defaults to .
///
public TimeSpan RefreshInterval { get; set; } = ConfigurationManager.DefaultRefreshInterval;
+
+ ///
+ /// Gets or sets whether or will be used to validate the inbound token.
+ ///
+ ///
+ /// The advantage of using TokenHandlers are:
+ /// There is an Async model.
+ /// The default token handler is a which is faster than a .
+ /// There is an ability to make use of a Last-Known-Good model for metadata that protects applications when metadata is published with errors.
+ ///
+ public bool UseSecurityTokenValidators { get; set; }
}
diff --git a/src/Security/Authentication/JwtBearer/src/PublicAPI.Unshipped.txt b/src/Security/Authentication/JwtBearer/src/PublicAPI.Unshipped.txt
index d9d3a043b473..4626d5e95af8 100644
--- a/src/Security/Authentication/JwtBearer/src/PublicAPI.Unshipped.txt
+++ b/src/Security/Authentication/JwtBearer/src/PublicAPI.Unshipped.txt
@@ -1,2 +1,5 @@
#nullable enable
Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler.JwtBearerHandler(Microsoft.Extensions.Options.IOptionsMonitor! options, Microsoft.Extensions.Logging.ILoggerFactory! logger, System.Text.Encodings.Web.UrlEncoder! encoder) -> void
+Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerOptions.TokenHandlers.get -> System.Collections.Generic.IList!
+Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerOptions.UseSecurityTokenValidators.get -> bool
+Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerOptions.UseSecurityTokenValidators.set -> void
diff --git a/src/Security/Authentication/WsFederation/src/PublicAPI.Unshipped.txt b/src/Security/Authentication/WsFederation/src/PublicAPI.Unshipped.txt
index 6586d9439638..473f251c9c8a 100644
--- a/src/Security/Authentication/WsFederation/src/PublicAPI.Unshipped.txt
+++ b/src/Security/Authentication/WsFederation/src/PublicAPI.Unshipped.txt
@@ -1,2 +1,5 @@
#nullable enable
Microsoft.AspNetCore.Authentication.WsFederation.WsFederationHandler.WsFederationHandler(Microsoft.Extensions.Options.IOptionsMonitor! options, Microsoft.Extensions.Logging.ILoggerFactory! logger, System.Text.Encodings.Web.UrlEncoder! encoder) -> void
+Microsoft.AspNetCore.Authentication.WsFederation.WsFederationOptions.TokenHandlers.get -> System.Collections.Generic.ICollection!
+Microsoft.AspNetCore.Authentication.WsFederation.WsFederationOptions.UseSecurityTokenHandlers.get -> bool
+Microsoft.AspNetCore.Authentication.WsFederation.WsFederationOptions.UseSecurityTokenHandlers.set -> void
diff --git a/src/Security/Authentication/WsFederation/src/Resources.resx b/src/Security/Authentication/WsFederation/src/Resources.resx
index e2edafb671bd..d006fa4ea32e 100644
--- a/src/Security/Authentication/WsFederation/src/Resources.resx
+++ b/src/Security/Authentication/WsFederation/src/Resources.resx
@@ -121,7 +121,7 @@
The service descriptor is missing.
- No token validator was found for the given token.
+ No token validator or token handler was found for the given token.The '{0}' option must be provided.
diff --git a/src/Security/Authentication/WsFederation/src/WsFederationHandler.cs b/src/Security/Authentication/WsFederation/src/WsFederationHandler.cs
index ca52664613ba..3b2594b8137a 100644
--- a/src/Security/Authentication/WsFederation/src/WsFederationHandler.cs
+++ b/src/Security/Authentication/WsFederation/src/WsFederationHandler.cs
@@ -176,6 +176,7 @@ protected override async Task HandleRemoteAuthenticateAsync
return HandleRequestResults.NoMessage;
}
+ List? validationFailures = null;
try
{
// Retrieve our cached redirect uri
@@ -241,42 +242,87 @@ protected override async Task HandleRemoteAuthenticateAsync
wsFederationMessage = securityTokenReceivedContext.ProtocolMessage;
properties = messageReceivedContext.Properties!;
- if (_configuration == null)
+ var tvp = await SetupTokenValidationParametersAsync();
+ ClaimsPrincipal? principal = null;
+ SecurityToken? validatedToken = null;
+ if (!Options.UseSecurityTokenHandlers)
{
- _configuration = await Options.ConfigurationManager.GetConfigurationAsync(Context.RequestAborted);
+ foreach (var tokenHandler in Options.TokenHandlers)
+ {
+ try
+ {
+ var tokenValidationResult = await tokenHandler.ValidateTokenAsync(token, tvp);
+ if (tokenValidationResult.IsValid)
+ {
+ principal = new ClaimsPrincipal(tokenValidationResult.ClaimsIdentity);
+ validatedToken = tokenValidationResult.SecurityToken;
+ break;
+ }
+ else
+ {
+ validationFailures ??= new List(1);
+ Exception exception = tokenValidationResult.Exception ?? new SecurityTokenValidationException($"The TokenHandler: '{tokenHandler}', was unable to validate the Token.");
+ validationFailures.Add(exception);
+ RequestRefresh(exception);
+ }
+ }
+ catch (Exception ex)
+ {
+ validationFailures ??= new List(1);
+ validationFailures.Add(new SecurityTokenValidationException($"TokenHandler: '{tokenHandler}', threw an exception (see inner exception).", ex));
+ RequestRefresh(ex);
+ }
+ }
}
-
- // Copy and augment to avoid cross request race conditions for updated configurations.
- var tvp = Options.TokenValidationParameters.Clone();
- var issuers = new[] { _configuration.Issuer };
- tvp.ValidIssuers = (tvp.ValidIssuers == null ? issuers : tvp.ValidIssuers.Concat(issuers));
- tvp.IssuerSigningKeys = (tvp.IssuerSigningKeys == null ? _configuration.SigningKeys : tvp.IssuerSigningKeys.Concat(_configuration.SigningKeys));
-
- ClaimsPrincipal? principal = null;
- SecurityToken? parsedToken = null;
- foreach (var validator in Options.SecurityTokenHandlers)
+ else
{
- if (validator.CanReadToken(token))
+
+#pragma warning disable CS0618 // Type or member is obsolete
+ foreach (var validator in Options.SecurityTokenHandlers)
{
- principal = validator.ValidateToken(token, tvp, out parsedToken);
- break;
+ if (validator.CanReadToken(token))
+ {
+ try
+ {
+ principal = validator.ValidateToken(token, tvp, out validatedToken);
+ }
+ catch (Exception ex)
+ {
+ validationFailures ??= new List(1);
+ validationFailures.Add(ex);
+ continue;
+ }
+ break;
+ }
}
+#pragma warning restore CS0618 // Type or member is obsolete
}
if (principal == null)
{
- throw new SecurityTokenException(Resources.Exception_NoTokenValidatorFound);
+ if (validationFailures == null || validationFailures.Count == 0)
+ {
+ throw new SecurityTokenException(Resources.Exception_NoTokenValidatorFound);
+ }
+ else if (validationFailures.Count == 1)
+ {
+ throw new SecurityTokenException(Resources.Exception_NoTokenValidatorFound, validationFailures[0]);
+ }
+ else
+ {
+ throw new SecurityTokenException(Resources.Exception_NoTokenValidatorFound, new AggregateException(validationFailures));
+ }
}
- if (Options.UseTokenLifetime && parsedToken != null)
+ if (Options.UseTokenLifetime && validatedToken != null)
{
// Override any session persistence to match the token lifetime.
- var issued = parsedToken.ValidFrom;
+ var issued = validatedToken.ValidFrom;
if (issued != DateTime.MinValue)
{
properties.IssuedUtc = issued.ToUniversalTime();
}
- var expires = parsedToken.ValidTo;
+ var expires = validatedToken.ValidTo;
if (expires != DateTime.MinValue)
{
properties.ExpiresUtc = expires.ToUniversalTime();
@@ -287,7 +333,7 @@ protected override async Task HandleRemoteAuthenticateAsync
var securityTokenValidatedContext = new SecurityTokenValidatedContext(Context, Scheme, Options, principal, properties)
{
ProtocolMessage = wsFederationMessage,
- SecurityToken = parsedToken,
+ SecurityToken = validatedToken,
};
await Events.SecurityTokenValidated(securityTokenValidatedContext);
@@ -306,17 +352,13 @@ protected override async Task HandleRemoteAuthenticateAsync
{
Logger.ExceptionProcessingMessage(exception);
- // Refresh the configuration for exceptions that may be caused by key rollovers. The user can also request a refresh in the notification.
- if (Options.RefreshOnIssuerKeyNotFound && exception is SecurityTokenSignatureKeyNotFoundException)
- {
- Options.ConfigurationManager.RequestRefresh();
- }
-
+ RequestRefresh(exception);
var authenticationFailedContext = new AuthenticationFailedContext(Context, Scheme, Options)
{
ProtocolMessage = wsFederationMessage,
Exception = exception
};
+
await Events.AuthenticationFailed(authenticationFailedContext);
if (authenticationFailedContext.Result != null)
{
@@ -327,6 +369,42 @@ protected override async Task HandleRemoteAuthenticateAsync
}
}
+ private async Task SetupTokenValidationParametersAsync()
+ {
+ // Clone to avoid cross request race conditions for updated configurations.
+ var tokenValidationParameters = Options.TokenValidationParameters.Clone();
+
+ if (Options.ConfigurationManager is BaseConfigurationManager baseConfigurationManager)
+ {
+ tokenValidationParameters.ConfigurationManager = baseConfigurationManager;
+ }
+ else
+ {
+ if (Options.ConfigurationManager != null)
+ {
+ // GetConfigurationAsync has a time interval that must pass before new http request will be issued.
+ _configuration = await Options.ConfigurationManager.GetConfigurationAsync(Context.RequestAborted);
+
+ var issuers = new[] { _configuration.Issuer };
+ tokenValidationParameters.ValidIssuers = (tokenValidationParameters.ValidIssuers == null ? issuers : tokenValidationParameters.ValidIssuers.Concat(issuers));
+ tokenValidationParameters.IssuerSigningKeys = (tokenValidationParameters.IssuerSigningKeys == null ? _configuration.SigningKeys : tokenValidationParameters.IssuerSigningKeys.Concat(_configuration.SigningKeys));
+ }
+ }
+
+ return tokenValidationParameters;
+ }
+
+ private void RequestRefresh(Exception exception)
+ {
+ // Refresh the configuration for exceptions that may be caused by key rollovers. The user can also request a refresh in the notification.
+ // Refreshing on SecurityTokenSignatureKeyNotFound may be redundant if Last-Known-Good is enabled, it won't do much harm, most likely will be a nop.
+ if (Options.RefreshOnIssuerKeyNotFound && exception is SecurityTokenSignatureKeyNotFoundException)
+ {
+ Options.ConfigurationManager.RequestRefresh();
+ }
+
+ }
+
///
/// Handles Signout
///
diff --git a/src/Security/Authentication/WsFederation/src/WsFederationOptions.cs b/src/Security/Authentication/WsFederation/src/WsFederationOptions.cs
index 81ec1f385bd5..bfb8de3b36c7 100644
--- a/src/Security/Authentication/WsFederation/src/WsFederationOptions.cs
+++ b/src/Security/Authentication/WsFederation/src/WsFederationOptions.cs
@@ -7,6 +7,7 @@
using Microsoft.AspNetCore.Http;
using Microsoft.IdentityModel.Protocols;
using Microsoft.IdentityModel.Protocols.WsFederation;
+using Microsoft.IdentityModel.JsonWebTokens;
using Microsoft.IdentityModel.Tokens;
using Microsoft.IdentityModel.Tokens.Saml;
using Microsoft.IdentityModel.Tokens.Saml2;
@@ -24,6 +25,7 @@ public class WsFederationOptions : RemoteAuthenticationOptions
new SamlSecurityTokenHandler(),
new JwtSecurityTokenHandler()
};
+
private TokenValidationParameters _tokenValidationParameters = new TokenValidationParameters();
///
@@ -37,6 +39,13 @@ public WsFederationOptions()
// If you manage to get it configured, then you can set RemoteSignOutPath accordingly.
RemoteSignOutPath = "/signin-wsfed";
Events = new WsFederationEvents();
+
+ TokenHandlers = new Collection()
+ {
+ new Saml2SecurityTokenHandler(),
+ new SamlSecurityTokenHandler(),
+ new JsonWebTokenHandler{ MapInboundClaims = JwtSecurityTokenHandler.DefaultMapInboundClaims }
+ };
}
///
@@ -96,6 +105,7 @@ public override void Validate()
///
/// Gets or sets the collection of used to read and validate the s.
///
+ [Obsolete("SecurityTokenHandlers is no longer used by default. Use TokenHandlers instead. To continue using SecurityTokenHandlers, set UseSecurityTokenHandlers to true. See https://aka.ms/aspnetcore8/security-token-changes")]
public ICollection SecurityTokenHandlers
{
get
@@ -108,6 +118,14 @@ public ICollection SecurityTokenHandlers
}
}
+ ///
+ /// Gets the collection of used to read and validate the s.
+ ///
+ public ICollection TokenHandlers
+ {
+ get; private set;
+ }
+
///
/// Gets or sets the type used to secure data handled by the middleware.
///
@@ -181,4 +199,15 @@ public TokenValidationParameters TokenValidationParameters
///
[EditorBrowsable(EditorBrowsableState.Never)]
public new bool SaveTokens { get; set; }
+
+ ///
+ /// Gets or sets whether or will be used to validate the inbound token.
+ ///
+ ///
+ /// The advantage of using the TokenHandlers are:
+ /// There is an Async model.
+ /// The default token handler for JsonWebTokens is a which is faster than a .
+ /// There is an ability to make use of a Last-Known-Good model for metadata that protects applications when metadata is published with errors.
+ ///
+ public bool UseSecurityTokenHandlers { get; set; }
}
diff --git a/src/Security/Authentication/test/JwtBearerTests.cs b/src/Security/Authentication/test/JwtBearerTests.cs
index c6c9268cca26..502afab2db02 100755
--- a/src/Security/Authentication/test/JwtBearerTests.cs
+++ b/src/Security/Authentication/test/JwtBearerTests.cs
@@ -18,6 +18,7 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
+using Microsoft.IdentityModel.JsonWebTokens;
using Microsoft.IdentityModel.Tokens;
namespace Microsoft.AspNetCore.Authentication.JwtBearer;
@@ -70,6 +71,7 @@ public async Task BearerTokenValidation()
ValidAudience = "audience.contoso.com",
IssuerSigningKey = key,
};
+ o.UseSecurityTokenValidators = true;
});
var newBearerToken = "Bearer " + tokenText;
@@ -107,6 +109,7 @@ public async Task SaveBearerToken()
ValidAudience = "audience.contoso.com",
IssuerSigningKey = key,
};
+ o.UseSecurityTokenValidators = true;
});
var newBearerToken = "Bearer " + tokenText;
@@ -121,9 +124,20 @@ public void MapInboundClaimsDefaultsToTrue()
{
var options = new JwtBearerOptions();
Assert.True(options.MapInboundClaims);
+
+#pragma warning disable CS0618 // Type or member is obsolete
var jwtHandler = options.SecurityTokenValidators.First() as JwtSecurityTokenHandler;
+#pragma warning restore CS0618 // Type or member is obsolete
Assert.NotNull(jwtHandler);
Assert.True(jwtHandler.MapInboundClaims);
+
+ var tokenHandler = options.TokenHandlers.First() as JsonWebTokenHandler;
+ Assert.NotNull(tokenHandler);
+ Assert.True(tokenHandler.MapInboundClaims);
+
+ options.MapInboundClaims = false;
+ Assert.False(jwtHandler.MapInboundClaims);
+ Assert.False(tokenHandler.MapInboundClaims);
}
[Fact]
@@ -132,7 +146,9 @@ public void MapInboundClaimsCanBeSetToFalse()
var options = new JwtBearerOptions();
options.MapInboundClaims = false;
Assert.False(options.MapInboundClaims);
+#pragma warning disable CS0618 // Type or member is obsolete
var jwtHandler = options.SecurityTokenValidators.First() as JwtSecurityTokenHandler;
+#pragma warning restore CS0618 // Type or member is obsolete
Assert.NotNull(jwtHandler);
Assert.False(jwtHandler.MapInboundClaims);
}
@@ -173,8 +189,11 @@ public async Task ThrowAtAuthenticationFailedEvent()
return Task.FromResult(0);
}
};
+ o.UseSecurityTokenValidators = true;
+#pragma warning disable CS0618 // Type or member is obsolete
o.SecurityTokenValidators.Clear();
o.SecurityTokenValidators.Insert(0, new InvalidTokenValidator());
+#pragma warning restore CS0618 // Type or member is obsolete
},
async (context, next) =>
{
@@ -218,6 +237,7 @@ public async Task CustomHeaderReceived()
return Task.FromResult