From 76b41faed749141442a50e9d3ab4dd5035fbbcd4 Mon Sep 17 00:00:00 2001 From: Peter <34331512+pmaytak@users.noreply.github.com> Date: Tue, 16 Jul 2024 20:34:18 -0700 Subject: [PATCH 1/8] Cache context switches (#2724) * Cache context switches. * Refactor switch reset in tests. * Rename. * Refactor to reset switches in one method. * Fix tests. * Fix tests. --- .../JwtTokenUtilities.cs | 17 ++- .../AppContextSwitches.cs | 67 ++++++++- .../ClaimsIdentityFactory.cs | 37 +++-- .../Json/JsonSerializerPrimitives.cs | 128 ++++++++++++++++++ .../AadTokenValidationParametersExtension.cs | 9 +- .../JsonWebTokenHandlerClaimsIdentityTests.cs | 15 +- .../JsonWebTokenTests.cs | 8 +- .../OpenIdConnectMessageTests.cs | 6 +- .../CaseSensitiveClaimsIdentityTests.cs | 4 +- .../ClaimsIdentityFactoryTests.cs | 16 +-- .../AadSigningKeyIssuerValidatorTests.cs | 24 ++-- .../CreateAndValidateTokens.cs | 4 +- 12 files changed, 275 insertions(+), 60 deletions(-) create mode 100644 src/Microsoft.IdentityModel.Tokens/Json/JsonSerializerPrimitives.cs diff --git a/src/Microsoft.IdentityModel.JsonWebTokens/JwtTokenUtilities.cs b/src/Microsoft.IdentityModel.JsonWebTokens/JwtTokenUtilities.cs index f24e2f4fd2..4220a83495 100644 --- a/src/Microsoft.IdentityModel.JsonWebTokens/JwtTokenUtilities.cs +++ b/src/Microsoft.IdentityModel.JsonWebTokens/JwtTokenUtilities.cs @@ -11,7 +11,9 @@ using Microsoft.IdentityModel.Json.Linq; using Microsoft.IdentityModel.Logging; using Microsoft.IdentityModel.Tokens; +using Microsoft.IdentityModel.Tokens.Json; using TokenLogMessages = Microsoft.IdentityModel.Tokens.LogMessages; +using System.Security.Claims; #if !NET45 using System.IO; @@ -537,8 +539,21 @@ internal static JsonDocument GetJsonDocumentFromBase64UrlEncodedString(string ra { return Base64UrlEncoding.Decode(rawString, startIndex, length, ParseDocument); } -#endif + internal static string GetStringClaimValueType(string str, string claimType) + { + if (!string.IsNullOrEmpty(claimType) && !AppContextSwitches.TryAllStringClaimsAsDateTime && JsonSerializerPrimitives.IsKnownToNotBeDateTime(claimType)) + return ClaimValueTypes.String; + if (DateTime.TryParse(str, out DateTime dateTimeValue)) + { + string dtUniversal = dateTimeValue.ToUniversalTime().ToString("o", CultureInfo.InvariantCulture); + if (dtUniversal.Equals(str, StringComparison.Ordinal)) + return ClaimValueTypes.DateTime; + } + + return ClaimValueTypes.String; + } +#endif } } diff --git a/src/Microsoft.IdentityModel.Tokens/AppContextSwitches.cs b/src/Microsoft.IdentityModel.Tokens/AppContextSwitches.cs index db3b8e269e..5a0af16bce 100644 --- a/src/Microsoft.IdentityModel.Tokens/AppContextSwitches.cs +++ b/src/Microsoft.IdentityModel.Tokens/AppContextSwitches.cs @@ -11,14 +11,73 @@ namespace Microsoft.IdentityModel.Tokens /// internal static class AppContextSwitches { +#if NET461_OR_GREATER || NETCOREAPP || NETSTANDARD /// - /// Enables a new behavior of using instead of globally. + /// Enables a fallback to the previous behavior of using instead of globally. /// - internal const string UseCaseSensitiveClaimsIdentityTypeSwitch = "Microsoft.IdentityModel.Tokens.UseCaseSensitiveClaimsIdentityType"; + internal const string UseClaimsIdentityTypeSwitch = "Microsoft.IdentityModel.Tokens.UseClaimsIdentityType"; -#if NET46_OR_GREATER || NETCOREAPP || NETSTANDARD - internal static bool UseCaseSensitiveClaimsIdentityType() => AppContext.TryGetSwitch(UseCaseSensitiveClaimsIdentityTypeSwitch, out bool useCaseSensitiveClaimsIdentityType) && useCaseSensitiveClaimsIdentityType; + private static bool? _useClaimsIdentityType; + internal static bool UseClaimsIdentityType => _useClaimsIdentityType ??= (AppContext.TryGetSwitch(UseClaimsIdentityTypeSwitch, out bool useClaimsIdentityType) && useClaimsIdentityType); + + /// + /// When validating the issuer signing key, specifies whether to fail if the 'tid' claim is missing. + /// + internal const string DoNotFailOnMissingTidSwitch = "Switch.Microsoft.IdentityModel.DontFailOnMissingTidValidateIssuerSigning"; + + private static bool? _doNotFailOnMissingTid; + + internal static bool DoNotFailOnMissingTid => _doNotFailOnMissingTid ??= (AppContext.TryGetSwitch(DoNotFailOnMissingTidSwitch, out bool doNotFailOnMissingTid) && doNotFailOnMissingTid); + + /// + /// When reading claims from the token, specifies whether to try to convert all string claims to DateTime. + /// Some claims are known not to be DateTime, so conversion is skipped. + /// + internal const string TryAllStringClaimsAsDateTimeSwitch = "Switch.Microsoft.IdentityModel.TryAllStringClaimsAsDateTime"; + + private static bool? _tryAllStringClaimsAsDateTime; + + internal static bool TryAllStringClaimsAsDateTime => _tryAllStringClaimsAsDateTime ??= (AppContext.TryGetSwitch(TryAllStringClaimsAsDateTimeSwitch, out bool tryAsDateTime) && tryAsDateTime); + + /// + /// Controls whether to validate the length of the authentication tag when decrypting a token. + /// + internal const string SkipValidationOfAuthenticationTagLengthSwitch = "Switch.Microsoft.IdentityModel.SkipAuthenticationTagLengthValidation"; + + private static bool? _skipValidationOfAuthenticationTagLength; + + internal static bool ShouldValidateAuthenticationTagLength => _skipValidationOfAuthenticationTagLength ??= !(AppContext.TryGetSwitch(SkipValidationOfAuthenticationTagLengthSwitch, out bool skipValidation) && skipValidation); + + /// + /// Controls whether to use the short name for the RSA OAEP key wrap algorithm. + /// + internal const string UseShortNameForRsaOaepKeySwitch = "Switch.Microsoft.IdentityModel.UseShortNameForRsaOaepKey"; + + private static bool? _useShortNameForRsaOaepKey; + + internal static bool ShouldUseShortNameForRsaOaepKey => _useShortNameForRsaOaepKey ??= AppContext.TryGetSwitch(UseShortNameForRsaOaepKeySwitch, out var useKeyWrap) && useKeyWrap; + + /// + /// Used for testing to reset all switches to its default value. + /// + internal static void ResetAllSwitches() + { + _useClaimsIdentityType = null; + AppContext.SetSwitch(UseClaimsIdentityTypeSwitch, false); + + _doNotFailOnMissingTid = null; + AppContext.SetSwitch(DoNotFailOnMissingTidSwitch, false); + + _tryAllStringClaimsAsDateTime = null; + AppContext.SetSwitch(TryAllStringClaimsAsDateTimeSwitch, false); + + _skipValidationOfAuthenticationTagLength = null; + AppContext.SetSwitch(SkipValidationOfAuthenticationTagLengthSwitch, false); + + _useShortNameForRsaOaepKey = null; + AppContext.SetSwitch(UseShortNameForRsaOaepKeySwitch, false); + } #else // .NET 4.5 does not support AppContext switches. Always use ClaimsIdentity. internal static bool UseCaseSensitiveClaimsIdentityType() => false; diff --git a/src/Microsoft.IdentityModel.Tokens/ClaimsIdentityFactory.cs b/src/Microsoft.IdentityModel.Tokens/ClaimsIdentityFactory.cs index 1f6f56bc74..ec7201b159 100644 --- a/src/Microsoft.IdentityModel.Tokens/ClaimsIdentityFactory.cs +++ b/src/Microsoft.IdentityModel.Tokens/ClaimsIdentityFactory.cs @@ -6,36 +6,43 @@ namespace Microsoft.IdentityModel.Tokens { +#if !NET45 /// - /// Facilitates the creation of and instances based on the . + /// Facilitates the creation of and instances based on the . /// +#endif + internal static class ClaimsIdentityFactory { internal static ClaimsIdentity Create(IEnumerable claims) { - if (AppContextSwitches.UseCaseSensitiveClaimsIdentityType()) - return new CaseSensitiveClaimsIdentity(claims); - - return new ClaimsIdentity(claims); +#if !NET45 + if (AppContextSwitches.UseClaimsIdentityType) + return new ClaimsIdentity(claims); +#endif + return new CaseSensitiveClaimsIdentity(claims); } internal static ClaimsIdentity Create(IEnumerable claims, string authenticationType) { - if (AppContextSwitches.UseCaseSensitiveClaimsIdentityType()) - return new CaseSensitiveClaimsIdentity(claims, authenticationType); - - return new ClaimsIdentity(claims, authenticationType); +#if !NET45 + if (AppContextSwitches.UseClaimsIdentityType) + return new ClaimsIdentity(claims, authenticationType); +#endif + return new CaseSensitiveClaimsIdentity(claims, authenticationType); } internal static ClaimsIdentity Create(string authenticationType, string nameType, string roleType, SecurityToken securityToken) { - if (AppContextSwitches.UseCaseSensitiveClaimsIdentityType()) - return new CaseSensitiveClaimsIdentity(authenticationType: authenticationType, nameType: nameType, roleType: roleType) - { - SecurityToken = securityToken, - }; +#if !NET45 - return new ClaimsIdentity(authenticationType: authenticationType, nameType: nameType, roleType: roleType); + if (AppContextSwitches.UseClaimsIdentityType) + return new ClaimsIdentity(authenticationType: authenticationType, nameType: nameType, roleType: roleType); +#endif + return new CaseSensitiveClaimsIdentity(authenticationType: authenticationType, nameType: nameType, roleType: roleType) + { + SecurityToken = securityToken, + }; } } } diff --git a/src/Microsoft.IdentityModel.Tokens/Json/JsonSerializerPrimitives.cs b/src/Microsoft.IdentityModel.Tokens/Json/JsonSerializerPrimitives.cs new file mode 100644 index 0000000000..758fee4753 --- /dev/null +++ b/src/Microsoft.IdentityModel.Tokens/Json/JsonSerializerPrimitives.cs @@ -0,0 +1,128 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; + +namespace Microsoft.IdentityModel.Tokens.Json +{ + internal static class JsonSerializerPrimitives + { +#if !NET45 + public static bool TryAllStringClaimsAsDateTime() + { + return AppContextSwitches.TryAllStringClaimsAsDateTime; + } +#endif + + /// + /// This is a non-exhaustive list of claim types that are not expected to be DateTime values + /// sourced from expected Entra V1 and V2 claims, OpenID Connect claims, and a selection of + /// restricted claim names. + /// + private static readonly HashSet s_knownNonDateTimeClaimTypes = new(StringComparer.Ordinal) + { + // Header Values. + "alg", + "cty", + "crit", + "enc", + "jku", + "jwk", + "kid", + "typ", + "x5c", + "x5t", + "x5t#S256", + "x5u", + "zip", + // JWT claims. + "acr", + "acrs", + "access_token", + "account_type", + "acct", + "actor", + "actort", + "actortoken", + "aio", + "altsecid", + "amr", + "app_displayname", + "appid", + "appidacr", + "at_hash", + "aud", + "authorization_code", + "azp", + "azpacr", + "c_hash", + "cnf", + "capolids", + "ctry", + "email", + "family_name", + "fwd", + "gender", + "given_name", + "groups", + "hasgroups", + "idp", + "idtyp", + "in_corp", + "ipaddr", + "iss", + "jti", + "login_hint", + "name", + "nameid", + "nickname", + "nonce", + "oid", + "onprem_sid", + "phone_number", + "phone_number_verified", + "pop_jwk", + "preferred_username", + "prn", + "puid", + "pwd_url", + "rh", + "role", + "roles", + "secaud", + "sid", + "sub", + "tenant_ctry", + "tenant_region_scope", + "tid", + "unique_name", + "upn", + "uti", + "ver", + "verified_primary_email", + "verified_secondary_email", + "vnet", + "website", + "wids", + "xms_cc", + "xms_edov", + "xms_pdl", + "xms_pl", + "xms_tpl", + "ztdid" + }; + + internal static bool IsKnownToNotBeDateTime(string claimType) + { + if (string.IsNullOrEmpty(claimType)) + return true; + + if (s_knownNonDateTimeClaimTypes.Contains(claimType)) + return true; + + return false; + } + } +} diff --git a/src/Microsoft.IdentityModel.Validators/AadTokenValidationParametersExtension.cs b/src/Microsoft.IdentityModel.Validators/AadTokenValidationParametersExtension.cs index 1a0e132dfd..be48e048aa 100644 --- a/src/Microsoft.IdentityModel.Validators/AadTokenValidationParametersExtension.cs +++ b/src/Microsoft.IdentityModel.Validators/AadTokenValidationParametersExtension.cs @@ -45,11 +45,11 @@ public static void EnableAadSigningKeyIssuerValidation(this TokenValidationParam } #if !NET45 - internal const string DontFailOnMissingTidSwitch = "Switch.Microsoft.IdentityModel.DontFailOnMissingTidValidateIssuerSigning"; + internal const string DoNotFailOnMissingTidSwitch = "Switch.Microsoft.IdentityModel.DontFailOnMissingTidValidateIssuerSigning"; private static bool DontFailOnMissingTid() { - return (AppContext.TryGetSwitch(DontFailOnMissingTidSwitch, out bool dontFailOnMissingTid) && dontFailOnMissingTid); + return (AppContext.TryGetSwitch(DoNotFailOnMissingTidSwitch, out bool doNotFailOnMissingTid) && doNotFailOnMissingTid); } #endif @@ -83,10 +83,9 @@ internal static bool ValidateIssuerSigningKey(SecurityKey securityKey, SecurityT if (string.IsNullOrEmpty(tenantIdFromToken)) { #if !NET45 - if (DontFailOnMissingTid()) - return true; + if (AppContextSwitches.DoNotFailOnMissingTid) + return true; #endif - throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidIssuerException(LogMessages.IDX40009)); } diff --git a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandlerClaimsIdentityTests.cs b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandlerClaimsIdentityTests.cs index 44e8854332..1a79169b42 100644 --- a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandlerClaimsIdentityTests.cs +++ b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandlerClaimsIdentityTests.cs @@ -13,12 +13,9 @@ namespace Microsoft.IdentityModel.JsonWebTokens.Tests public class JsonWebTokenHandlerClaimsIdentityTests { -#if NET46_OR_GREATER || NETCOREAPP || NETSTANDARD [Fact] public void CreateClaimsIdentity_ReturnsCaseSensitveClaimsIdentity_WithAppContextSwitch() { - AppContext.SetSwitch(AppContextSwitches.UseCaseSensitiveClaimsIdentityTypeSwitch, true); - var handler = new DerivedJsonWebTokenHandler(); var jsonWebToken = new JsonWebToken(Default.Jwt(Default.SecurityTokenDescriptor())); var tokenValidationParameters = new TokenValidationParameters(); @@ -40,14 +37,17 @@ public void CreateClaimsIdentity_ReturnsCaseSensitveClaimsIdentity_WithAppContex actualClaimsIdentity = handler.CreateClaimsIdentityInternal(jsonWebToken, tokenValidationParameters, Default.Issuer); Assert.IsType(actualClaimsIdentity); Assert.NotNull(((CaseSensitiveClaimsIdentity)actualClaimsIdentity).SecurityToken); - - AppContext.SetSwitch(AppContextSwitches.UseCaseSensitiveClaimsIdentityTypeSwitch, false); - } +#if !NET452 + AppContextSwitches.ResetAllSwitches(); #endif + } +#if NET46_OR_GREATER || NETCOREAPP || NETSTANDARD [Fact] public void CreateClaimsIdentity_ReturnsClaimsIdentity_ByDefault() { + AppContext.SetSwitch(AppContextSwitches.UseClaimsIdentityTypeSwitch, true); + var handler = new DerivedJsonWebTokenHandler(); var jsonWebToken = new JsonWebToken(Default.Jwt(Default.SecurityTokenDescriptor())); var tokenValidationParameters = new TokenValidationParameters(); @@ -58,7 +58,10 @@ public void CreateClaimsIdentity_ReturnsClaimsIdentity_ByDefault() // This will also test mapped claims flow. handler.MapInboundClaims = true; Assert.IsType(handler.CreateClaimsIdentityInternal(jsonWebToken, tokenValidationParameters, Default.Issuer)); + + AppContextSwitches.ResetAllSwitches(); } +#endif private class DerivedJsonWebTokenHandler : JsonWebTokenHandler { diff --git a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenTests.cs b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenTests.cs index c80ce297a3..62fa2cc891 100644 --- a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenTests.cs +++ b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenTests.cs @@ -596,7 +596,7 @@ public static TheoryData ParseTokenTheoryData theoryData.Add(new JwtTheoryData(nameof(EncodedJwts.InvalidHeader)) { Token = EncodedJwts.InvalidHeader, -#if NET452 +#if NET45 ExpectedException = new ExpectedException(typeof(ArgumentException), "IDX14102:", typeof(JsonReaderException), false ), #else ExpectedException = new ExpectedException(typeof(ArgumentException), "IDX14102:", typeof(JsonReaderException), true), @@ -606,7 +606,7 @@ public static TheoryData ParseTokenTheoryData theoryData.Add(new JwtTheoryData(nameof(EncodedJwts.InvalidPayload)) { Token = EncodedJwts.InvalidPayload, -#if NET452 +#if NET45 ExpectedException = new ExpectedException(typeof(ArgumentException), "IDX14101:", typeof(JsonReaderException), false ), #else ExpectedException = new ExpectedException(typeof(ArgumentException), "IDX14101:", typeof(JsonReaderException), true), @@ -616,7 +616,7 @@ public static TheoryData ParseTokenTheoryData theoryData.Add(new JwtTheoryData(nameof(EncodedJwts.JWSEmptyHeader)) { Token = EncodedJwts.JWSEmptyHeader, -#if NET452 +#if NET45 ExpectedException = new ExpectedException(typeof(ArgumentException), "IDX14102:", typeof(JsonReaderException), false ), #else ExpectedException = new ExpectedException(typeof(ArgumentException), "IDX14102:", typeof(JsonReaderException), true), @@ -626,7 +626,7 @@ public static TheoryData ParseTokenTheoryData theoryData.Add(new JwtTheoryData(nameof(EncodedJwts.JWSEmptyPayload)) { Token = EncodedJwts.JWSEmptyPayload, -#if NET452 +#if NET45 ExpectedException = new ExpectedException(typeof(ArgumentException), "IDX14101:", typeof(JsonReaderException), false ), #else ExpectedException = new ExpectedException(typeof(ArgumentException), "IDX14101:", typeof(JsonReaderException), true), diff --git a/test/Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests/OpenIdConnectMessageTests.cs b/test/Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests/OpenIdConnectMessageTests.cs index f4e9423925..14fa2e7adf 100644 --- a/test/Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests/OpenIdConnectMessageTests.cs +++ b/test/Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests/OpenIdConnectMessageTests.cs @@ -218,7 +218,7 @@ public void OidcCreateAuthenticationRequestUrl(string testId, OpenIdConnectMessa TestUtilities.WriteHeader(testId, "OidcCreateAuthenticationRequestUrl", true); var context = new CompareContext(); // there is no net452 target, we bind to net45 -#if NET452 +#if NET45 if(!message.SkuTelemetryValue.Equals("ID_NET45")) context.Diffs.Add($"{message.SkuTelemetryValue} != ID_NET45"); #elif NET461 @@ -494,7 +494,7 @@ public void OidcCreateLogoutRequestUrl(string testId, OpenIdConnectMessage messa var context = new CompareContext(); // there is no net452 target, we bind to net45 -#if NET452 +#if NET45 if (!message.SkuTelemetryValue.Equals("ID_NET45")) context.Diffs.Add($"{message.SkuTelemetryValue} != ID_NET45"); #elif NET461 @@ -505,7 +505,7 @@ public void OidcCreateLogoutRequestUrl(string testId, OpenIdConnectMessage messa context.Diffs.Add($"{message.SkuTelemetryValue} != ID_NET472"); #elif NET6_0 if (!message.SkuTelemetryValue.Equals("ID_NET6_0")) - context.Diffs.Add($"{message.SkuTelemetryValue} != ID_NETCOREAPP3_1"); + context.Diffs.Add($"{message.SkuTelemetryValue} != ID_NET6_0"); #elif NET_CORE if (!message.SkuTelemetryValue.Equals("ID_NETSTANDARD2_0")) context.Diffs.Add($"{message.SkuTelemetryValue} != ID_NETSTANDARD2_0"); diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/CaseSensitiveClaimsIdentityTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/CaseSensitiveClaimsIdentityTests.cs index 18241e4e26..d0eadf09b1 100644 --- a/test/Microsoft.IdentityModel.Tokens.Tests/CaseSensitiveClaimsIdentityTests.cs +++ b/test/Microsoft.IdentityModel.Tokens.Tests/CaseSensitiveClaimsIdentityTests.cs @@ -228,12 +228,12 @@ public class CaseSensitiveClaimsIdentityTheoryData(string testId) : TheoryDataBa private static ClaimsIdentity CreateCaseSensitiveClaimsIdentity(JObject claims, TokenValidationParameters validationParameters = null) { - AppContext.SetSwitch(AppContextSwitches.UseCaseSensitiveClaimsIdentityTypeSwitch, true); + AppContext.SetSwitch(AppContextSwitches.UseClaimsIdentityTypeSwitch, false); var handler = new JsonWebTokenHandler(); var claimsIdentity = handler.CreateClaimsIdentityInternal(new JsonWebToken(CreateUnsignedToken(claims)), validationParameters ?? new TokenValidationParameters(), Default.Issuer); - AppContext.SetSwitch(AppContextSwitches.UseCaseSensitiveClaimsIdentityTypeSwitch, false); + AppContext.SetSwitch(AppContextSwitches.UseClaimsIdentityTypeSwitch, true); return claimsIdentity; } diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/ClaimsIdentityFactoryTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/ClaimsIdentityFactoryTests.cs index f17deeae91..5f5a6d6866 100644 --- a/test/Microsoft.IdentityModel.Tokens.Tests/ClaimsIdentityFactoryTests.cs +++ b/test/Microsoft.IdentityModel.Tokens.Tests/ClaimsIdentityFactoryTests.cs @@ -19,7 +19,7 @@ public class ClaimsIdentityFactoryTests [InlineData(false)] public void Create_FromTokenValidationParameters_ReturnsCorrectClaimsIdentity(bool useCaseSensitiveClaimsIdentity) { - AppContext.SetSwitch(AppContextSwitches.UseCaseSensitiveClaimsIdentityTypeSwitch, useCaseSensitiveClaimsIdentity); + AppContext.SetSwitch(AppContextSwitches.UseClaimsIdentityTypeSwitch, useCaseSensitiveClaimsIdentity); var jsonWebToken = new JsonWebToken(Default.Jwt(Default.SecurityTokenDescriptor())); var tokenValidationParameters = new TokenValidationParameters(); @@ -35,16 +35,16 @@ public void Create_FromTokenValidationParameters_ReturnsCorrectClaimsIdentity(bo if (useCaseSensitiveClaimsIdentity) { - Assert.IsType(actualClaimsIdentity); - Assert.NotNull(((CaseSensitiveClaimsIdentity)actualClaimsIdentity).SecurityToken); - Assert.Equal(jsonWebToken, ((CaseSensitiveClaimsIdentity)actualClaimsIdentity).SecurityToken); + Assert.IsType(actualClaimsIdentity); } else { - Assert.IsType(actualClaimsIdentity); + Assert.IsType(actualClaimsIdentity); + Assert.NotNull(((CaseSensitiveClaimsIdentity)actualClaimsIdentity).SecurityToken); + Assert.Equal(jsonWebToken, ((CaseSensitiveClaimsIdentity)actualClaimsIdentity).SecurityToken); } - AppContext.SetSwitch(AppContextSwitches.UseCaseSensitiveClaimsIdentityTypeSwitch, false); + AppContextSwitches.ResetAllSwitches(); } [Theory] @@ -53,7 +53,7 @@ public void Create_FromTokenValidationParameters_ReturnsCorrectClaimsIdentity(bo [InlineData(false, false)] public void Create_FromDerivedTokenValidationParameters_ReturnsCorrectClaimsIdentity(bool tvpReturnsCaseSensitiveClaimsIdentity, bool tvpReturnsCaseSensitiveClaimsIdentityWithToken) { - AppContext.SetSwitch(AppContextSwitches.UseCaseSensitiveClaimsIdentityTypeSwitch, true); + AppContext.SetSwitch(AppContextSwitches.UseClaimsIdentityTypeSwitch, true); var jsonWebToken = new JsonWebToken(Default.Jwt(Default.SecurityTokenDescriptor())); var tokenValidationParameters = new DerivedTokenValidationParameters(tvpReturnsCaseSensitiveClaimsIdentity, tvpReturnsCaseSensitiveClaimsIdentityWithToken); @@ -87,7 +87,7 @@ public void Create_FromDerivedTokenValidationParameters_ReturnsCorrectClaimsIden Assert.Equal(tokenValidationParameters.NameClaimType, actualClaimsIdentity.NameClaimType); Assert.Equal(tokenValidationParameters.RoleClaimType, actualClaimsIdentity.RoleClaimType); - AppContext.SetSwitch(AppContextSwitches.UseCaseSensitiveClaimsIdentityTypeSwitch, false); + AppContext.SetSwitch(AppContextSwitches.UseClaimsIdentityTypeSwitch, false); } #endif diff --git a/test/Microsoft.IdentityModel.Validators.Tests/AadSigningKeyIssuerValidatorTests.cs b/test/Microsoft.IdentityModel.Validators.Tests/AadSigningKeyIssuerValidatorTests.cs index 3da5af997c..2ba51f4a3c 100644 --- a/test/Microsoft.IdentityModel.Validators.Tests/AadSigningKeyIssuerValidatorTests.cs +++ b/test/Microsoft.IdentityModel.Validators.Tests/AadSigningKeyIssuerValidatorTests.cs @@ -181,7 +181,11 @@ public void ValidateIssuerSigningKeyTests(AadSigningKeyIssuerTheoryData theoryDa } finally { +#if !NET452 + AppContextSwitches.ResetAllSwitches(); +#else theoryData.TearDownAction?.Invoke(); +#endif } TestUtilities.AssertFailIfErrors(context); @@ -346,8 +350,8 @@ public static TheoryData ValidateIssuerSigningKey SecurityKey = KeyingMaterial.JsonWebKeyP256, SecurityToken = new JwtSecurityToken(), OpenIdConnectConfiguration = mockConfiguration, - SetupAction = () => AppContext.SetSwitch(AadTokenValidationParametersExtension.DontFailOnMissingTidSwitch, true), - TearDownAction = () => AppContext.SetSwitch(AadTokenValidationParametersExtension.DontFailOnMissingTidSwitch, false) + SetupAction = () => AppContext.SetSwitch(AadTokenValidationParametersExtension.DoNotFailOnMissingTidSwitch, true), + TearDownAction = () => AppContext.SetSwitch(AadTokenValidationParametersExtension.DoNotFailOnMissingTidSwitch, false) }); theoryData.Add(new AadSigningKeyIssuerTheoryData @@ -357,8 +361,8 @@ public static TheoryData ValidateIssuerSigningKey SecurityToken = new JwtSecurityToken(), OpenIdConnectConfiguration = mockConfiguration, ExpectedException = ExpectedException.SecurityTokenInvalidIssuerException("IDX40009"), - SetupAction = () => AppContext.SetSwitch(AadTokenValidationParametersExtension.DontFailOnMissingTidSwitch, false), - TearDownAction = () => AppContext.SetSwitch(AadTokenValidationParametersExtension.DontFailOnMissingTidSwitch, isEnabled: false) + SetupAction = () => AppContext.SetSwitch(AadTokenValidationParametersExtension.DoNotFailOnMissingTidSwitch, false), + TearDownAction = () => AppContext.SetSwitch(AadTokenValidationParametersExtension.DoNotFailOnMissingTidSwitch, isEnabled: false) }); theoryData.Add(new AadSigningKeyIssuerTheoryData @@ -367,8 +371,8 @@ public static TheoryData ValidateIssuerSigningKey SecurityKey = KeyingMaterial.JsonWebKeyP256, SecurityToken = new JwtSecurityToken(), OpenIdConnectConfiguration = mockConfiguration, - SetupAction = () => AppContext.SetSwitch(AadTokenValidationParametersExtension.DontFailOnMissingTidSwitch, true), - TearDownAction = () => AppContext.SetSwitch(AadTokenValidationParametersExtension.DontFailOnMissingTidSwitch, false) + SetupAction = () => AppContext.SetSwitch(AadTokenValidationParametersExtension.DoNotFailOnMissingTidSwitch, true), + TearDownAction = () => AppContext.SetSwitch(AadTokenValidationParametersExtension.DoNotFailOnMissingTidSwitch, false) }); theoryData.Add(new AadSigningKeyIssuerTheoryData @@ -378,8 +382,8 @@ public static TheoryData ValidateIssuerSigningKey SecurityToken = new JsonWebToken(Default.Jwt(Default.SecurityTokenDescriptor(Default.SymmetricSigningCredentials, [issClaim]))), OpenIdConnectConfiguration = mockConfiguration, ExpectedException = ExpectedException.SecurityTokenInvalidIssuerException("IDX40009"), - SetupAction = () => AppContext.SetSwitch(AadTokenValidationParametersExtension.DontFailOnMissingTidSwitch, false), - TearDownAction = () => AppContext.SetSwitch(AadTokenValidationParametersExtension.DontFailOnMissingTidSwitch, isEnabled: false) + SetupAction = () => AppContext.SetSwitch(AadTokenValidationParametersExtension.DoNotFailOnMissingTidSwitch, false), + TearDownAction = () => AppContext.SetSwitch(AadTokenValidationParametersExtension.DoNotFailOnMissingTidSwitch, isEnabled: false) }); theoryData.Add(new AadSigningKeyIssuerTheoryData @@ -388,8 +392,8 @@ public static TheoryData ValidateIssuerSigningKey SecurityKey = KeyingMaterial.JsonWebKeyP256, SecurityToken = new JsonWebToken(Default.Jwt(Default.SecurityTokenDescriptor(Default.SymmetricSigningCredentials, [issClaim]))), OpenIdConnectConfiguration = mockConfiguration, - SetupAction = () => AppContext.SetSwitch(AadTokenValidationParametersExtension.DontFailOnMissingTidSwitch, true), - TearDownAction = () => AppContext.SetSwitch(AadTokenValidationParametersExtension.DontFailOnMissingTidSwitch, false) + SetupAction = () => AppContext.SetSwitch(AadTokenValidationParametersExtension.DoNotFailOnMissingTidSwitch, true), + TearDownAction = () => AppContext.SetSwitch(AadTokenValidationParametersExtension.DoNotFailOnMissingTidSwitch, false) }); #endif diff --git a/test/System.IdentityModel.Tokens.Jwt.Tests/CreateAndValidateTokens.cs b/test/System.IdentityModel.Tokens.Jwt.Tests/CreateAndValidateTokens.cs index 05b9bee2fc..c38e5e52de 100644 --- a/test/System.IdentityModel.Tokens.Jwt.Tests/CreateAndValidateTokens.cs +++ b/test/System.IdentityModel.Tokens.Jwt.Tests/CreateAndValidateTokens.cs @@ -615,8 +615,8 @@ public static TheoryData RoundTripTokensTheoryData() ValidationParameters = Default.SymmetricEncryptSignTokenValidationParameters }); -#if NET461 || NET462 || NET_CORE - // RsaPss is not supported on .NET < 4.6 +#if NET462 || NET_CORE + // RsaPss is not supported on .NET < 4.6.2 var rsaPssSigningCredentials = new SigningCredentials(Default.AsymmetricSigningKey, SecurityAlgorithms.RsaSsaPssSha256); theoryData.Add(new JwtTheoryData { From 221f89b8a5c96f98c7a7cee50d629a4e2815ac68 Mon Sep 17 00:00:00 2001 From: Westin Musser <127992899+westin-m@users.noreply.github.com> Date: Fri, 9 Aug 2024 12:30:04 -0700 Subject: [PATCH 2/8] Apply suggestions from code review Co-authored-by: Keegan Caruso --- src/Microsoft.IdentityModel.JsonWebTokens/JwtTokenUtilities.cs | 1 + .../AadTokenValidationParametersExtension.cs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Microsoft.IdentityModel.JsonWebTokens/JwtTokenUtilities.cs b/src/Microsoft.IdentityModel.JsonWebTokens/JwtTokenUtilities.cs index 4220a83495..d3369ec8fb 100644 --- a/src/Microsoft.IdentityModel.JsonWebTokens/JwtTokenUtilities.cs +++ b/src/Microsoft.IdentityModel.JsonWebTokens/JwtTokenUtilities.cs @@ -539,6 +539,7 @@ internal static JsonDocument GetJsonDocumentFromBase64UrlEncodedString(string ra { return Base64UrlEncoding.Decode(rawString, startIndex, length, ParseDocument); } + internal static string GetStringClaimValueType(string str, string claimType) { if (!string.IsNullOrEmpty(claimType) && !AppContextSwitches.TryAllStringClaimsAsDateTime && JsonSerializerPrimitives.IsKnownToNotBeDateTime(claimType)) diff --git a/src/Microsoft.IdentityModel.Validators/AadTokenValidationParametersExtension.cs b/src/Microsoft.IdentityModel.Validators/AadTokenValidationParametersExtension.cs index be48e048aa..daaa9d8ae2 100644 --- a/src/Microsoft.IdentityModel.Validators/AadTokenValidationParametersExtension.cs +++ b/src/Microsoft.IdentityModel.Validators/AadTokenValidationParametersExtension.cs @@ -84,7 +84,7 @@ internal static bool ValidateIssuerSigningKey(SecurityKey securityKey, SecurityT { #if !NET45 if (AppContextSwitches.DoNotFailOnMissingTid) - return true; + return true; #endif throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidIssuerException(LogMessages.IDX40009)); } From b5e5dad19a8a9c0a3a0af480d8229c3105ac405b Mon Sep 17 00:00:00 2001 From: Westin Musser Date: Fri, 9 Aug 2024 12:36:15 -0700 Subject: [PATCH 3/8] pr feedback --- .../JwtTokenUtilities.cs | 15 -- .../Json/JsonSerializerPrimitives.cs | 128 ------------------ 2 files changed, 143 deletions(-) delete mode 100644 src/Microsoft.IdentityModel.Tokens/Json/JsonSerializerPrimitives.cs diff --git a/src/Microsoft.IdentityModel.JsonWebTokens/JwtTokenUtilities.cs b/src/Microsoft.IdentityModel.JsonWebTokens/JwtTokenUtilities.cs index 4220a83495..1d07ac7e0b 100644 --- a/src/Microsoft.IdentityModel.JsonWebTokens/JwtTokenUtilities.cs +++ b/src/Microsoft.IdentityModel.JsonWebTokens/JwtTokenUtilities.cs @@ -11,7 +11,6 @@ using Microsoft.IdentityModel.Json.Linq; using Microsoft.IdentityModel.Logging; using Microsoft.IdentityModel.Tokens; -using Microsoft.IdentityModel.Tokens.Json; using TokenLogMessages = Microsoft.IdentityModel.Tokens.LogMessages; using System.Security.Claims; @@ -539,20 +538,6 @@ internal static JsonDocument GetJsonDocumentFromBase64UrlEncodedString(string ra { return Base64UrlEncoding.Decode(rawString, startIndex, length, ParseDocument); } - internal static string GetStringClaimValueType(string str, string claimType) - { - if (!string.IsNullOrEmpty(claimType) && !AppContextSwitches.TryAllStringClaimsAsDateTime && JsonSerializerPrimitives.IsKnownToNotBeDateTime(claimType)) - return ClaimValueTypes.String; - - if (DateTime.TryParse(str, out DateTime dateTimeValue)) - { - string dtUniversal = dateTimeValue.ToUniversalTime().ToString("o", CultureInfo.InvariantCulture); - if (dtUniversal.Equals(str, StringComparison.Ordinal)) - return ClaimValueTypes.DateTime; - } - - return ClaimValueTypes.String; - } #endif } } diff --git a/src/Microsoft.IdentityModel.Tokens/Json/JsonSerializerPrimitives.cs b/src/Microsoft.IdentityModel.Tokens/Json/JsonSerializerPrimitives.cs deleted file mode 100644 index 758fee4753..0000000000 --- a/src/Microsoft.IdentityModel.Tokens/Json/JsonSerializerPrimitives.cs +++ /dev/null @@ -1,128 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Generic; - -namespace Microsoft.IdentityModel.Tokens.Json -{ - internal static class JsonSerializerPrimitives - { -#if !NET45 - public static bool TryAllStringClaimsAsDateTime() - { - return AppContextSwitches.TryAllStringClaimsAsDateTime; - } -#endif - - /// - /// This is a non-exhaustive list of claim types that are not expected to be DateTime values - /// sourced from expected Entra V1 and V2 claims, OpenID Connect claims, and a selection of - /// restricted claim names. - /// - private static readonly HashSet s_knownNonDateTimeClaimTypes = new(StringComparer.Ordinal) - { - // Header Values. - "alg", - "cty", - "crit", - "enc", - "jku", - "jwk", - "kid", - "typ", - "x5c", - "x5t", - "x5t#S256", - "x5u", - "zip", - // JWT claims. - "acr", - "acrs", - "access_token", - "account_type", - "acct", - "actor", - "actort", - "actortoken", - "aio", - "altsecid", - "amr", - "app_displayname", - "appid", - "appidacr", - "at_hash", - "aud", - "authorization_code", - "azp", - "azpacr", - "c_hash", - "cnf", - "capolids", - "ctry", - "email", - "family_name", - "fwd", - "gender", - "given_name", - "groups", - "hasgroups", - "idp", - "idtyp", - "in_corp", - "ipaddr", - "iss", - "jti", - "login_hint", - "name", - "nameid", - "nickname", - "nonce", - "oid", - "onprem_sid", - "phone_number", - "phone_number_verified", - "pop_jwk", - "preferred_username", - "prn", - "puid", - "pwd_url", - "rh", - "role", - "roles", - "secaud", - "sid", - "sub", - "tenant_ctry", - "tenant_region_scope", - "tid", - "unique_name", - "upn", - "uti", - "ver", - "verified_primary_email", - "verified_secondary_email", - "vnet", - "website", - "wids", - "xms_cc", - "xms_edov", - "xms_pdl", - "xms_pl", - "xms_tpl", - "ztdid" - }; - - internal static bool IsKnownToNotBeDateTime(string claimType) - { - if (string.IsNullOrEmpty(claimType)) - return true; - - if (s_knownNonDateTimeClaimTypes.Contains(claimType)) - return true; - - return false; - } - } -} From c271a5b005d4630a8526eb8a2bf33e69bdd76b78 Mon Sep 17 00:00:00 2001 From: Westin Musser Date: Fri, 9 Aug 2024 14:52:08 -0700 Subject: [PATCH 4/8] unneeded using --- src/Microsoft.IdentityModel.JsonWebTokens/JwtTokenUtilities.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Microsoft.IdentityModel.JsonWebTokens/JwtTokenUtilities.cs b/src/Microsoft.IdentityModel.JsonWebTokens/JwtTokenUtilities.cs index 1d07ac7e0b..d8bae3878a 100644 --- a/src/Microsoft.IdentityModel.JsonWebTokens/JwtTokenUtilities.cs +++ b/src/Microsoft.IdentityModel.JsonWebTokens/JwtTokenUtilities.cs @@ -12,7 +12,6 @@ using Microsoft.IdentityModel.Logging; using Microsoft.IdentityModel.Tokens; using TokenLogMessages = Microsoft.IdentityModel.Tokens.LogMessages; -using System.Security.Claims; #if !NET45 using System.IO; From 71767bec64a65a705c8be81b8b54df0e553c961e Mon Sep 17 00:00:00 2001 From: Westin Musser Date: Sun, 11 Aug 2024 12:16:09 -0700 Subject: [PATCH 5/8] remove unused switches --- .../AppContextSwitches.cs | 37 ------------------- 1 file changed, 37 deletions(-) diff --git a/src/Microsoft.IdentityModel.Tokens/AppContextSwitches.cs b/src/Microsoft.IdentityModel.Tokens/AppContextSwitches.cs index 5a0af16bce..6e991f357b 100644 --- a/src/Microsoft.IdentityModel.Tokens/AppContextSwitches.cs +++ b/src/Microsoft.IdentityModel.Tokens/AppContextSwitches.cs @@ -30,34 +30,6 @@ internal static class AppContextSwitches internal static bool DoNotFailOnMissingTid => _doNotFailOnMissingTid ??= (AppContext.TryGetSwitch(DoNotFailOnMissingTidSwitch, out bool doNotFailOnMissingTid) && doNotFailOnMissingTid); - /// - /// When reading claims from the token, specifies whether to try to convert all string claims to DateTime. - /// Some claims are known not to be DateTime, so conversion is skipped. - /// - internal const string TryAllStringClaimsAsDateTimeSwitch = "Switch.Microsoft.IdentityModel.TryAllStringClaimsAsDateTime"; - - private static bool? _tryAllStringClaimsAsDateTime; - - internal static bool TryAllStringClaimsAsDateTime => _tryAllStringClaimsAsDateTime ??= (AppContext.TryGetSwitch(TryAllStringClaimsAsDateTimeSwitch, out bool tryAsDateTime) && tryAsDateTime); - - /// - /// Controls whether to validate the length of the authentication tag when decrypting a token. - /// - internal const string SkipValidationOfAuthenticationTagLengthSwitch = "Switch.Microsoft.IdentityModel.SkipAuthenticationTagLengthValidation"; - - private static bool? _skipValidationOfAuthenticationTagLength; - - internal static bool ShouldValidateAuthenticationTagLength => _skipValidationOfAuthenticationTagLength ??= !(AppContext.TryGetSwitch(SkipValidationOfAuthenticationTagLengthSwitch, out bool skipValidation) && skipValidation); - - /// - /// Controls whether to use the short name for the RSA OAEP key wrap algorithm. - /// - internal const string UseShortNameForRsaOaepKeySwitch = "Switch.Microsoft.IdentityModel.UseShortNameForRsaOaepKey"; - - private static bool? _useShortNameForRsaOaepKey; - - internal static bool ShouldUseShortNameForRsaOaepKey => _useShortNameForRsaOaepKey ??= AppContext.TryGetSwitch(UseShortNameForRsaOaepKeySwitch, out var useKeyWrap) && useKeyWrap; - /// /// Used for testing to reset all switches to its default value. /// @@ -68,15 +40,6 @@ internal static void ResetAllSwitches() _doNotFailOnMissingTid = null; AppContext.SetSwitch(DoNotFailOnMissingTidSwitch, false); - - _tryAllStringClaimsAsDateTime = null; - AppContext.SetSwitch(TryAllStringClaimsAsDateTimeSwitch, false); - - _skipValidationOfAuthenticationTagLength = null; - AppContext.SetSwitch(SkipValidationOfAuthenticationTagLengthSwitch, false); - - _useShortNameForRsaOaepKey = null; - AppContext.SetSwitch(UseShortNameForRsaOaepKeySwitch, false); } #else // .NET 4.5 does not support AppContext switches. Always use ClaimsIdentity. From ee1ac7f1ae19fce73ba0472d913d43c15cf1447e Mon Sep 17 00:00:00 2001 From: Westin Musser Date: Mon, 12 Aug 2024 13:16:51 -0700 Subject: [PATCH 6/8] PR feedback --- .../AppContextSwitches.cs | 22 +++++++--- .../ClaimsIdentityFactory.cs | 41 +++++++++++-------- .../CryptoProviderFactory.cs | 6 +-- .../AadTokenValidationParametersExtension.cs | 9 ---- .../JsonWebTokenHandlerClaimsIdentityTests.cs | 16 ++++---- .../JsonWebTokenTests.cs | 8 ++-- .../CaseSensitiveClaimsIdentityTests.cs | 4 +- .../ClaimsIdentityFactoryTests.cs | 14 +++---- .../SignatureProviderTests.cs | 4 +- 9 files changed, 65 insertions(+), 59 deletions(-) diff --git a/src/Microsoft.IdentityModel.Tokens/AppContextSwitches.cs b/src/Microsoft.IdentityModel.Tokens/AppContextSwitches.cs index 6e991f357b..3e8b0bf066 100644 --- a/src/Microsoft.IdentityModel.Tokens/AppContextSwitches.cs +++ b/src/Microsoft.IdentityModel.Tokens/AppContextSwitches.cs @@ -13,13 +13,13 @@ internal static class AppContextSwitches { #if NET461_OR_GREATER || NETCOREAPP || NETSTANDARD /// - /// Enables a fallback to the previous behavior of using instead of globally. + /// Enables a new behavior of using instead of globally. /// - internal const string UseClaimsIdentityTypeSwitch = "Microsoft.IdentityModel.Tokens.UseClaimsIdentityType"; + internal const string UseCaseSensitiveClaimsIdentityTypeSwitch = "Microsoft.IdentityModel.Tokens.UseCaseSensitiveClaimsIdentityType"; - private static bool? _useClaimsIdentityType; + private static bool? _useCaseSensitiveClaimsIdentityType; - internal static bool UseClaimsIdentityType => _useClaimsIdentityType ??= (AppContext.TryGetSwitch(UseClaimsIdentityTypeSwitch, out bool useClaimsIdentityType) && useClaimsIdentityType); + internal static bool UseCaseSensitiveClaimsIdentityType => _useCaseSensitiveClaimsIdentityType ??= (AppContext.TryGetSwitch(UseCaseSensitiveClaimsIdentityTypeSwitch, out bool useCaseSensitiveClaimsIdentityType) && useCaseSensitiveClaimsIdentityType); /// /// When validating the issuer signing key, specifies whether to fail if the 'tid' claim is missing. @@ -30,16 +30,26 @@ internal static class AppContextSwitches internal static bool DoNotFailOnMissingTid => _doNotFailOnMissingTid ??= (AppContext.TryGetSwitch(DoNotFailOnMissingTidSwitch, out bool doNotFailOnMissingTid) && doNotFailOnMissingTid); + + internal const string SkipValidationOfHmacKey = "Switch.Microsoft.IdentityModel.UnsafeRelaxHmacKeySizeValidation"; + + private static bool? _skipValidationOfHmacKeySizes; + + internal static bool SkipValidationOfHmacKeySizes => _skipValidationOfHmacKeySizes ??= (AppContext.TryGetSwitch(SkipValidationOfHmacKey, out bool skipValidationOfHmacKeySizes) && skipValidationOfHmacKeySizes); + /// /// Used for testing to reset all switches to its default value. /// internal static void ResetAllSwitches() { - _useClaimsIdentityType = null; - AppContext.SetSwitch(UseClaimsIdentityTypeSwitch, false); + _useCaseSensitiveClaimsIdentityType = null; + AppContext.SetSwitch(UseCaseSensitiveClaimsIdentityTypeSwitch, false); _doNotFailOnMissingTid = null; AppContext.SetSwitch(DoNotFailOnMissingTidSwitch, false); + + _skipValidationOfHmacKeySizes = null; + AppContext.SetSwitch(SkipValidationOfHmacKey, false); } #else // .NET 4.5 does not support AppContext switches. Always use ClaimsIdentity. diff --git a/src/Microsoft.IdentityModel.Tokens/ClaimsIdentityFactory.cs b/src/Microsoft.IdentityModel.Tokens/ClaimsIdentityFactory.cs index ec7201b159..9adc31891d 100644 --- a/src/Microsoft.IdentityModel.Tokens/ClaimsIdentityFactory.cs +++ b/src/Microsoft.IdentityModel.Tokens/ClaimsIdentityFactory.cs @@ -8,7 +8,7 @@ namespace Microsoft.IdentityModel.Tokens { #if !NET45 /// - /// Facilitates the creation of and instances based on the . + /// Facilitates the creation of and instances based on the . /// #endif @@ -16,33 +16,40 @@ internal static class ClaimsIdentityFactory { internal static ClaimsIdentity Create(IEnumerable claims) { -#if !NET45 - if (AppContextSwitches.UseClaimsIdentityType) - return new ClaimsIdentity(claims); +#if NET45 + if (AppContextSwitches.UseCaseSensitiveClaimsIdentityType()) +#else + if (AppContextSwitches.UseCaseSensitiveClaimsIdentityType) #endif - return new CaseSensitiveClaimsIdentity(claims); + return new CaseSensitiveClaimsIdentity(claims); + + return new ClaimsIdentity(claims); } internal static ClaimsIdentity Create(IEnumerable claims, string authenticationType) { -#if !NET45 - if (AppContextSwitches.UseClaimsIdentityType) - return new ClaimsIdentity(claims, authenticationType); +#if NET45 + if (AppContextSwitches.UseCaseSensitiveClaimsIdentityType()) +#else + if (AppContextSwitches.UseCaseSensitiveClaimsIdentityType) #endif - return new CaseSensitiveClaimsIdentity(claims, authenticationType); + return new CaseSensitiveClaimsIdentity(claims, authenticationType); + + return new ClaimsIdentity(claims, authenticationType); } internal static ClaimsIdentity Create(string authenticationType, string nameType, string roleType, SecurityToken securityToken) { -#if !NET45 - - if (AppContextSwitches.UseClaimsIdentityType) - return new ClaimsIdentity(authenticationType: authenticationType, nameType: nameType, roleType: roleType); +#if NET45 + if (AppContextSwitches.UseCaseSensitiveClaimsIdentityType()) +#else + if (AppContextSwitches.UseCaseSensitiveClaimsIdentityType) #endif - return new CaseSensitiveClaimsIdentity(authenticationType: authenticationType, nameType: nameType, roleType: roleType) - { - SecurityToken = securityToken, - }; + return new CaseSensitiveClaimsIdentity(authenticationType: authenticationType, nameType: nameType, roleType: roleType) + { + SecurityToken = securityToken, + }; + return new ClaimsIdentity(authenticationType: authenticationType, nameType: nameType, roleType: roleType); } } } diff --git a/src/Microsoft.IdentityModel.Tokens/CryptoProviderFactory.cs b/src/Microsoft.IdentityModel.Tokens/CryptoProviderFactory.cs index 6b3243c11f..074416d995 100644 --- a/src/Microsoft.IdentityModel.Tokens/CryptoProviderFactory.cs +++ b/src/Microsoft.IdentityModel.Tokens/CryptoProviderFactory.cs @@ -19,9 +19,7 @@ public class CryptoProviderFactory private static object _cacheLock = new object(); private static int _defaultSignatureProviderObjectPoolCacheSize = Environment.ProcessorCount * 4; private int _signatureProviderObjectPoolCacheSize = _defaultSignatureProviderObjectPoolCacheSize; -#if !NET45 - internal const string _skipValidationOfHmacKeySizes = "Switch.Microsoft.IdentityModel.UnsafeRelaxHmacKeySizeValidation"; -#endif + /// /// Returns the default instance. /// @@ -492,7 +490,7 @@ public virtual KeyedHashAlgorithm CreateKeyedHashAlgorithm(byte[] keyBytes, stri private static void ValidateKeySize(byte[] keyBytes, string algorithm, int expectedNumberOfBytes) { #if !NET45 - if (AppContext.TryGetSwitch(_skipValidationOfHmacKeySizes, out bool skipValidationOfHmacKeySize) && skipValidationOfHmacKeySize) + if (AppContextSwitches.SkipValidationOfHmacKeySizes) return; #endif if (keyBytes.Length < expectedNumberOfBytes) diff --git a/src/Microsoft.IdentityModel.Validators/AadTokenValidationParametersExtension.cs b/src/Microsoft.IdentityModel.Validators/AadTokenValidationParametersExtension.cs index daaa9d8ae2..03bd391a7f 100644 --- a/src/Microsoft.IdentityModel.Validators/AadTokenValidationParametersExtension.cs +++ b/src/Microsoft.IdentityModel.Validators/AadTokenValidationParametersExtension.cs @@ -44,15 +44,6 @@ public static void EnableAadSigningKeyIssuerValidation(this TokenValidationParam }; } -#if !NET45 - internal const string DoNotFailOnMissingTidSwitch = "Switch.Microsoft.IdentityModel.DontFailOnMissingTidValidateIssuerSigning"; - - private static bool DontFailOnMissingTid() - { - return (AppContext.TryGetSwitch(DoNotFailOnMissingTidSwitch, out bool doNotFailOnMissingTid) && doNotFailOnMissingTid); - } -#endif - /// /// Validates the issuer signing key. /// diff --git a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandlerClaimsIdentityTests.cs b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandlerClaimsIdentityTests.cs index 1a79169b42..0089fc6cbe 100644 --- a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandlerClaimsIdentityTests.cs +++ b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandlerClaimsIdentityTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. using System; @@ -13,9 +13,12 @@ namespace Microsoft.IdentityModel.JsonWebTokens.Tests public class JsonWebTokenHandlerClaimsIdentityTests { +#if NET46_OR_GREATER || NETCOREAPP || NETSTANDARD [Fact] public void CreateClaimsIdentity_ReturnsCaseSensitveClaimsIdentity_WithAppContextSwitch() { + AppContext.SetSwitch(AppContextSwitches.UseCaseSensitiveClaimsIdentityTypeSwitch, true); + var handler = new DerivedJsonWebTokenHandler(); var jsonWebToken = new JsonWebToken(Default.Jwt(Default.SecurityTokenDescriptor())); var tokenValidationParameters = new TokenValidationParameters(); @@ -37,17 +40,14 @@ public void CreateClaimsIdentity_ReturnsCaseSensitveClaimsIdentity_WithAppContex actualClaimsIdentity = handler.CreateClaimsIdentityInternal(jsonWebToken, tokenValidationParameters, Default.Issuer); Assert.IsType(actualClaimsIdentity); Assert.NotNull(((CaseSensitiveClaimsIdentity)actualClaimsIdentity).SecurityToken); -#if !NET452 + AppContextSwitches.ResetAllSwitches(); -#endif } +#endif -#if NET46_OR_GREATER || NETCOREAPP || NETSTANDARD [Fact] public void CreateClaimsIdentity_ReturnsClaimsIdentity_ByDefault() { - AppContext.SetSwitch(AppContextSwitches.UseClaimsIdentityTypeSwitch, true); - var handler = new DerivedJsonWebTokenHandler(); var jsonWebToken = new JsonWebToken(Default.Jwt(Default.SecurityTokenDescriptor())); var tokenValidationParameters = new TokenValidationParameters(); @@ -58,10 +58,10 @@ public void CreateClaimsIdentity_ReturnsClaimsIdentity_ByDefault() // This will also test mapped claims flow. handler.MapInboundClaims = true; Assert.IsType(handler.CreateClaimsIdentityInternal(jsonWebToken, tokenValidationParameters, Default.Issuer)); - +#if !NET452 AppContextSwitches.ResetAllSwitches(); - } #endif + } private class DerivedJsonWebTokenHandler : JsonWebTokenHandler { diff --git a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenTests.cs b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenTests.cs index 62fa2cc891..c80ce297a3 100644 --- a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenTests.cs +++ b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenTests.cs @@ -596,7 +596,7 @@ public static TheoryData ParseTokenTheoryData theoryData.Add(new JwtTheoryData(nameof(EncodedJwts.InvalidHeader)) { Token = EncodedJwts.InvalidHeader, -#if NET45 +#if NET452 ExpectedException = new ExpectedException(typeof(ArgumentException), "IDX14102:", typeof(JsonReaderException), false ), #else ExpectedException = new ExpectedException(typeof(ArgumentException), "IDX14102:", typeof(JsonReaderException), true), @@ -606,7 +606,7 @@ public static TheoryData ParseTokenTheoryData theoryData.Add(new JwtTheoryData(nameof(EncodedJwts.InvalidPayload)) { Token = EncodedJwts.InvalidPayload, -#if NET45 +#if NET452 ExpectedException = new ExpectedException(typeof(ArgumentException), "IDX14101:", typeof(JsonReaderException), false ), #else ExpectedException = new ExpectedException(typeof(ArgumentException), "IDX14101:", typeof(JsonReaderException), true), @@ -616,7 +616,7 @@ public static TheoryData ParseTokenTheoryData theoryData.Add(new JwtTheoryData(nameof(EncodedJwts.JWSEmptyHeader)) { Token = EncodedJwts.JWSEmptyHeader, -#if NET45 +#if NET452 ExpectedException = new ExpectedException(typeof(ArgumentException), "IDX14102:", typeof(JsonReaderException), false ), #else ExpectedException = new ExpectedException(typeof(ArgumentException), "IDX14102:", typeof(JsonReaderException), true), @@ -626,7 +626,7 @@ public static TheoryData ParseTokenTheoryData theoryData.Add(new JwtTheoryData(nameof(EncodedJwts.JWSEmptyPayload)) { Token = EncodedJwts.JWSEmptyPayload, -#if NET45 +#if NET452 ExpectedException = new ExpectedException(typeof(ArgumentException), "IDX14101:", typeof(JsonReaderException), false ), #else ExpectedException = new ExpectedException(typeof(ArgumentException), "IDX14101:", typeof(JsonReaderException), true), diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/CaseSensitiveClaimsIdentityTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/CaseSensitiveClaimsIdentityTests.cs index d0eadf09b1..18241e4e26 100644 --- a/test/Microsoft.IdentityModel.Tokens.Tests/CaseSensitiveClaimsIdentityTests.cs +++ b/test/Microsoft.IdentityModel.Tokens.Tests/CaseSensitiveClaimsIdentityTests.cs @@ -228,12 +228,12 @@ public class CaseSensitiveClaimsIdentityTheoryData(string testId) : TheoryDataBa private static ClaimsIdentity CreateCaseSensitiveClaimsIdentity(JObject claims, TokenValidationParameters validationParameters = null) { - AppContext.SetSwitch(AppContextSwitches.UseClaimsIdentityTypeSwitch, false); + AppContext.SetSwitch(AppContextSwitches.UseCaseSensitiveClaimsIdentityTypeSwitch, true); var handler = new JsonWebTokenHandler(); var claimsIdentity = handler.CreateClaimsIdentityInternal(new JsonWebToken(CreateUnsignedToken(claims)), validationParameters ?? new TokenValidationParameters(), Default.Issuer); - AppContext.SetSwitch(AppContextSwitches.UseClaimsIdentityTypeSwitch, true); + AppContext.SetSwitch(AppContextSwitches.UseCaseSensitiveClaimsIdentityTypeSwitch, false); return claimsIdentity; } diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/ClaimsIdentityFactoryTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/ClaimsIdentityFactoryTests.cs index 5f5a6d6866..169ea80e36 100644 --- a/test/Microsoft.IdentityModel.Tokens.Tests/ClaimsIdentityFactoryTests.cs +++ b/test/Microsoft.IdentityModel.Tokens.Tests/ClaimsIdentityFactoryTests.cs @@ -19,7 +19,7 @@ public class ClaimsIdentityFactoryTests [InlineData(false)] public void Create_FromTokenValidationParameters_ReturnsCorrectClaimsIdentity(bool useCaseSensitiveClaimsIdentity) { - AppContext.SetSwitch(AppContextSwitches.UseClaimsIdentityTypeSwitch, useCaseSensitiveClaimsIdentity); + AppContext.SetSwitch(AppContextSwitches.UseCaseSensitiveClaimsIdentityTypeSwitch, useCaseSensitiveClaimsIdentity); var jsonWebToken = new JsonWebToken(Default.Jwt(Default.SecurityTokenDescriptor())); var tokenValidationParameters = new TokenValidationParameters(); @@ -34,15 +34,15 @@ public void Create_FromTokenValidationParameters_ReturnsCorrectClaimsIdentity(bo Assert.Equal(tokenValidationParameters.RoleClaimType, actualClaimsIdentity.RoleClaimType); if (useCaseSensitiveClaimsIdentity) - { - Assert.IsType(actualClaimsIdentity); - } - else { Assert.IsType(actualClaimsIdentity); Assert.NotNull(((CaseSensitiveClaimsIdentity)actualClaimsIdentity).SecurityToken); Assert.Equal(jsonWebToken, ((CaseSensitiveClaimsIdentity)actualClaimsIdentity).SecurityToken); } + else + { + Assert.IsType(actualClaimsIdentity); + } AppContextSwitches.ResetAllSwitches(); } @@ -53,7 +53,7 @@ public void Create_FromTokenValidationParameters_ReturnsCorrectClaimsIdentity(bo [InlineData(false, false)] public void Create_FromDerivedTokenValidationParameters_ReturnsCorrectClaimsIdentity(bool tvpReturnsCaseSensitiveClaimsIdentity, bool tvpReturnsCaseSensitiveClaimsIdentityWithToken) { - AppContext.SetSwitch(AppContextSwitches.UseClaimsIdentityTypeSwitch, true); + AppContext.SetSwitch(AppContextSwitches.UseCaseSensitiveClaimsIdentityTypeSwitch, true); var jsonWebToken = new JsonWebToken(Default.Jwt(Default.SecurityTokenDescriptor())); var tokenValidationParameters = new DerivedTokenValidationParameters(tvpReturnsCaseSensitiveClaimsIdentity, tvpReturnsCaseSensitiveClaimsIdentityWithToken); @@ -87,7 +87,7 @@ public void Create_FromDerivedTokenValidationParameters_ReturnsCorrectClaimsIden Assert.Equal(tokenValidationParameters.NameClaimType, actualClaimsIdentity.NameClaimType); Assert.Equal(tokenValidationParameters.RoleClaimType, actualClaimsIdentity.RoleClaimType); - AppContext.SetSwitch(AppContextSwitches.UseClaimsIdentityTypeSwitch, false); + AppContext.SetSwitch(AppContextSwitches.UseCaseSensitiveClaimsIdentityTypeSwitch, false); } #endif diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/SignatureProviderTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/SignatureProviderTests.cs index 53b3313d65..7684e0dea1 100644 --- a/test/Microsoft.IdentityModel.Tokens.Tests/SignatureProviderTests.cs +++ b/test/Microsoft.IdentityModel.Tokens.Tests/SignatureProviderTests.cs @@ -745,7 +745,7 @@ public void SymmetricSecurityKeySizesSign(SymmetricSignatureProviderTheoryData t [Theory, MemberData(nameof(SymmetricSecurityKeySizesVerifyOFFTheoryData))] public void SymmetricSecurityKeySizesVerifyOFF(SymmetricSignatureProviderTheoryData theoryData) { - AppContext.SetSwitch(CryptoProviderFactory._skipValidationOfHmacKeySizes, true); + AppContext.SetSwitch(AppContextSwitches.SkipValidationOfHmacKey, true); var context = TestUtilities.WriteHeader($"{this}.SymmetricSecurityKeySizes", theoryData); try { @@ -759,7 +759,7 @@ public void SymmetricSecurityKeySizesVerifyOFF(SymmetricSignatureProviderTheoryD theoryData.ExpectedException.ProcessException(ex, context); } - AppContext.SetSwitch(CryptoProviderFactory._skipValidationOfHmacKeySizes, false); + AppContext.SetSwitch(AppContextSwitches.SkipValidationOfHmacKey, false); TestUtilities.AssertFailIfErrors(context); } From 278c0430bc01d0b5175056848d5e16ee6d3738e7 Mon Sep 17 00:00:00 2001 From: Westin Musser Date: Mon, 12 Aug 2024 13:40:27 -0700 Subject: [PATCH 7/8] fix reference to switch --- .../AadSigningKeyIssuerValidatorTests.cs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/test/Microsoft.IdentityModel.Validators.Tests/AadSigningKeyIssuerValidatorTests.cs b/test/Microsoft.IdentityModel.Validators.Tests/AadSigningKeyIssuerValidatorTests.cs index 2ba51f4a3c..f7503bc1ec 100644 --- a/test/Microsoft.IdentityModel.Validators.Tests/AadSigningKeyIssuerValidatorTests.cs +++ b/test/Microsoft.IdentityModel.Validators.Tests/AadSigningKeyIssuerValidatorTests.cs @@ -350,8 +350,8 @@ public static TheoryData ValidateIssuerSigningKey SecurityKey = KeyingMaterial.JsonWebKeyP256, SecurityToken = new JwtSecurityToken(), OpenIdConnectConfiguration = mockConfiguration, - SetupAction = () => AppContext.SetSwitch(AadTokenValidationParametersExtension.DoNotFailOnMissingTidSwitch, true), - TearDownAction = () => AppContext.SetSwitch(AadTokenValidationParametersExtension.DoNotFailOnMissingTidSwitch, false) + SetupAction = () => AppContext.SetSwitch(AppContextSwitches.DoNotFailOnMissingTidSwitch, true), + TearDownAction = () => AppContext.SetSwitch(AppContextSwitches.DoNotFailOnMissingTidSwitch, false) }); theoryData.Add(new AadSigningKeyIssuerTheoryData @@ -361,8 +361,8 @@ public static TheoryData ValidateIssuerSigningKey SecurityToken = new JwtSecurityToken(), OpenIdConnectConfiguration = mockConfiguration, ExpectedException = ExpectedException.SecurityTokenInvalidIssuerException("IDX40009"), - SetupAction = () => AppContext.SetSwitch(AadTokenValidationParametersExtension.DoNotFailOnMissingTidSwitch, false), - TearDownAction = () => AppContext.SetSwitch(AadTokenValidationParametersExtension.DoNotFailOnMissingTidSwitch, isEnabled: false) + SetupAction = () => AppContext.SetSwitch(AppContextSwitches.DoNotFailOnMissingTidSwitch, false), + TearDownAction = () => AppContext.SetSwitch(AppContextSwitches.DoNotFailOnMissingTidSwitch, isEnabled: false) }); theoryData.Add(new AadSigningKeyIssuerTheoryData @@ -371,8 +371,8 @@ public static TheoryData ValidateIssuerSigningKey SecurityKey = KeyingMaterial.JsonWebKeyP256, SecurityToken = new JwtSecurityToken(), OpenIdConnectConfiguration = mockConfiguration, - SetupAction = () => AppContext.SetSwitch(AadTokenValidationParametersExtension.DoNotFailOnMissingTidSwitch, true), - TearDownAction = () => AppContext.SetSwitch(AadTokenValidationParametersExtension.DoNotFailOnMissingTidSwitch, false) + SetupAction = () => AppContext.SetSwitch(AppContextSwitches.DoNotFailOnMissingTidSwitch, true), + TearDownAction = () => AppContext.SetSwitch(AppContextSwitches.DoNotFailOnMissingTidSwitch, false) }); theoryData.Add(new AadSigningKeyIssuerTheoryData @@ -382,8 +382,8 @@ public static TheoryData ValidateIssuerSigningKey SecurityToken = new JsonWebToken(Default.Jwt(Default.SecurityTokenDescriptor(Default.SymmetricSigningCredentials, [issClaim]))), OpenIdConnectConfiguration = mockConfiguration, ExpectedException = ExpectedException.SecurityTokenInvalidIssuerException("IDX40009"), - SetupAction = () => AppContext.SetSwitch(AadTokenValidationParametersExtension.DoNotFailOnMissingTidSwitch, false), - TearDownAction = () => AppContext.SetSwitch(AadTokenValidationParametersExtension.DoNotFailOnMissingTidSwitch, isEnabled: false) + SetupAction = () => AppContext.SetSwitch(AppContextSwitches.DoNotFailOnMissingTidSwitch, false), + TearDownAction = () => AppContext.SetSwitch(AppContextSwitches.DoNotFailOnMissingTidSwitch, isEnabled: false) }); theoryData.Add(new AadSigningKeyIssuerTheoryData @@ -392,8 +392,8 @@ public static TheoryData ValidateIssuerSigningKey SecurityKey = KeyingMaterial.JsonWebKeyP256, SecurityToken = new JsonWebToken(Default.Jwt(Default.SecurityTokenDescriptor(Default.SymmetricSigningCredentials, [issClaim]))), OpenIdConnectConfiguration = mockConfiguration, - SetupAction = () => AppContext.SetSwitch(AadTokenValidationParametersExtension.DoNotFailOnMissingTidSwitch, true), - TearDownAction = () => AppContext.SetSwitch(AadTokenValidationParametersExtension.DoNotFailOnMissingTidSwitch, false) + SetupAction = () => AppContext.SetSwitch(AppContextSwitches.DoNotFailOnMissingTidSwitch, true), + TearDownAction = () => AppContext.SetSwitch(AppContextSwitches.DoNotFailOnMissingTidSwitch, false) }); #endif From 46531d0742e066a5dac34c6c080cbf2ae553eabe Mon Sep 17 00:00:00 2001 From: Westin Musser Date: Mon, 12 Aug 2024 14:42:45 -0700 Subject: [PATCH 8/8] small fix --- .../SignatureProviderTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/SignatureProviderTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/SignatureProviderTests.cs index 7684e0dea1..506cb464b8 100644 --- a/test/Microsoft.IdentityModel.Tokens.Tests/SignatureProviderTests.cs +++ b/test/Microsoft.IdentityModel.Tokens.Tests/SignatureProviderTests.cs @@ -759,7 +759,7 @@ public void SymmetricSecurityKeySizesVerifyOFF(SymmetricSignatureProviderTheoryD theoryData.ExpectedException.ProcessException(ex, context); } - AppContext.SetSwitch(AppContextSwitches.SkipValidationOfHmacKey, false); + AppContextSwitches.ResetAllSwitches(); TestUtilities.AssertFailIfErrors(context); }