Skip to content

Commit

Permalink
set up tests to track expected behavior
Browse files Browse the repository at this point in the history
  • Loading branch information
JoshLozensky committed Jun 12, 2024
1 parent f01c50d commit 2b71523
Show file tree
Hide file tree
Showing 2 changed files with 143 additions and 47 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -802,8 +802,10 @@ public void CreateJWEUsingSecurityTokenDescriptor(CreateTokenTheoryData theoryDa
var context = TestUtilities.WriteHeader($"{this}.CreateJWEUsingSecurityTokenDescriptor", theoryData);
theoryData.ValidationParameters.ValidateLifetime = false;
if (theoryData.TokenDescriptor != null && !theoryData.AudiencesForSecurityTokenDescriptor.IsNullOrEmpty())
{
foreach (var audience in theoryData.AudiencesForSecurityTokenDescriptor)
theoryData.TokenDescriptor.Audiences.Add(audience);
}

try
{
Expand Down Expand Up @@ -913,6 +915,15 @@ public static TheoryData<CreateTokenTheoryData> CreateJWEUsingSecurityTokenDescr
TokenDecryptionKey = KeyingMaterial.DefaultSymmetricSecurityKey_512,
ValidAudiences = Default.Audiences,
ValidIssuer = Default.Issuer
},

// There is a known difference in the 'Aud' claim between the two tokens. Since the JsonWebTokenHandler
// will only include the SecurityTokenDescriptor Audience and Audiences members if one or both are set in
// the 'Aud' claim, while the JwtSecurityTokenHandler will also include any aud claims set in either the
// Claims or Subject members even if Audience/Audiences is defined (not both, Claims takes precedence).
PropertiesToIgnoreWhenComparing = new Dictionary<Type, List<string>>
{
{ typeof(JsonWebToken), new List<string> {"EncodedPayload", "EncodedSignature"} },
}
},
new CreateTokenTheoryData
Expand Down Expand Up @@ -1915,8 +1926,10 @@ public void CreateJWSUsingSecurityTokenDescriptor(CreateTokenTheoryData theoryDa
{
JsonWebTokenHandler6x jsonWebTokenHandler6x = new JsonWebTokenHandler6x();
if (theoryData.TokenDescriptor != null && !theoryData.AudiencesForSecurityTokenDescriptor.IsNullOrEmpty())
{
foreach (var audience in theoryData.AudiencesForSecurityTokenDescriptor)
theoryData.TokenDescriptor.Audiences.Add(audience);
}

string jwtFromSecurityTokenDescriptor6x = jwtFromSecurityTokenDescriptor6x = jsonWebTokenHandler6x.CreateToken(theoryData.TokenDescriptor6x ?? theoryData.TokenDescriptor);
string jwtFromSecurityTokenDescriptor = theoryData.JsonWebTokenHandler.CreateToken(theoryData.TokenDescriptor);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,130 @@ public static TheoryData<CreateTokenTheoryData> CreateJWEWithPayloadStringTheory
}
}

