diff --git a/src/Microsoft.IdentityModel.TestExtensions/TestTokenCreator.cs b/src/Microsoft.IdentityModel.TestExtensions/TestTokenCreator.cs index ace57502c4..5b0db81a15 100644 --- a/src/Microsoft.IdentityModel.TestExtensions/TestTokenCreator.cs +++ b/src/Microsoft.IdentityModel.TestExtensions/TestTokenCreator.cs @@ -357,7 +357,7 @@ public SecurityTokenDescriptor CreateTokenDescriptorWithInstanceOverrides() { var securityTokenDescriptor = new SecurityTokenDescriptor() { - Subject = new ClaimsIdentity(_payloadClaims), + Subject = ClaimsIdentityFactory.Create(_payloadClaims), }; if (!string.IsNullOrEmpty(Issuer)) diff --git a/src/Microsoft.IdentityModel.Tokens.Saml/Saml/SamlSecurityTokenHandler.cs b/src/Microsoft.IdentityModel.Tokens.Saml/Saml/SamlSecurityTokenHandler.cs index 9b70430151..8cc0013ee5 100644 --- a/src/Microsoft.IdentityModel.Tokens.Saml/Saml/SamlSecurityTokenHandler.cs +++ b/src/Microsoft.IdentityModel.Tokens.Saml/Saml/SamlSecurityTokenHandler.cs @@ -870,7 +870,7 @@ protected virtual void SetDelegateFromAttribute(SamlAttribute attribute, ClaimsI } } - subject.Actor = new ClaimsIdentity(claims, "Federation"); + subject.Actor = ClaimsIdentityFactory.Create(claims, "Federation"); SetDelegateFromAttribute(actingAsAttribute, subject.Actor, issuer); } diff --git a/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Saml2SecurityTokenHandler.cs b/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Saml2SecurityTokenHandler.cs index 4100330a51..5c914442da 100644 --- a/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Saml2SecurityTokenHandler.cs +++ b/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Saml2SecurityTokenHandler.cs @@ -1122,7 +1122,7 @@ protected virtual void SetClaimsIdentityActorFromAttribute(Saml2Attribute attrib } } - identity.Actor = new ClaimsIdentity(claims); + identity.Actor = ClaimsIdentityFactory.Create(claims); SetClaimsIdentityActorFromAttribute(actorAttribute, identity.Actor, issuer); } @@ -1308,6 +1308,7 @@ protected virtual ClaimsIdentity CreateClaimsIdentity(Saml2SecurityToken samlTok } var identity = validationParameters.CreateClaimsIdentity(samlToken, actualIssuer); + ProcessSubject(samlToken.Assertion.Subject, identity, actualIssuer); ProcessStatements(samlToken.Assertion.Statements, identity, actualIssuer); diff --git a/src/Microsoft.IdentityModel.Tokens/AppContextSwitches.cs b/src/Microsoft.IdentityModel.Tokens/AppContextSwitches.cs new file mode 100644 index 0000000000..865e7d0de0 --- /dev/null +++ b/src/Microsoft.IdentityModel.Tokens/AppContextSwitches.cs @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Security.Claims; + +namespace Microsoft.IdentityModel.Tokens +{ + /// + /// AppContext switches for Microsoft.IdentityModel.Tokens and referencing packages. + /// + internal static class AppContextSwitches + { + /// + /// Enables a new behavior of using instead of globally. + /// + internal const string UseCaseSensitiveClaimsIdentityTypeSwitch = "Microsoft.IdentityModel.Tokens.UseCaseSensitiveClaimsIdentity"; + + internal static bool UseCaseSensitiveClaimsIdentityType() => (AppContext.TryGetSwitch(UseCaseSensitiveClaimsIdentityTypeSwitch, out bool useCaseSensitiveClaimsIdentityType) && useCaseSensitiveClaimsIdentityType); + } +} diff --git a/src/Microsoft.IdentityModel.Tokens/CaseSensitiveClaimsIdentity.cs b/src/Microsoft.IdentityModel.Tokens/CaseSensitiveClaimsIdentity.cs new file mode 100644 index 0000000000..1c48b36a2c --- /dev/null +++ b/src/Microsoft.IdentityModel.Tokens/CaseSensitiveClaimsIdentity.cs @@ -0,0 +1,122 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Security.Claims; + +namespace Microsoft.IdentityModel.Tokens +{ + /// + /// A derived where claim retrieval is case-sensitive. The current retrieves claims in a case-insensitive manner which is different than querying the underlying . The provides consistent retrieval logic between the and . + /// + public class CaseSensitiveClaimsIdentity : ClaimsIdentity + { + /// + /// Gets the associated with this claims identity. + /// + public SecurityToken SecurityToken { get; internal set; } + + /// + /// Initializes an instance of . + /// + public CaseSensitiveClaimsIdentity() : base() + { + } + + /// + /// Initializes an instance of . + /// + /// The authentication method used to establish this identity. + public CaseSensitiveClaimsIdentity(string authenticationType) : base(authenticationType) + { + } + + /// + /// Initializes an instance of . + /// + /// to copy. + public CaseSensitiveClaimsIdentity(ClaimsIdentity claimsIdentity) : base(claimsIdentity) + { + } + + /// + /// Initializes an instance of . + /// + /// associated with this instance. + public CaseSensitiveClaimsIdentity(IEnumerable claims) : base(claims) + { + } + + /// + /// Initializes an instance of . + /// + /// associated with this instance. + /// The authentication method used to establish this identity. + public CaseSensitiveClaimsIdentity(IEnumerable claims, string authenticationType) : base(claims, authenticationType) + { + } + + /// + /// Initializes an instance of . + /// + /// associated with this instance. + /// The authentication method used to establish this identity. + /// The used when obtaining the value of . + /// The used when performing logic for . + public CaseSensitiveClaimsIdentity(IEnumerable claims, string authenticationType, string nameType, string roleType) : + base(claims, authenticationType, nameType, roleType) + { + } + + /// + /// Initializes an instance of . + /// + /// The authentication method used to establish this identity. + /// The used when obtaining the value of . + /// The used when performing logic for . + public CaseSensitiveClaimsIdentity(string authenticationType, string nameType, string roleType) : + base(authenticationType, nameType, roleType) + { + } + + /// + /// Retrieves a where each equals . + /// + /// The type of the claim to match. + /// A of matched claims. + /// Comparison is . + /// if is null. + public override IEnumerable FindAll(string type) + { + return base.FindAll(claim => claim?.Type.Equals(type, StringComparison.Ordinal) == true); + } + + /// + /// Retrieves the first where equals . + /// + /// The type of the claim to match. + /// A , if nothing matches. + /// Comparison is . + /// if is null. + public override Claim FindFirst(string type) + { + return base.FindFirst(claim => claim?.Type.Equals(type, StringComparison.Ordinal) == true); + } + + /// + /// Determines if a claim with type AND value is contained within this claims identity. + /// + /// The type of the claim to match. + /// The value of the claim to match. + /// true if a claim is matched, false otherwise. + /// Comparison is for and . + /// if is null. + /// if is null. + public override bool HasClaim(string type, string value) + { + return base.HasClaim(claim => claim?.Type.Equals(type, StringComparison.Ordinal) == true + && claim?.Value.Equals(value, StringComparison.Ordinal) == true); + } + } +} diff --git a/src/Microsoft.IdentityModel.Tokens/ClaimsIdentityFactory.cs b/src/Microsoft.IdentityModel.Tokens/ClaimsIdentityFactory.cs new file mode 100644 index 0000000000..1f6f56bc74 --- /dev/null +++ b/src/Microsoft.IdentityModel.Tokens/ClaimsIdentityFactory.cs @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Collections.Generic; +using System.Security.Claims; + +namespace Microsoft.IdentityModel.Tokens +{ + /// + /// Facilitates the creation of and instances based on the . + /// + internal static class ClaimsIdentityFactory + { + internal static ClaimsIdentity Create(IEnumerable claims) + { + if (AppContextSwitches.UseCaseSensitiveClaimsIdentityType()) + return new CaseSensitiveClaimsIdentity(claims); + + return new ClaimsIdentity(claims); + } + + internal static ClaimsIdentity Create(IEnumerable claims, string authenticationType) + { + if (AppContextSwitches.UseCaseSensitiveClaimsIdentityType()) + return new CaseSensitiveClaimsIdentity(claims, authenticationType); + + return new ClaimsIdentity(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, + }; + + return new ClaimsIdentity(authenticationType: authenticationType, nameType: nameType, roleType: roleType); + } + } +} diff --git a/src/Microsoft.IdentityModel.Tokens/TokenHandler.cs b/src/Microsoft.IdentityModel.Tokens/TokenHandler.cs index bba4f7c69b..38843534f1 100644 --- a/src/Microsoft.IdentityModel.Tokens/TokenHandler.cs +++ b/src/Microsoft.IdentityModel.Tokens/TokenHandler.cs @@ -31,8 +31,8 @@ public abstract class TokenHandler /// 'value' less than 1. public virtual int MaximumTokenSizeInBytes { - get => _maximumTokenSizeInBytes; - set => _maximumTokenSizeInBytes = (value < 1) ? throw LogExceptionMessage(new ArgumentOutOfRangeException(nameof(value), FormatInvariant(LogMessages.IDX10101, LogHelper.MarkAsNonPII(value)))) : value; + get => _maximumTokenSizeInBytes; + set => _maximumTokenSizeInBytes = (value < 1) ? throw LogExceptionMessage(new ArgumentOutOfRangeException(nameof(value), FormatInvariant(LogMessages.IDX10101, MarkAsNonPII(value)))) : value; } /// @@ -54,7 +54,6 @@ public int TokenLifetimeInMinutes } #region methods - /// /// Validates a token. /// On a validation failure, no exception will be thrown; instead, the exception will be set in the returned TokenValidationResult.Exception property. diff --git a/src/Microsoft.IdentityModel.Tokens/TokenValidationParameters.cs b/src/Microsoft.IdentityModel.Tokens/TokenValidationParameters.cs index bb68764a6d..4be594cfde 100644 --- a/src/Microsoft.IdentityModel.Tokens/TokenValidationParameters.cs +++ b/src/Microsoft.IdentityModel.Tokens/TokenValidationParameters.cs @@ -255,7 +255,7 @@ public virtual ClaimsIdentity CreateClaimsIdentity(SecurityToken securityToken, if (LogHelper.IsEnabled(EventLogLevel.Informational)) LogHelper.LogInformation(LogMessages.IDX10245, securityToken); - return new ClaimsIdentity(authenticationType: AuthenticationType ?? DefaultAuthenticationType, nameType: nameClaimType ?? ClaimsIdentity.DefaultNameClaimType, roleType: roleClaimType ?? ClaimsIdentity.DefaultRoleClaimType); + return ClaimsIdentityFactory.Create(authenticationType: AuthenticationType ?? DefaultAuthenticationType, nameType: nameClaimType ?? ClaimsIdentity.DefaultNameClaimType, roleType: roleClaimType ?? ClaimsIdentity.DefaultRoleClaimType, securityToken); } /// diff --git a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandlerClaimsIdentityTests.cs b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandlerClaimsIdentityTests.cs new file mode 100644 index 0000000000..e91c6a27b1 --- /dev/null +++ b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandlerClaimsIdentityTests.cs @@ -0,0 +1,74 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Security.Claims; +using Microsoft.IdentityModel.TestUtils; +using Microsoft.IdentityModel.Tokens; +using Xunit; + +namespace Microsoft.IdentityModel.JsonWebTokens.Tests +{ + [Collection(nameof(JsonWebTokenHandlerClaimsIdentityTests))] + public class JsonWebTokenHandlerClaimsIdentityTests + { + [Fact] + public void CreateClaimsIdentity_ReturnsClaimsIdentity_ByDefault() + { + var handler = new DerivedJsonWebTokenHandler(); + var jsonWebToken = new JsonWebToken(Default.Jwt(Default.SecurityTokenDescriptor())); + var tokenValidationParameters = new TokenValidationParameters(); + + var actualClaimsIdentity = handler.CreateClaimsIdentity(jsonWebToken, tokenValidationParameters); + Assert.IsType(actualClaimsIdentity); + + actualClaimsIdentity = handler.CreateClaimsIdentity(jsonWebToken, tokenValidationParameters, Default.Issuer); + Assert.IsType(actualClaimsIdentity); + + actualClaimsIdentity = handler.CreateClaimsIdentityInternal(jsonWebToken, tokenValidationParameters, Default.Issuer); + Assert.IsType(actualClaimsIdentity); + + // This will also test mapped claims flow. + handler.MapInboundClaims = true; + actualClaimsIdentity = handler.CreateClaimsIdentityInternal(jsonWebToken, tokenValidationParameters, Default.Issuer); + Assert.IsType(actualClaimsIdentity); + } + + [Fact] + public void CreateClaimsIdentity_ReturnsCaseSensitiveClaimsIdentity_WithAppContextSwitch() + { + AppContext.SetSwitch(AppContextSwitches.UseCaseSensitiveClaimsIdentityTypeSwitch, true); + + var handler = new DerivedJsonWebTokenHandler(); + var jsonWebToken = new JsonWebToken(Default.Jwt(Default.SecurityTokenDescriptor())); + var tokenValidationParameters = new TokenValidationParameters(); + + var actualClaimsIdentity = handler.CreateClaimsIdentity(jsonWebToken, tokenValidationParameters); + Assert.IsType(actualClaimsIdentity); + Assert.NotNull(((CaseSensitiveClaimsIdentity)actualClaimsIdentity).SecurityToken); + + actualClaimsIdentity = handler.CreateClaimsIdentity(jsonWebToken, tokenValidationParameters, Default.Issuer); + Assert.IsType(actualClaimsIdentity); + Assert.NotNull(((CaseSensitiveClaimsIdentity)actualClaimsIdentity).SecurityToken); + + actualClaimsIdentity = handler.CreateClaimsIdentityInternal(jsonWebToken, tokenValidationParameters, Default.Issuer); + Assert.IsType(actualClaimsIdentity); + Assert.NotNull(((CaseSensitiveClaimsIdentity)actualClaimsIdentity).SecurityToken); + + // This will also test mapped claims flow. + handler.MapInboundClaims = true; + actualClaimsIdentity = handler.CreateClaimsIdentityInternal(jsonWebToken, tokenValidationParameters, Default.Issuer); + Assert.IsType(actualClaimsIdentity); + Assert.NotNull(((CaseSensitiveClaimsIdentity)actualClaimsIdentity).SecurityToken); + + AppContext.SetSwitch(AppContextSwitches.UseCaseSensitiveClaimsIdentityTypeSwitch, false); + } + + private class DerivedJsonWebTokenHandler : JsonWebTokenHandler + { + public new ClaimsIdentity CreateClaimsIdentity(JsonWebToken jwtToken, TokenValidationParameters validationParameters) => base.CreateClaimsIdentity(jwtToken, validationParameters); + public new ClaimsIdentity CreateClaimsIdentity(JsonWebToken jwtToken, TokenValidationParameters validationParameters, string issuer) => base.CreateClaimsIdentity(jwtToken, validationParameters, issuer); + public new ClaimsIdentity CreateClaimsIdentityInternal(SecurityToken securityToken, TokenValidationParameters tokenValidationParameters, string issuer) => base.CreateClaimsIdentityInternal(securityToken, tokenValidationParameters, issuer); + } + } +} diff --git a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandlerTests.cs b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandlerTests.cs index f7b1d7b843..210a2ebcc1 100644 --- a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandlerTests.cs +++ b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandlerTests.cs @@ -538,7 +538,7 @@ public static TheoryData CreateJWEWithAesGcmTheoryData } } - // Tests checks to make sure that the token string created by the JsonWebTokenHandler is consistent with the + // Tests checks to make sure that the token string created by the JsonWebTokenHandler is consistent with the // token string created by the JwtSecurityTokenHandler. [Theory, MemberData(nameof(CreateJWETheoryData))] public void CreateJWE(CreateTokenTheoryData theoryData) @@ -793,7 +793,7 @@ public static TheoryData SecurityTokenDecryptionTheoryDat } } - // Tests checks to make sure that the token string (JWE) created by calling + // Tests checks to make sure that the token string (JWE) created by calling // CreateToken(string payload, SigningCredentials signingCredentials, EncryptingCredentials encryptingCredentials) // is equivalent to the token string created by calling CreateToken(SecurityTokenDescriptor tokenDescriptor). [Theory, MemberData(nameof(CreateJWEUsingSecurityTokenDescriptorTheoryData))] @@ -1149,7 +1149,7 @@ public static TheoryData CreateJWEUsingSecurityTokenDescr } } - // Tests checks to make sure that the token string created by the JsonWebTokenHandler is consistent with the + // Tests checks to make sure that the token string created by the JsonWebTokenHandler is consistent with the // token string created by the JwtSecurityTokenHandler. [Theory, MemberData(nameof(CreateJWSTheoryData))] public void CreateJWS(CreateTokenTheoryData theoryData) @@ -2007,7 +2007,7 @@ public static TheoryData CreateJWSUsingSecurityTokenDescr { // Test checks that the values in SecurityTokenDescriptor.Subject.Claims // are properly combined with those specified in SecurityTokenDescriptor.Claims. - // Duplicate values (if present with different case) should not be overridden. + // Duplicate values (if present with different case) should not be overridden. // For example, the 'aud' claim on TokenDescriptor.Claims will not be overridden // by the 'AUD' claim on TokenDescriptor.Subject.Claims, but the 'exp' claim will. new CreateTokenTheoryData("TokenDescriptorWithBothSubjectAndClaims") @@ -2476,7 +2476,7 @@ public async Task AdditionalHeaderValues() // Test checks to make sure that the token payload retrieved from ValidateToken is the same as the payload - // the token was initially created with. + // the token was initially created with. [Fact] public void RoundTripJWS() { @@ -3004,7 +3004,7 @@ public async Task ValidateJsonWebTokenClaimMapping() TestUtilities.AssertFailIfErrors(context); } - // Test shows if the JwtSecurityTokenHandler has mapping OFF and + // Test shows if the JwtSecurityTokenHandler has mapping OFF and // the JsonWebTokenHandler has mapping ON,the claims are different. [Fact] public async Task ValidateDifferentClaimsBetweenHandlers() diff --git a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenTests.cs b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenTests.cs index 08644135d1..d1f6fd6ad7 100644 --- a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenTests.cs +++ b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenTests.cs @@ -116,7 +116,7 @@ public void JWETouchAllProperties() TestUtilities.AssertFailIfErrors(context); } - // Test checks to make sure that the JsonWebToken.GetClaim() method is able to retrieve every Claim returned by the Claims property (with the exception + // Test checks to make sure that the JsonWebToken.GetClaim() method is able to retrieve every Claim returned by the Claims property (with the exception // of Claims that are JObjects or arrays, as those are converted to strings by the GetClaim() method). [Fact] public void CompareGetClaimAndClaims() diff --git a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JwtTokenUtilitiesTests.cs b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JwtTokenUtilitiesTests.cs index e33b7d78d5..d8d764085e 100644 --- a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JwtTokenUtilitiesTests.cs +++ b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JwtTokenUtilitiesTests.cs @@ -19,7 +19,7 @@ public class JwtTokenUtilitiesTests { // Used for formatting a message for testing with one parameter. private const string TestMessageOneParam = "This is the parameter: '{0}'."; - + [Fact] public void LogSecurityArtifactTest() { diff --git a/test/Microsoft.IdentityModel.Protocols.WsFederation.Tests/WsFederationMessageTests.cs b/test/Microsoft.IdentityModel.Protocols.WsFederation.Tests/WsFederationMessageTests.cs index 56e528849f..b3b6aab8f4 100644 --- a/test/Microsoft.IdentityModel.Protocols.WsFederation.Tests/WsFederationMessageTests.cs +++ b/test/Microsoft.IdentityModel.Protocols.WsFederation.Tests/WsFederationMessageTests.cs @@ -474,7 +474,7 @@ public static TheoryData GetTokenParsingStringDat TestId = nameof(ReferenceXml.WresultWsTrust14WithoutNamespaceUnusualSpacing) }, // these tests show that one shouldn't rely on parsing the Wresult alone as - // the following Wresult's should error. The correct pattern is to call GetToken() or GetTokenUsingXmlReader() to ensure + // the following Wresult's should error. The correct pattern is to call GetToken() or GetTokenUsingXmlReader() to ensure // the Wresult is well formed. new WsFederationMessageTheoryData { diff --git a/test/Microsoft.IdentityModel.TestUtils/ClaimSets.cs b/test/Microsoft.IdentityModel.TestUtils/ClaimSets.cs index 703e9eabd7..6b5fd051bf 100644 --- a/test/Microsoft.IdentityModel.TestUtils/ClaimSets.cs +++ b/test/Microsoft.IdentityModel.TestUtils/ClaimSets.cs @@ -297,7 +297,7 @@ public static List Simple(string issuer, string originalIssuer) public static IEnumerable SimpleShortClaimtypes(string issuer, string originalIssuer) { return new List() - { + { NewClaimWithShortType(ClaimTypes.Country, "USA", ClaimValueTypes.String, issuer ?? Default.Issuer, originalIssuer ?? Default.OriginalIssuer), NewClaimWithShortType(ClaimTypes.Email, "user@contoso.com", ClaimValueTypes.String, issuer ?? Default.Issuer, originalIssuer ?? Default.OriginalIssuer), NewClaimWithShortType(ClaimTypes.GivenName, "Tony", ClaimValueTypes.String, issuer ?? Default.Issuer, originalIssuer ?? Default.OriginalIssuer ), @@ -376,7 +376,7 @@ public static List DuplicateTypes() { return DuplicateTypes(Default.Issuer, Default.Issuer); } - + /// /// Returns an enumeration containing duplicate claims. Used to test duplicates. /// diff --git a/test/Microsoft.IdentityModel.TestUtils/IdentityComparer.cs b/test/Microsoft.IdentityModel.TestUtils/IdentityComparer.cs index 753b97efad..a9fac40796 100644 --- a/test/Microsoft.IdentityModel.TestUtils/IdentityComparer.cs +++ b/test/Microsoft.IdentityModel.TestUtils/IdentityComparer.cs @@ -44,6 +44,7 @@ public class IdentityComparer { typeof(CanonicalizingTransfrom).ToString(), CompareAllPublicProperties }, { typeof(Claim).ToString(), CompareAllPublicProperties }, { typeof(ClaimsIdentity).ToString(), CompareAllPublicProperties }, + { typeof(CaseSensitiveClaimsIdentity).ToString(), CompareAllPublicProperties }, { typeof(ClaimsPrincipal).ToString(), CompareAllPublicProperties }, { typeof(Collection).ToString(), ContinueCheckingEquality }, { typeof(DateTime).ToString(), AreDateTimesEqual }, @@ -54,6 +55,7 @@ public class IdentityComparer { typeof(IDictionary).ToString(), AreStringDictionariesEqual}, { typeof(IEnumerable).ToString(), AreClaimsEnumsEqual }, { typeof(IEnumerable).ToString(), AreClaimsIdentitiesEnumsEqual }, + { typeof(IEnumerable).ToString(), AreClaimsIdentitiesEnumsEqual }, { typeof(IEnumerable).ToString(), AreObjectEnumsEqual }, { typeof(IEnumerable).ToString(), AreSecurityKeyEnumsEqual }, { typeof(IEnumerable).ToString(), AreStringEnumsEqual }, @@ -1280,6 +1282,12 @@ public static bool CompareAllPublicProperties(object obj1, object obj2, CompareC continue; } + if (type == typeof(CaseSensitiveClaimsIdentity)) + { + if (propertyInfo.Name == "SecurityToken") + continue; + } + if (propertyInfo.GetMethod != null) { object val1 = propertyInfo.GetValue(obj1, null); diff --git a/test/Microsoft.IdentityModel.TestUtils/NotDefault.cs b/test/Microsoft.IdentityModel.TestUtils/NotDefault.cs index a80002b9fb..1eb9d511cb 100644 --- a/test/Microsoft.IdentityModel.TestUtils/NotDefault.cs +++ b/test/Microsoft.IdentityModel.TestUtils/NotDefault.cs @@ -100,7 +100,7 @@ public static string RoleClaimType { get { return Default.RoleClaimType.Replace("Default", "NotDefault"); } } - + public static EncryptingCredentials SymmetricEncryptionCredentials { get { return new EncryptingCredentials(new SymmetricSecurityKey(KeyingMaterial.SymmetricKeyBytes2_256), "dir", SecurityAlgorithms.Aes128CbcHmacSha256); } diff --git a/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlSecurityTokenHandlerTests.cs b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlSecurityTokenHandlerTests.cs index c22802d852..74121b25db 100644 --- a/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlSecurityTokenHandlerTests.cs +++ b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlSecurityTokenHandlerTests.cs @@ -20,7 +20,7 @@ namespace Microsoft.IdentityModel.Tokens.Saml.Tests { /// - /// + /// /// public class SamlSecurityTokenHandlerTests { @@ -120,7 +120,8 @@ public static TheoryData CanReadTokenTheoryData public void CreateClaimsIdentities(SamlTheoryData theoryData) { TestUtilities.WriteHeader($"{this}.CreateClaimsIdentities", theoryData); - var context = new CompareContext($"{this}.CreateClaimsIdentities, {theoryData.TestId}") { IgnoreType = true }; + var context = new CompareContext($"{this}.CreateClaimsIdentities, {theoryData.TestId}"); + try { var identities = ((theoryData.Handler) as SamlSecurityTokenHandlerPublic).CreateClaimsIdentitiesPublic(theoryData.TokenTestSet.SecurityToken as SamlSecurityToken, theoryData.Issuer, theoryData.ValidationParameters); diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/CaseSensitiveClaimsIdentityTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/CaseSensitiveClaimsIdentityTests.cs new file mode 100644 index 0000000000..68b008ea01 --- /dev/null +++ b/test/Microsoft.IdentityModel.Tokens.Tests/CaseSensitiveClaimsIdentityTests.cs @@ -0,0 +1,245 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Claims; +using Microsoft.IdentityModel.JsonWebTokens; +using Microsoft.IdentityModel.TestUtils; +using Newtonsoft.Json.Linq; +using Xunit; +using JwtRegisteredClaimNames = Microsoft.IdentityModel.JsonWebTokens.JwtRegisteredClaimNames; + +namespace Microsoft.IdentityModel.Tokens.Tests +{ + [Collection(nameof(CaseSensitiveClaimsIdentityTests))] + public class CaseSensitiveClaimsIdentityTests + { + private static readonly string LowerCaseClaimName = "tid"; + private static readonly string LowerCaseClaimValue = "tenant"; + private static readonly string UpperCaseClaimName = "TID"; + private static readonly string UpperCaseClaimValue = "TENANT"; + private static readonly JObject _defaultPayload = new() + { + [JwtRegisteredClaimNames.Iss] = Default.Issuer, + [JwtRegisteredClaimNames.Aud] = Default.Audience, + }; + + [Theory, MemberData(nameof(GetCaseSensitiveClaimsIdentityTheoryData))] + public void FindAll_DoesCaseSensitiveSearch(CaseSensitiveClaimsIdentityTheoryData theoryData) + { + var context = TestUtilities.WriteHeader($"{this}.FindAll_DoesCaseSensitiveSearch", theoryData); + + try + { + var actualClaims = theoryData.ClaimsIdentity.FindAll(theoryData.ClaimNameSearch).Select(claim => claim.Type); + + IdentityComparer.AreEqual(theoryData.ExpectedClaims, actualClaims, context); + theoryData.ExpectedException.ProcessNoException(context); + } + catch (Exception ex) + { + theoryData.ExpectedException.ProcessException(ex, context); + } + + TestUtilities.AssertFailIfErrors(context); + } + + [Theory, MemberData(nameof(GetCaseSensitiveClaimsIdentityTheoryData))] + public void FindFirst_DoesCaseSensitiveSearch(CaseSensitiveClaimsIdentityTheoryData theoryData) + { + var context = TestUtilities.WriteHeader($"{this}.FindFirst_DoesCaseSensitiveSearch", theoryData); + + try + { + var actualClaim = theoryData.ClaimsIdentity.FindFirst(theoryData.ClaimNameSearch)?.Type; + + IdentityComparer.AreEqual(theoryData.ExpectedClaim, actualClaim, context); + theoryData.ExpectedException.ProcessNoException(context); + } + catch (Exception ex) + { + theoryData.ExpectedException.ProcessException(ex, context); + } + + TestUtilities.AssertFailIfErrors(context); + } + + [Theory, MemberData(nameof(GetCaseSensitiveClaimsIdentityTheoryData))] + public void HasClaim_DoesCaseSensitiveSearch(CaseSensitiveClaimsIdentityTheoryData theoryData) + { + var context = TestUtilities.WriteHeader($"{this}.HasClaim_DoesCaseSensitiveSearch", theoryData); + + try + { + var actualHasClaim = theoryData.ClaimsIdentity.HasClaim(theoryData.ClaimNameSearch, theoryData.ClaimValueSearch); + + IdentityComparer.AreEqual(theoryData.ExpectedHasClaim, actualHasClaim, context); + theoryData.ExpectedException.ProcessNoException(context); + } + catch (Exception ex) + { + theoryData.ExpectedException.ProcessException(ex, context); + } + + TestUtilities.AssertFailIfErrors(context); + } + + [Fact] + public void AddClaim_AddsClaim() + { + var claimsIdentity = new CaseSensitiveClaimsIdentity(); + + Assert.Empty(claimsIdentity.Claims); + + var claim = new Claim("claimType", "claimValue"); + claimsIdentity.AddClaim(claim); + + Assert.NotEmpty(claimsIdentity.Claims); + } + + [Fact] + public void AddClaim_AddNull_ThrowsException() + { + var claimsIdentity = new CaseSensitiveClaimsIdentity(); + + Assert.Throws(() => claimsIdentity.AddClaim(null)); + } + + [Fact] + public void RemoveClaim_RemovesClaim() + { + var claimsIdentity = new CaseSensitiveClaimsIdentity(); + var claim = new Claim("claimType", "claimValue"); + claimsIdentity.AddClaim(claim); + + Assert.NotEmpty(claimsIdentity.Claims); + + var claimToRemove = claimsIdentity.Claims.First(); + + claimsIdentity.RemoveClaim(claimToRemove); + + Assert.Empty(claimsIdentity.Claims); + } + + [Fact] + public void DefaultProperties_CorrectlySet() + { + var validationParameters = new TokenValidationParameters() + { + NameClaimType = "tvp_name", + RoleClaimType = "tvp_role", + }; + var claimsIdentity = CreateCaseSensitiveClaimsIdentity(new JObject(), validationParameters); + + Assert.Equal(validationParameters.NameClaimType, claimsIdentity.NameClaimType); + Assert.Equal(validationParameters.RoleClaimType, claimsIdentity.RoleClaimType); + } + + public static TheoryData GetCaseSensitiveClaimsIdentityTheoryData + { + get + { + return new TheoryData + { + new CaseSensitiveClaimsIdentityTheoryData("UppercaseSearch_ClaimsExist") + { + ClaimsIdentity = CreateCaseSensitiveClaimsIdentity(new JObject { + [LowerCaseClaimName] = LowerCaseClaimValue, + [UpperCaseClaimName] = UpperCaseClaimValue, + }), + ClaimNameSearch = UpperCaseClaimName, + ClaimValueSearch = UpperCaseClaimValue, + ExpectedHasClaim = true, + ExpectedClaim = UpperCaseClaimName, + ExpectedClaims = [UpperCaseClaimName], + }, + new CaseSensitiveClaimsIdentityTheoryData("LowercaseSearch_ClaimsExist") + { + ClaimsIdentity = CreateCaseSensitiveClaimsIdentity(new JObject { + [UpperCaseClaimName] = UpperCaseClaimValue, + [LowerCaseClaimName] = LowerCaseClaimValue, + }), + ClaimNameSearch = LowerCaseClaimName, + ClaimValueSearch = LowerCaseClaimValue, + ExpectedHasClaim = true, + ExpectedClaim = LowerCaseClaimName, + ExpectedClaims = [LowerCaseClaimName], + }, + new CaseSensitiveClaimsIdentityTheoryData("UppercaseSearch_ClaimsMissing") + { + ClaimsIdentity = CreateCaseSensitiveClaimsIdentity(new JObject { + [LowerCaseClaimName] = LowerCaseClaimValue, + }), + ClaimNameSearch = UpperCaseClaimName, + ClaimValueSearch = UpperCaseClaimValue, + ExpectedHasClaim = false, + ExpectedClaim = null, + ExpectedClaims = [], + }, + new CaseSensitiveClaimsIdentityTheoryData("LowercaseSearch_ClaimsMissing") + { + ClaimsIdentity = CreateCaseSensitiveClaimsIdentity(new JObject { + [UpperCaseClaimName] = UpperCaseClaimValue, + }), + ClaimNameSearch = LowerCaseClaimName, + ClaimValueSearch = LowerCaseClaimValue, + ExpectedHasClaim = false, + ExpectedClaim = null, + ExpectedClaims = [], + }, + new CaseSensitiveClaimsIdentityTheoryData("UppercaseMixedSearch_ClaimsMissing") + { + ClaimsIdentity = CreateCaseSensitiveClaimsIdentity(new JObject { + [LowerCaseClaimName] = UpperCaseClaimValue, + }), + ClaimNameSearch = UpperCaseClaimName, + ClaimValueSearch = UpperCaseClaimValue, + ExpectedHasClaim = false, + ExpectedClaim = null, + ExpectedClaims = [], + }, + new CaseSensitiveClaimsIdentityTheoryData("LowercaseMixedSearch_ClaimsMissing") + { + ClaimsIdentity = CreateCaseSensitiveClaimsIdentity(new JObject { + [UpperCaseClaimName] = LowerCaseClaimValue, + }), + ClaimNameSearch = LowerCaseClaimName, + ClaimValueSearch = LowerCaseClaimValue, + ExpectedHasClaim = false, + ExpectedClaim = null, + ExpectedClaims = [], + }, + }; + } + } + + public class CaseSensitiveClaimsIdentityTheoryData(string testId) : TheoryDataBase(testId) + { + internal ClaimsIdentity ClaimsIdentity { get; set; } + internal string ClaimNameSearch { get; set; } + internal string ClaimValueSearch { get; set; } + internal bool ExpectedHasClaim { get; set; } + internal string ExpectedClaim { get; set; } + internal List ExpectedClaims { get; set; } + } + + private static ClaimsIdentity CreateCaseSensitiveClaimsIdentity(JObject claims, TokenValidationParameters validationParameters = null) + { + 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.UseCaseSensitiveClaimsIdentityTypeSwitch, false); + return claimsIdentity; + } + + private static string CreateUnsignedToken(JObject payload) + { + // Add default claims to the beginning of the payload + var jObject = new JObject(_defaultPayload); + jObject.Merge(payload); + return string.Concat(Base64UrlEncoder.Encode("{}"), ".", Base64UrlEncoder.Encode(jObject.ToString()), "."); + } + } +} diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/ClaimsIdentityFactoryTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/ClaimsIdentityFactoryTests.cs new file mode 100644 index 0000000000..c0e6e9b0d1 --- /dev/null +++ b/test/Microsoft.IdentityModel.Tokens.Tests/ClaimsIdentityFactoryTests.cs @@ -0,0 +1,140 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Security.Claims; +using Microsoft.IdentityModel.JsonWebTokens; +using Microsoft.IdentityModel.TestUtils; +using Xunit; + +namespace Microsoft.IdentityModel.Tokens.Tests +{ + [Collection(nameof(ClaimsIdentityFactoryTests))] + public class ClaimsIdentityFactoryTests + { + [Theory] + [InlineData(true)] + [InlineData(false)] + public void Create_FromTokenValidationParameters_ReturnsCorrectClaimsIdentity(bool useCaseSensitiveClaimsIdentity) + { + AppContext.SetSwitch(AppContextSwitches.UseCaseSensitiveClaimsIdentityTypeSwitch, useCaseSensitiveClaimsIdentity); + + var jsonWebToken = new JsonWebToken(Default.Jwt(Default.SecurityTokenDescriptor())); + var tokenValidationParameters = new TokenValidationParameters(); + tokenValidationParameters.AuthenticationType = "custom-authentication-type"; + tokenValidationParameters.NameClaimType = "custom-name"; + tokenValidationParameters.RoleClaimType = "custom-role"; + + var actualClaimsIdentity = tokenValidationParameters.CreateClaimsIdentity(jsonWebToken, Default.Issuer); + + Assert.Equal(tokenValidationParameters.AuthenticationType, actualClaimsIdentity.AuthenticationType); + Assert.Equal(tokenValidationParameters.NameClaimType, actualClaimsIdentity.NameClaimType); + Assert.Equal(tokenValidationParameters.RoleClaimType, actualClaimsIdentity.RoleClaimType); + + if (useCaseSensitiveClaimsIdentity) + { + Assert.IsType(actualClaimsIdentity); + Assert.NotNull(((CaseSensitiveClaimsIdentity)actualClaimsIdentity).SecurityToken); + Assert.Equal(jsonWebToken, ((CaseSensitiveClaimsIdentity)actualClaimsIdentity).SecurityToken); + } + else + { + Assert.IsType(actualClaimsIdentity); + } + + AppContext.SetSwitch(AppContextSwitches.UseCaseSensitiveClaimsIdentityTypeSwitch, false); + } + + [Theory] + [InlineData(true, true)] + [InlineData(true, false)] + [InlineData(false, false)] + public void Create_FromDerivedTokenValidationParameters_ReturnsCorrectClaimsIdentity(bool tvpReturnsCaseSensitiveClaimsIdentity, bool tvpReturnsCaseSensitiveClaimsIdentityWithToken) + { + AppContext.SetSwitch(AppContextSwitches.UseCaseSensitiveClaimsIdentityTypeSwitch, true); + + var jsonWebToken = new JsonWebToken(Default.Jwt(Default.SecurityTokenDescriptor())); + var tokenValidationParameters = new DerivedTokenValidationParameters(tvpReturnsCaseSensitiveClaimsIdentity, tvpReturnsCaseSensitiveClaimsIdentityWithToken); + tokenValidationParameters.AuthenticationType = "custom-authentication-type"; + tokenValidationParameters.NameClaimType = "custom-name"; + tokenValidationParameters.RoleClaimType = "custom-role"; + + var actualClaimsIdentity = tokenValidationParameters.CreateClaimsIdentity(jsonWebToken, Default.Issuer); + + if (tvpReturnsCaseSensitiveClaimsIdentity) + { + Assert.IsType(actualClaimsIdentity); + if (tvpReturnsCaseSensitiveClaimsIdentityWithToken) + { + var securityToken = ((CaseSensitiveClaimsIdentity)actualClaimsIdentity).SecurityToken; + Assert.NotNull(securityToken); + Assert.IsType(securityToken); + Assert.NotEqual(jsonWebToken, securityToken); + } + else + { + Assert.Null(((CaseSensitiveClaimsIdentity)actualClaimsIdentity).SecurityToken); + } + } + else + { + Assert.IsType(actualClaimsIdentity); + } + + Assert.Equal(tokenValidationParameters.AuthenticationType, actualClaimsIdentity.AuthenticationType); + Assert.Equal(tokenValidationParameters.NameClaimType, actualClaimsIdentity.NameClaimType); + Assert.Equal(tokenValidationParameters.RoleClaimType, actualClaimsIdentity.RoleClaimType); + + AppContext.SetSwitch(AppContextSwitches.UseCaseSensitiveClaimsIdentityTypeSwitch, false); + } + + + + private class DerivedTokenValidationParameters : TokenValidationParameters + { + private bool _returnCaseSensitiveClaimsIdentity; + private bool _returnCaseSensitiveClaimsIdentityWithToken; + + public DerivedTokenValidationParameters(bool returnCaseSensitiveClaimsIdentity = false, bool returnCaseSensitiveClaimsIdentityWithToken = false) + { + _returnCaseSensitiveClaimsIdentity = returnCaseSensitiveClaimsIdentity; + _returnCaseSensitiveClaimsIdentityWithToken = returnCaseSensitiveClaimsIdentityWithToken; + } + + public override ClaimsIdentity CreateClaimsIdentity(SecurityToken securityToken, string issuer) + { + if (_returnCaseSensitiveClaimsIdentity) + { + if (_returnCaseSensitiveClaimsIdentityWithToken) + { + return new CaseSensitiveClaimsIdentity(AuthenticationType, NameClaimType, RoleClaimType) + { + SecurityToken = new TvpJsonWebToken(Default.Jwt(Default.SecurityTokenDescriptor())), + }; + } + else + { + return new CaseSensitiveClaimsIdentity(AuthenticationType, NameClaimType, RoleClaimType); + } + } + + return new ClaimsIdentity(AuthenticationType, NameClaimType, RoleClaimType); + } + } + + private class TvpJsonWebToken : JsonWebToken + { + public TvpJsonWebToken(string jwtEncodedString) : base(jwtEncodedString) + { + } + + public TvpJsonWebToken(ReadOnlyMemory encodedTokenMemory) : base(encodedTokenMemory) + { + } + + public TvpJsonWebToken(string header, string payload) : base(header, payload) + { + } + } + } +} diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/CrossTokenTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/CrossTokenTests.cs index be26ab7654..82e4cd9efb 100644 --- a/test/Microsoft.IdentityModel.Tokens.Tests/CrossTokenTests.cs +++ b/test/Microsoft.IdentityModel.Tokens.Tests/CrossTokenTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System; +using System.Collections.Generic; using System.IdentityModel.Tokens.Jwt; using Microsoft.IdentityModel.JsonWebTokens; using Microsoft.IdentityModel.TestUtils; @@ -23,6 +24,7 @@ public class CrossTokenTests public void CrossTokenValidateToken(CrossTokenTheoryData theoryData) { var context = TestUtilities.WriteHeader($"{this}.CrossTokenValidateToken", theoryData); + try { var samlToken = IdentityUtilities.CreateEncodedSaml(theoryData.SecurityTokenDescriptor, theoryData.SamlTokenHandler); diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/IdentityComparerTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/IdentityComparerTests.cs index e78a0391b6..8cdfee9723 100644 --- a/test/Microsoft.IdentityModel.Tokens.Tests/IdentityComparerTests.cs +++ b/test/Microsoft.IdentityModel.Tokens.Tests/IdentityComparerTests.cs @@ -31,7 +31,7 @@ public void CompareClaims() var originalClaim = new Claim(ClaimTypes.Country, Default.Country, ClaimValueTypes.String, Default.Issuer, Default.OriginalIssuer); var claimsToCompare = new List() - { + { // Claim with different value for 'type' new Claim(Guid.NewGuid().ToString(), Default.Country, ClaimValueTypes.String, Default.Issuer, Default.OriginalIssuer), // Claim with different value for 'value' @@ -480,10 +480,10 @@ public void CompareSamlAuthorizationDecisionStatements() var context = new CompareContext($"{this}.CompareSamlAuthorizationDecisionStatements"); var samlAction = new SamlAction(Guid.NewGuid().ToString()); var samlAttributeStatement1 = - new SamlAuthorizationDecisionStatement(new SamlSubject(), + new SamlAuthorizationDecisionStatement(new SamlSubject(), Guid.NewGuid().ToString(), Default.SamlAccessDecision, new List { samlAction }); var samlAttributeStatement2 = - new SamlAuthorizationDecisionStatement(new SamlSubject(), + new SamlAuthorizationDecisionStatement(new SamlSubject(), Guid.NewGuid().ToString(), Default.SamlAccessDecision, new List { samlAction }); IdentityComparer.AreEqual(samlAttributeStatement1, samlAttributeStatement2, context); @@ -503,7 +503,7 @@ public void CompareSamlSecurityTokens() new SamlAttributeStatement(new SamlSubject(), new List {new SamlAttribute("1", "2", "3")}) })); - var samlSecurityToken2 = + var samlSecurityToken2 = new SamlSecurityToken(new SamlAssertion(Guid.NewGuid().ToString(), Default.Issuer, DateTime.Parse(Default.IssueInstantString), null, new SamlAdvice(), new List diff --git a/test/System.IdentityModel.Tokens.Jwt.Tests/JwtSecurityTokenHandlerTests.cs b/test/System.IdentityModel.Tokens.Jwt.Tests/JwtSecurityTokenHandlerTests.cs index ea2c58a738..271092119f 100644 --- a/test/System.IdentityModel.Tokens.Jwt.Tests/JwtSecurityTokenHandlerTests.cs +++ b/test/System.IdentityModel.Tokens.Jwt.Tests/JwtSecurityTokenHandlerTests.cs @@ -257,7 +257,7 @@ public static TheoryData CreateJWEWithPayloadStringTheory } } - // Tests checks to make sure that the token string created by the JwtSecurityTokenHandler is consistent with the + // Tests checks to make sure that the token string created by the JwtSecurityTokenHandler is consistent with the // token string created by the JsonWebTokenHandler. [Theory, MemberData(nameof(CreateJWEUsingSecurityTokenDescriptorTheoryData))] public void CreateJWEUsingSecurityTokenDescriptor(CreateTokenTheoryData theoryData) diff --git a/test/System.IdentityModel.Tokens.Jwt.Tests/References.cs b/test/System.IdentityModel.Tokens.Jwt.Tests/References.cs index ae1099b0e7..4814ffccfa 100644 --- a/test/System.IdentityModel.Tokens.Jwt.Tests/References.cs +++ b/test/System.IdentityModel.Tokens.Jwt.Tests/References.cs @@ -450,7 +450,7 @@ public static class EncodedJwts public static string Asymmetric_1024 = @"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwOi8vR290Snd0LmNvbSIsImF1ZCI6Imh0dHA6Ly9Db250b3NvLmNvbSIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL2NvdW50cnkiOiJVU0EiLCJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9lbWFpbGFkZHJlc3MiOiJ1c2VyQGNvbnRvc28uY29tIiwiaHR0cDovL3NjaGVtYXMueG1sc29hcC5vcmcvd3MvMjAwNS8wNS9pZGVudGl0eS9jbGFpbXMvZ2l2ZW5uYW1lIjoiVG9ueSIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL2hvbWVwaG9uZSI6IjU1NS4xMjEyIiwiaHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS93cy8yMDA4LzA2L2lkZW50aXR5L2NsYWltcy9yb2xlIjoiU2FsZXMiLCJzdWIiOiJib2IifQ.WlNiBiAqmS4G-Em5O-uYiWLK5CJO8B-6Hvqjv_DXpoxldGiMWzivuyJocXPIIDVbcLxovmTc5j0KKgA9foOFBSkEEasqESA0VTYE30T1kkrGOaElola5DZagzax2zDipjxhbtBdMsvgF2t6GQJKyF0oFt828_yRGUsUnaXxg_MY"; public static string Asymmetric_2048 = @"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwOi8vR290Snd0LmNvbSIsImF1ZCI6Imh0dHA6Ly9Db250b3NvLmNvbSIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL2NvdW50cnkiOiJVU0EiLCJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9lbWFpbGFkZHJlc3MiOiJ1c2VyQGNvbnRvc28uY29tIiwiaHR0cDovL3NjaGVtYXMueG1sc29hcC5vcmcvd3MvMjAwNS8wNS9pZGVudGl0eS9jbGFpbXMvZ2l2ZW5uYW1lIjoiVG9ueSIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL2hvbWVwaG9uZSI6IjU1NS4xMjEyIiwiaHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS93cy8yMDA4LzA2L2lkZW50aXR5L2NsYWltcy9yb2xlIjoiU2FsZXMiLCJzdWIiOiJib2IifQ.XYeDHk0XRs1ybrk2AMWu3ZwNC6gPUYqxacJtUDSfQCGouRFdmkYtZcgvWAhH8iFv3DmPgfX0lI9WCtjN2JOZqOx5w90r9UKCh_9e_vUKZyjLkyUEv3iBl2HTpxfcj3ns5MmZI50N8O2cYq1d6-CRK_oi8oKhLWKfrD8LoMpCtV8zjraEB1GUfJvMrxPTIzHSF-V_nmu5aPIoHVyxAcc1jShkYdnS5Dz8nVqLBleCAQ2Tv-8N9Q8l1362b088y15auc-hBb76KmMU2aCutyJDRz0NqsCkFz-cV-vnIj-hzl562DzSUP48nEMTwEIO_bRKex1R5beZ36ZrKLP1GQxc8Q"; public static string Symmetric_256 = @"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOi8vR290Snd0LmNvbSIsImF1ZCI6Imh0dHA6Ly9Db250b3NvLmNvbSIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL2NvdW50cnkiOiJVU0EiLCJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9lbWFpbGFkZHJlc3MiOiJ1c2VyQGNvbnRvc28uY29tIiwiaHR0cDovL3NjaGVtYXMueG1sc29hcC5vcmcvd3MvMjAwNS8wNS9pZGVudGl0eS9jbGFpbXMvZ2l2ZW5uYW1lIjoiVG9ueSIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL2hvbWVwaG9uZSI6IjU1NS4xMjEyIiwiaHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS93cy8yMDA4LzA2L2lkZW50aXR5L2NsYWltcy9yb2xlIjoiU2FsZXMiLCJzdWIiOiJib2IifQ._IFPA82MzKeV4IrsgZX8mkAEfzWT8-zEE4b5R2nzih4"; - public static string InvalidHeader = @"eyJcdWQiOiJodHRwOi8vbG9jYWxob3N0L1JQIiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdC9TdHMiLCJuYm.eyJpc3MiOiJodHRwOi8vR290Snd0LmNvbSIsImF1ZCI6Imh0dHA6Ly9Db250b3NvLmNvbSIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL2NvdW50cnkiOiJVU0EiLCJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9lbWFpbGFkZHJlc3MiOiJ1c2VyQGNvbnRvc28uY29tIiwiaHR0cDovL3NjaGVtYXMueG1sc29hcC5vcmcvd3MvMjAwNS8wNS9pZGVudGl0eS9jbGFpbXMvZ2l2ZW5uYW1lIjoiVG9ueSIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL2hvbWVwaG9uZSI6IjU1NS4xMjEyIiwiaHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS93cy8yMDA4LzA2L2lkZW50aXR5L2NsYWltcy9yb2xlIjoiU2FsZXMiLCJzdWIiOiJib2IifQ.QW0Wfw-R5n3BHXE0vG-0giRFeB6W9oFrWJyFTaLI0qICDYx3yZ2eLXJ3zNFLVf3OG-MqytN5tqUdNfK1mRzeubqvdODHLFX36e1o3X8DR_YumyyQvgSeTJ0wwqT8PowbE3nbKfiX4TtJ4jffBelGKnL6vdx3AU2cwvLfSVp8ppA"; + public static string InvalidHeader = @"eyJcdWQiOiJodHRwOi8vbG9jYWxob3N0L1JQIiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdC9TdHMiLCJuYm.eyJpc3MiOiJodHRwOi8vR290Snd0LmNvbSIsImF1ZCI6Imh0dHA6Ly9Db250b3NvLmNvbSIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL2NvdW50cnkiOiJVU0EiLCJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9lbWFpbGFkZHJlc3MiOiJ1c2VyQGNvbnRvc28uY29tIiwiaHR0cDovL3NjaGVtYXMueG1sc29hcC5vcmcvd3MvMjAwNS8wNS9pZGVudGl0eS9jbGFpbXMvZ2l2ZW5uYW1lIjoiVG9ueSIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL2hvbWVwaG9uZSI6IjU1NS4xMjEyIiwiaHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS93cy8yMDA4LzA2L2lkZW50aXR5L2NsYWltcy9yb2xlIjoiU2FsZXMiLCJzdWIiOiJib2IifQ.QW0Wfw-R5n3BHXE0vG-0giRFeB6W9oFrWJyFTaLI0qICDYx3yZ2eLXJ3zNFLVf3OG-MqytN5tqUdNfK1mRzeubqvdODHLFX36e1o3X8DR_YumyyQvgSeTJ0wwqT8PowbE3nbKfiX4TtJ4jffBelGKnL6vdx3AU2cwvLfSVp8ppA"; public static string InvalidPayload = @"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsIng1dCI6InZ4VThJR1pYdEFtemg0NzdDT05CR2dYRTlfYyJ9.eyJcdWQiOiJodHRwOi8vbG9jYWxob3N0L1JQIiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdC9TdHMiLCJuYmYiOjEzNjcyODA0MDUsImV4cCI6MTM2NzMwOTIwNSwiaHR0cDovL3NjaGVtYXMueG1sc29hcC5vcmcvd3MvMjAwNS8wNS9pZGVudGl0eS9jbGFpbXMvZ2l2ZW5uYW1lIjoiYWFsIn0.Pro66IUD94jvZNnG_l96Hph78L_LYSx6eobO6QfWF3y038ebLZorhKYgAj1LtsNVAbq7E_I5tnoI1Y4YUV5_wMGtMqT_XTB4N8vktDzf0Y32MhopsDrveofJAAFAUP1npYZtFF89RAWzy1GaXqXw05SbUcyMPWTSvmPk_frzJRTc-utAaBAp-zKqS1KXGB_s99x7lDxy3ZFMDFtFHQlOJiXeClXYCVkB-ZmvrSFSAIasFK4eIG9pOcMY43_wS7ybNjF7WncY6PEi6JmUoh2AwA-SCdY-Bhs80Tf4GMB2HsmuMkSVgoptt6Fgf-q8LhWG0W80g66JRgdhMj85BZ6bxg"; public static string LiveJwt = @"eyJhbGciOiJIUzI1NiIsImtpZCI6IjAiLCJ0eXAiOiJKV1QifQ.eyJ2ZXIiOjEsImlzcyI6InVybjp3aW5kb3dzOmxpdmVpZCIsImV4cCI6MTM2ODY0ODg2MywidWlkIjoiMzgwZTE3YzMxNGU2ZmMyODA0NzA3MjI5NTc3MjEwZmIiLCJhdWQiOiJ3d3cuc3JpLWRldjEwMC5jb20iLCJ1cm46bWljcm9zb2Z0OmFwcHVyaSI6Im1zLWFwcDovL1MtMS0xNS0yLTM2MzczOTQzNzAtMjIzMTgyMTkzNi01NjUwMTU1MS0xNTE0NjEzNDgyLTQ1NjgzNjc4LTM1NzUyNjE4NTItMjMzNTgyNjkwIiwidXJuOm1pY3Jvc29mdDphcHBpZCI6IjAwMDAwMDAwNEMwRTdBNUMifQ.I-sE7t6IJUho1TfgaLilNuzro-pWOMgg33rQ351GcoM"; public static string OverClaims = @"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6ImtyaU1QZG1Cdng2OHNrVDgtbVBBQjNCc2VlQSJ9.eyJhdWQiOiJodHRwczovL2dyYXBoLndpbmRvd3MubmV0IiwiaXNzIjoiaHR0cHM6Ly9zdHMud2luZG93cy5uZXQvNzJmOTg4YmYtODZmMS00MWFmLTkxYWItMmQ3Y2QwMTFkYjQ3LyIsImlhdCI6MTQwNTk2ODkyMiwibmJmIjoxNDA1OTY4OTIyLCJleHAiOjE0MDU5NzI4MjIsInZlciI6IjEuMCIsInRpZCI6IjcyZjk4OGJmLTg2ZjEtNDFhZi05MWFiLTJkN2NkMDExZGI0NyIsImFtciI6WyJwd2QiXSwib2lkIjoiMzVjNzZlZWQtZjY0MC00YWU3LWFhZTItMzI3NzE3MWVhM2U1IiwidXBuIjoibmJhbGlnYUBtaWNyb3NvZnQuY29tIiwidW5pcXVlX25hbWUiOiJuYmFsaWdhQG1pY3Jvc29mdC5jb20iLCJzdWIiOiI1R0UwVkhBSlBuaUdNSWluN3dMNFBFMFE5MjAzTG00bHJBUnBrcEFBYmprIiwicHVpZCI6IjEwMDM3RkZFODAxQjI4QTAiLCJmYW1pbHlfbmFtZSI6IkJhbGlnYSIsImdpdmVuX25hbWUiOiJOYW1yYXRhIiwiX2NsYWltX25hbWVzIjp7Imdyb3VwcyI6InNyYzEifSwiX2NsYWltX3NvdXJjZXMiOnsic3JjMSI6eyJlbmRwb2ludCI6Imh0dHBzOi8vZ3JhcGgud2luZG93cy5uZXQvNzJmOTg4YmYtODZmMS00MWFmLTkxYWItMmQ3Y2QwMTFkYjQ3L3VzZXJzLzM1Yzc2ZWVkLWY2NDAtNGFlNy1hYWUyLTMyNzcxNzFlYTNlNS9nZXRNZW1iZXJPYmplY3RzIn19LCJhcHBpZCI6IjExOGUxNzBmLWNmMjYtNDAwZi1hMGU5LTk2OTEwYjMxMTg3ZSIsImFwcGlkYWNyIjoiMSIsInNjcCI6IlVzZXJQcm9maWxlLlJlYWQiLCJhY3IiOiIxIn0.PWNfaBajC6KAr2dKiG0aJ1295hIXm9XWZPdrCw6zMgT0s46rrcBFMWOJQ-4Cz1aSqour6tslg8cl4_1rAjlkVwsXs7QTekMHxIcf3SPpM6vPTa7OfQ4dzBbPQV_QKif1xBXDkFQfZPAF2tPwcK_VBzHT0Z94_CpOtxChXmGEctW38Rt6f8bC_aaD6nsTZOt6NdAmI2AVOchpp7qNWEdBTvdcoNyz_a5VbUwWsHGCvozcOLjjFLles-K0BhiFw3MyJU_DMG-H6TgeBtwJPiuU2vHUTea26sfKHbpe7GypBo1PjY7odDWMH-d7c1Z0fT-UL15dAV419zX1NGbl-cujsw";