diff --git a/src/Security/Authentication/JwtBearer/src/JwtBearerOptions.cs b/src/Security/Authentication/JwtBearer/src/JwtBearerOptions.cs index 758added5b..dff6fc43a7 100644 --- a/src/Security/Authentication/JwtBearer/src/JwtBearerOptions.cs +++ b/src/Security/Authentication/JwtBearer/src/JwtBearerOptions.cs @@ -16,6 +16,13 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer /// public class JwtBearerOptions : AuthenticationSchemeOptions { + private JwtSecurityTokenHandler _defaultHandler = new JwtSecurityTokenHandler(); + + public JwtBearerOptions() + { + SecurityTokenValidators = new List { _defaultHandler }; + } + /// /// Gets or sets if HTTPS is required for the metadata address or authority. /// The default is true. This should be disabled only in development environments. @@ -90,7 +97,7 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer /// /// Gets the ordered list of used to validate access tokens. /// - public IList SecurityTokenValidators { get; } = new List { new JwtSecurityTokenHandler() }; + public IList SecurityTokenValidators { get; private set; } /// /// Gets or sets the parameters used to validate identity tokens. @@ -111,6 +118,18 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer /// from returning an error and an error_description in the WWW-Authenticate header. /// 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 . + /// 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; + } /// /// 1 day is the default time interval that afterwards, will obtain new configuration. diff --git a/src/Security/Authentication/OpenIdConnect/samples/OpenIdConnectSample/Startup.cs b/src/Security/Authentication/OpenIdConnect/samples/OpenIdConnectSample/Startup.cs index c0696ba1a2..3e761aa6db 100644 --- a/src/Security/Authentication/OpenIdConnect/samples/OpenIdConnectSample/Startup.cs +++ b/src/Security/Authentication/OpenIdConnect/samples/OpenIdConnectSample/Startup.cs @@ -89,8 +89,6 @@ namespace OpenIdConnectSample public void ConfigureServices(IServiceCollection services) { - JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); - services.Configure(options => { options.MinimumSameSitePolicy = SameSiteMode.Unspecified; @@ -120,6 +118,7 @@ namespace OpenIdConnectSample o.SaveTokens = true; o.GetClaimsFromUserInfoEndpoint = true; o.AccessDeniedPath = "/access-denied-from-remote"; + o.MapInboundClaims = false; // o.ClaimActions.MapAllExcept("aud", "iss", "iat", "nbf", "exp", "aio", "c_hash", "uti", "nonce"); diff --git a/src/Security/Authentication/OpenIdConnect/src/OpenIdConnectOptions.cs b/src/Security/Authentication/OpenIdConnect/src/OpenIdConnectOptions.cs index 1b86c81901..be344d0475 100644 --- a/src/Security/Authentication/OpenIdConnect/src/OpenIdConnectOptions.cs +++ b/src/Security/Authentication/OpenIdConnect/src/OpenIdConnectOptions.cs @@ -18,6 +18,7 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect public class OpenIdConnectOptions : RemoteAuthenticationOptions { private CookieBuilder _nonceCookieBuilder; + private JwtSecurityTokenHandler _defaultHandler = new JwtSecurityTokenHandler(); /// /// Initializes a new @@ -38,6 +39,7 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect CallbackPath = new PathString("/signin-oidc"); SignedOutCallbackPath = new PathString("/signout-callback-oidc"); RemoteSignOutPath = new PathString("/signout-oidc"); + SecurityTokenValidator = _defaultHandler; Events = new OpenIdConnectEvents(); Scope.Add("openid"); @@ -253,7 +255,7 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect /// /// Gets or sets the used to validate identity tokens. /// - public ISecurityTokenValidator SecurityTokenValidator { get; set; } = new JwtSecurityTokenHandler(); + public ISecurityTokenValidator SecurityTokenValidator { get; set; } /// /// Gets or sets the parameters used to validate identity tokens. @@ -337,5 +339,17 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect /// The minimum time between retrievals, in the event that a retrieval failed, or that a refresh was explicitly requested. 30 seconds is the default. /// public TimeSpan RefreshInterval { get; set; } = ConfigurationManager.DefaultRefreshInterval; + + /// + /// Gets or sets the property on the default instance of in SecurityTokenValidator, which is used when determining + /// whether or not to map claim types that are extracted when validating 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; + } } } diff --git a/src/Security/Authentication/test/JwtBearerTests.cs b/src/Security/Authentication/test/JwtBearerTests.cs index 5075f1c888..be87343edf 100755 --- a/src/Security/Authentication/test/JwtBearerTests.cs +++ b/src/Security/Authentication/test/JwtBearerTests.cs @@ -116,6 +116,27 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer Assert.Equal(tokenText, await response.Response.Content.ReadAsStringAsync()); } + [Fact] + public void MapInboundClaimsDefaultsToTrue() + { + var options = new JwtBearerOptions(); + Assert.True(options.MapInboundClaims); + var jwtHandler = options.SecurityTokenValidators.First() as JwtSecurityTokenHandler; + Assert.NotNull(jwtHandler); + Assert.True(jwtHandler.MapInboundClaims); + } + + [Fact] + public void MapInboundClaimsCanBeSetToFalse() + { + var options = new JwtBearerOptions(); + options.MapInboundClaims = false; + Assert.False(options.MapInboundClaims); + var jwtHandler = options.SecurityTokenValidators.First() as JwtSecurityTokenHandler; + Assert.NotNull(jwtHandler); + Assert.False(jwtHandler.MapInboundClaims); + } + [Fact] public async Task SignInThrows() { diff --git a/src/Security/Authentication/test/OpenIdConnect/OpenIdConnectTests.cs b/src/Security/Authentication/test/OpenIdConnect/OpenIdConnectTests.cs index 76de0d29ff..387f86f1f0 100644 --- a/src/Security/Authentication/test/OpenIdConnect/OpenIdConnectTests.cs +++ b/src/Security/Authentication/test/OpenIdConnect/OpenIdConnectTests.cs @@ -3,6 +3,7 @@ using System; using System.Globalization; +using System.IdentityModel.Tokens.Jwt; using System.Linq; using System.Net; using System.Security.Claims; @@ -366,6 +367,27 @@ namespace Microsoft.AspNetCore.Authentication.Test.OpenIdConnect Assert.Contains(remoteSignOutTransaction.Response.Headers, h => h.Key == "Set-Cookie"); } + [Fact] + public void MapInboundClaimsDefaultsToTrue() + { + var options = new OpenIdConnectOptions(); + Assert.True(options.MapInboundClaims); + var jwtHandler = options.SecurityTokenValidator as JwtSecurityTokenHandler; + Assert.NotNull(jwtHandler); + Assert.True(jwtHandler.MapInboundClaims); + } + + [Fact] + public void MapInboundClaimsCanBeSetToFalse() + { + var options = new OpenIdConnectOptions(); + options.MapInboundClaims = false; + Assert.False(options.MapInboundClaims); + var jwtHandler = options.SecurityTokenValidator as JwtSecurityTokenHandler; + Assert.NotNull(jwtHandler); + Assert.False(jwtHandler.MapInboundClaims); + } + // Test Cases for calculating the expiration time of cookie from cookie name [Fact] public void NonceCookieExpirationTime()