// Tests for expected differences between the JwtSecurityTokenHandler and the JsonWebTokenHandler.
[Theory, MemberData(nameof(CreateJWEUsingSecurityTokenDescriptorTheoryData))]
public void CheckExpectedDifferenceInAudClaimUsingSecurityTokenDescriptor(CreateTokenTheoryData theoryData)
{
if (theoryData.TokenDescriptor == null)
return;

CompareContext context = TestUtilities.WriteHeader($"{this}.CheckExpectedDifferencesUsingSecurityTokenDescriptor", theoryData);
theoryData.ValidationParameters.ValidateLifetime = false;
var audMemberSet = false;
var audSetInClaims = false;
var audSetInSubject = false;

if (!theoryData.AudiencesForSecurityTokenDescriptor.IsNullOrEmpty())
{
audMemberSet = true;
foreach (var audience in theoryData.AudiencesForSecurityTokenDescriptor)
theoryData.TokenDescriptor.Audiences.Add(audience);
}
else if (!theoryData.TokenDescriptor.Audience.IsNullOrEmpty())
{
audMemberSet = true;
}

if (!theoryData.TokenDescriptor.Claims.IsNullOrEmpty() && theoryData.TokenDescriptor.Claims.ContainsKey(JwtRegisteredClaimNames.Aud))
audSetInClaims = true;

if (theoryData.TokenDescriptor.Subject != null && theoryData.TokenDescriptor.Subject.Claims.Any(c => c.Type == JwtRegisteredClaimNames.Aud))
audSetInSubject = true;

try
{
SecurityToken jweFromJwtHandler = theoryData.JwtSecurityTokenHandler.CreateToken(theoryData.TokenDescriptor);
string tokenFromJwtHandler = theoryData.JwtSecurityTokenHandler.WriteToken(jweFromJwtHandler);

string jweFromJsonHandler = theoryData.JsonWebTokenHandler.CreateToken(theoryData.TokenDescriptor);

ClaimsPrincipal claimsPrincipalJwtHandler = theoryData.JwtSecurityTokenHandler.ValidateToken(tokenFromJwtHandler, theoryData.ValidationParameters, out SecurityToken validatedTokenFromJwtHandler);
TokenValidationResult validationResultJsonHandler = theoryData.JsonWebTokenHandler.ValidateTokenAsync(jweFromJsonHandler, theoryData.ValidationParameters).Result;

int expectedAudClaimCount = 0;
int additionalAudClaimsForJwtHandler = 0;


if (audMemberSet)
{
if (!theoryData.TokenDescriptor.Audiences.IsNullOrEmpty())
expectedAudClaimCount += theoryData.TokenDescriptor.Audiences.Count;

if (!theoryData.TokenDescriptor.Audience.IsNullOrEmpty())
expectedAudClaimCount++;

if (audSetInClaims)
additionalAudClaimsForJwtHandler += GetClaimCountFromTokenDescriptorClaims(theoryData.TokenDescriptor, JwtRegisteredClaimNames.Aud);

else if (audSetInSubject)
additionalAudClaimsForJwtHandler += GetClaimCountFromTokenDescriptorSubject(theoryData.TokenDescriptor, JwtRegisteredClaimNames.Aud);
}
else if (audSetInClaims)
{
expectedAudClaimCount += GetClaimCountFromTokenDescriptorClaims(theoryData.TokenDescriptor, JwtRegisteredClaimNames.Aud);
}
else if (audSetInSubject)
{
expectedAudClaimCount += GetClaimCountFromTokenDescriptorSubject(theoryData.TokenDescriptor, JwtRegisteredClaimNames.Aud);
}

if (validationResultJsonHandler != null && validationResultJsonHandler.Claims != null)
{
if (validationResultJsonHandler.Claims.TryGetValue(JwtRegisteredClaimNames.Aud, out var audClaims))
{
switch (audClaims)
{
case string _:
Assert.True(expectedAudClaimCount == 1);
break;
case IEnumerable<string> enumerable:
Assert.True(expectedAudClaimCount == enumerable.Count());
break;
case IEnumerable<object> enumerable:
Assert.True(expectedAudClaimCount == enumerable.Count());
break;
default:
Assert.True(false, "Unexpected type for 'aud' claim.");
break;
}
}
}
if (claimsPrincipalJwtHandler != null)
{
IEnumerable<Claim> jwtHandlerAudClaims = claimsPrincipalJwtHandler.FindAll(JwtRegisteredClaimNames.Aud);
Assert.True(expectedAudClaimCount + additionalAudClaimsForJwtHandler == jwtHandlerAudClaims.Count());
}
}
catch (Exception ex)
{
theoryData.ExpectedException.ProcessException(ex, context);
}

TestUtilities.AssertFailIfErrors(context);
}

private static int GetClaimCountFromTokenDescriptorClaims(SecurityTokenDescriptor tokenDescriptor, string claimName)
{
if (tokenDescriptor.Claims != null && tokenDescriptor.Claims.ContainsKey(claimName))
{
switch (tokenDescriptor.Claims[claimName])
{
case string _:
return 1;
case IEnumerable<string> enumerable:
return enumerable.Count();
}
}
return 0;
}
private static int GetClaimCountFromTokenDescriptorSubject(SecurityTokenDescriptor tokenDescriptor, string claimName)
{
if (tokenDescriptor.Subject != null && tokenDescriptor.Subject.Claims != null)
return tokenDescriptor.Subject.Claims.Where(c => c.Type.Equals(claimName, StringComparison.Ordinal)).Count();

return 0;
}

// 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))]
Expand All @@ -281,7 +405,7 @@ public void CreateJWEUsingSecurityTokenDescriptor(CreateTokenTheoryData theoryDa
var validationResultJson = theoryData.JsonWebTokenHandler.ValidateTokenAsync(jweFromJsonHandler, theoryData.ValidationParameters).Result;

if (validationResultJson.Exception != null && validationResultJson.IsValid)
context.Diffs.Add("validationResultJson.IsValid, validationResultJson.Exception != null");
context.Diffs.Add("validationResultJsonHandler.IsValid, validationResultJsonHandler.Exception != null");

IdentityComparer.AreEqual(validationResultJson.IsValid, theoryData.IsValid, context);

Expand All @@ -298,7 +422,7 @@ public void CreateJWEUsingSecurityTokenDescriptor(CreateTokenTheoryData theoryDa

if (!IdentityComparer.AreEqual(claimsPrincipalJwt.Identity, validationResultJson.ClaimsIdentity, context))
{
context.Diffs.Add("claimsPrincipalJwt.Identity != validationResultJson.ClaimsIdentity");
context.Diffs.Add("claimsPrincipalJwtHandler.Identity != validationResultJsonHandler.ClaimsIdentity");
context.Diffs.Add("*******************************************************************");
}

Expand Down Expand Up @@ -409,34 +533,6 @@ public static TheoryData<CreateTokenTheoryData> CreateJWEUsingSecurityTokenDescr
ValidationParameters = Default.AsymmetricSignTokenValidationParameters
},
new CreateTokenTheoryData
{
TestId = "AudClaimPresentInClaimsDictionary_MultipleAudiences",
TokenDescriptor = new SecurityTokenDescriptor
{
SigningCredentials = Default.AsymmetricSigningCredentials,
EncryptingCredentials = null,
Claims = Default.PayloadJsonDictionary
},
AudiencesForSecurityTokenDescriptor = Default.Audiences,
JwtSecurityTokenHandler = tokenHandler,
JsonWebTokenHandler = jsonTokenHandler,
ValidationParameters = Default.AsymmetricSignTokenValidationParameters
},
new CreateTokenTheoryData
{
TestId = "NoClaimsDictionary_MultipleAudiences",
TokenDescriptor = new SecurityTokenDescriptor
{
SigningCredentials = Default.X509AsymmetricSigningCredentials,
EncryptingCredentials = null,
Subject = new ClaimsIdentity(Default.PayloadJsonClaims),
},
AudiencesForSecurityTokenDescriptor = Default.Audiences,
JwtSecurityTokenHandler = tokenHandler,
JsonWebTokenHandler = jsonTokenHandler,
ValidationParameters = Default.AsymmetricSignTokenValidationParameters
},
new CreateTokenTheoryData
{
TestId = "AudClaimNotPresentInClaimsDictionary_MultipleAudiences",
TokenDescriptor = new SecurityTokenDescriptor
Expand All @@ -451,20 +547,6 @@ public static TheoryData<CreateTokenTheoryData> CreateJWEUsingSecurityTokenDescr
ValidationParameters = Default.AsymmetricSignTokenValidationParameters
},
new CreateTokenTheoryData
{
TestId = "NoClaimsDictionary_MultipleAudiences",
TokenDescriptor = new SecurityTokenDescriptor
{
SigningCredentials = Default.X509AsymmetricSigningCredentials,
EncryptingCredentials = null,
Subject = new ClaimsIdentity(Default.PayloadJsonClaims),
},
AudiencesForSecurityTokenDescriptor = Default.Audiences,
JwtSecurityTokenHandler = tokenHandler,
JsonWebTokenHandler = jsonTokenHandler,
ValidationParameters = Default.AsymmetricSignTokenValidationParameters
},
new CreateTokenTheoryData
{
First = true,
TestId = "IdenticalClaims",
Expand Down Expand Up @@ -1318,7 +1400,7 @@ public void JWEDecompressionTest(JWEDecompressionTheoryData theoryData)
var claimsPrincipal = handler.ValidateToken(theoryData.JWECompressionString, theoryData.ValidationParameters, out var validatedToken);

if (!claimsPrincipal.Claims.Any())
context.Diffs.Add("claimsPrincipalJwt.Claims is empty.");
context.Diffs.Add("claimsPrincipalJwtHandler.Claims is empty.");

theoryData.ExpectedException.ProcessNoException(context);
}
Expand Down Expand Up @@ -1729,7 +1811,8 @@ public static TheoryData<JwtTheoryData> ValidateAudienceTheoryData
{
var tokenHandler = new JwtSecurityTokenHandler();
var securityTokenDescriptorWithAudiences = new SecurityTokenDescriptor{ Issuer = Default.Issuer };
securityTokenDescriptorWithAudiences.AddAudiences(Default.Audiences);
foreach (var audience in Default.Audiences)
securityTokenDescriptorWithAudiences.Audiences.Add(audience);

return new TheoryData<JwtTheoryData>
{
Expand Down Expand Up @@ -2307,7 +2390,7 @@ public void ValidateJWSWithConfig(JwtTheoryData theoryData)
AadIssuerValidator.GetAadIssuerValidator(Default.AadV1Authority).ConfigurationManagerV1 = theoryData.ValidationParameters.ConfigurationManager;
new JwtSecurityTokenHandler().ValidateToken(theoryData.Token, theoryData.ValidationParameters, out _);
if (theoryData.ShouldSetLastKnownConfiguration && theoryData.ValidationParameters.ConfigurationManager.LastKnownGoodConfiguration == null)
context.AddDiff("validationResultJson.IsValid, but the configuration was not set as the LastKnownGoodConfiguration");
context.AddDiff("validationResultJsonHandler.IsValid, but the configuration was not set as the LastKnownGoodConfiguration");
theoryData.ExpectedException.ProcessNoException(context);
}
catch (Exception ex)
Expand Down

0 comments on commit 2b71523

Please sign in to comment.