-
Notifications
You must be signed in to change notification settings - Fork 432
Regression tests: Audience #2838
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 3 commits
1296457
76e6b9b
8d2dba3
2f98fc7
c3ee1e7
cef517e
57079d9
f0ea745
dd83685
b6c4385
d518e5f
a7eff15
08a8800
33f4c7c
c47d3b8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,213 @@ | ||
| // Copyright (c) Microsoft Corporation. All rights reserved. | ||
| // Licensed under the MIT License. | ||
|
|
||
| #nullable enable | ||
| using System.Collections.Generic; | ||
| using System.Threading; | ||
| using System.Threading.Tasks; | ||
| using Microsoft.IdentityModel.TestUtils; | ||
| using Microsoft.IdentityModel.Tokens; | ||
| using Xunit; | ||
|
|
||
| namespace Microsoft.IdentityModel.JsonWebTokens.Tests | ||
| { | ||
| public partial class JsonWebTokenHandlerValidateTokenAsyncTests | ||
| { | ||
| [Theory, MemberData(nameof(ValidateTokenAsync_AudienceTestCases))] | ||
iNinja marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| public async Task ValidateTokenAsync_Audience(ValidateTokenAsyncAudienceTheoryData theoryData) | ||
| { | ||
| var context = TestUtilities.WriteHeader($"{this}.ValidateTokenAsync", theoryData); | ||
iNinja marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| JsonWebTokenHandler jsonWebTokenHandler = new JsonWebTokenHandler(); | ||
|
|
||
| SecurityTokenDescriptor securityTokenDescriptor = new SecurityTokenDescriptor | ||
| { | ||
| Subject = Default.ClaimsIdentity, | ||
| SigningCredentials = Default.AsymmetricSigningCredentials, | ||
| Audience = theoryData.Audience, | ||
| Issuer = Default.Issuer, | ||
| }; | ||
|
|
||
| string jwtString = jsonWebTokenHandler.CreateToken(securityTokenDescriptor); | ||
iNinja marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| TokenValidationResult tokenValidationParametersResult = | ||
| await jsonWebTokenHandler.ValidateTokenAsync(jwtString, theoryData.TokenValidationParameters); | ||
iNinja marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| ValidationResult<ValidatedToken> validationParametersResult = | ||
| await jsonWebTokenHandler.ValidateTokenAsync( | ||
iNinja marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| jwtString, theoryData.ValidationParameters!, theoryData.CallContext, CancellationToken.None); | ||
|
|
||
| if (tokenValidationParametersResult.IsValid != theoryData.ExpectedIsValid) | ||
| context.AddDiff($"tokenValidationParametersResult.IsValid != theoryData.ExpectedIsValid"); | ||
|
|
||
| if (validationParametersResult.IsSuccess != theoryData.ExpectedIsValid) | ||
| context.AddDiff($"validationParametersResult.IsSuccess != theoryData.ExpectedIsValid"); | ||
|
|
||
| if (theoryData.ExpectedIsValid && | ||
| tokenValidationParametersResult.IsValid && | ||
| validationParametersResult.IsSuccess) | ||
| { | ||
| IdentityComparer.AreEqual( | ||
| tokenValidationParametersResult.ClaimsIdentity, | ||
iNinja marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| validationParametersResult.UnwrapResult().ClaimsIdentity, | ||
| context); | ||
| IdentityComparer.AreEqual( | ||
| tokenValidationParametersResult.Claims, | ||
| validationParametersResult.UnwrapResult().Claims, | ||
| context); | ||
| } | ||
| else | ||
| { | ||
iNinja marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| theoryData.ExpectedException.ProcessException(tokenValidationParametersResult.Exception, context); | ||
|
|
||
| if (!validationParametersResult.IsSuccess) | ||
| { | ||
| // If there is a special case for the ValidationParameters path, use that. | ||
| if (theoryData.ExpectedExceptionValidationParameters != null) | ||
| theoryData.ExpectedExceptionValidationParameters | ||
| .ProcessException(validationParametersResult.UnwrapError().GetException(), context); | ||
| else | ||
| theoryData.ExpectedException | ||
| .ProcessException(validationParametersResult.UnwrapError().GetException(), context); | ||
| } | ||
| } | ||
|
|
||
| TestUtilities.AssertFailIfErrors(context); | ||
| } | ||
|
|
||
| public static TheoryData<ValidateTokenAsyncAudienceTheoryData> ValidateTokenAsync_AudienceTestCases | ||
| { | ||
| get | ||
| { | ||
| return new TheoryData<ValidateTokenAsyncAudienceTheoryData> | ||
| { | ||
| new ValidateTokenAsyncAudienceTheoryData("Valid_AudiencesMatch") | ||
| { | ||
| Audience = Default.Audience, | ||
| TokenValidationParameters = CreateTokenValidationParameters([Default.Audience]), | ||
| ValidationParameters = CreateValidationParameters([Default.Audience]), | ||
| }, | ||
| new ValidateTokenAsyncAudienceTheoryData("Invalid_AudiencesDontMatch") | ||
iNinja marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| { | ||
| // This scenario is the same if the token audience is an empty string or whitespace. | ||
| // As long as the token audience and the valid audience are not equal, the validation fails. | ||
| TokenValidationParameters = CreateTokenValidationParameters([Default.Audience]), | ||
| ValidationParameters = CreateValidationParameters([Default.Audience]), | ||
| Audience = "InvalidAudience", | ||
| ExpectedIsValid = false, | ||
| ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10214:"), | ||
| // ValidateTokenAsync with ValidationParameters returns a different error message to account for the | ||
| // removal of the ValidAudience property from the ValidationParameters class. | ||
| ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenInvalidAudienceException("IDX10215:"), | ||
| }, | ||
| new ValidateTokenAsyncAudienceTheoryData("Valid_AudienceWithinValidAudiences") | ||
| { | ||
iNinja marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| Audience = Default.Audience, | ||
| TokenValidationParameters = CreateTokenValidationParameters(["ExtraAudience", Default.Audience, "AnotherAudience"]), | ||
| ValidationParameters = CreateValidationParameters(["ExtraAudience", Default.Audience, "AnotherAudience"]), | ||
| }, | ||
| new ValidateTokenAsyncAudienceTheoryData("Valid_AudienceWithSlash_IgnoreTrailingSlashTrue") | ||
| { | ||
| // Audience has a trailing slash, but IgnoreTrailingSlashWhenValidatingAudience is true. | ||
| Audience = Default.Audience + "/", | ||
| TokenValidationParameters = CreateTokenValidationParameters([Default.Audience], true), | ||
| ValidationParameters = CreateValidationParameters([Default.Audience], true), | ||
| }, | ||
| new ValidateTokenAsyncAudienceTheoryData("Invalid_AudienceWithSlash_IgnoreTrailingSlashFalse") | ||
| { | ||
| // Audience has a trailing slash and IgnoreTrailingSlashWhenValidatingAudience is false. | ||
| Audience = Default.Audience + "/", | ||
| TokenValidationParameters = CreateTokenValidationParameters([Default.Audience], false), | ||
| ValidationParameters = CreateValidationParameters([Default.Audience], false), | ||
| ExpectedIsValid = false, | ||
| ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10214:"), | ||
iNinja marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenInvalidAudienceException("IDX10215:"), | ||
| }, | ||
| new ValidateTokenAsyncAudienceTheoryData("Valid_ValidAudiencesWithSlash_IgnoreTrailingSlashTrue") | ||
| { | ||
| // ValidAudiences has a trailing slash, but IgnoreTrailingSlashWhenValidatingAudience is true. | ||
| Audience = Default.Audience, | ||
| TokenValidationParameters = CreateTokenValidationParameters([Default.Audience + "/"], true), | ||
| ValidationParameters = CreateValidationParameters([Default.Audience + "/"], true), | ||
| }, | ||
| new ValidateTokenAsyncAudienceTheoryData("Invalid_ValidAudiencesWithSlash_IgnoreTrailingSlashFalse") | ||
| { | ||
| // ValidAudiences has a trailing slash and IgnoreTrailingSlashWhenValidatingAudience is false. | ||
| Audience = Default.Audience, | ||
| TokenValidationParameters = CreateTokenValidationParameters([Default.Audience + "/"], false), | ||
| ValidationParameters = CreateValidationParameters([Default.Audience + "/"], false), | ||
| ExpectedIsValid = false, | ||
| ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10214:"), | ||
| ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenInvalidAudienceException("IDX10215:"), | ||
| }, | ||
| new ValidateTokenAsyncAudienceTheoryData("Invalid_AudienceNullIsTreatedAsEmptyList") | ||
| { | ||
| // JsonWebToken.Audiences defaults to an empty list if no audiences are provided. | ||
| TokenValidationParameters = CreateTokenValidationParameters([Default.Audience]), | ||
| ValidationParameters = CreateValidationParameters([Default.Audience]), | ||
| Audience = null, | ||
| ExpectedIsValid = false, | ||
| ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10206:"), | ||
| }, | ||
| new ValidateTokenAsyncAudienceTheoryData("Invalid_ValidAudiencesIsNull") | ||
| { | ||
| TokenValidationParameters = CreateTokenValidationParameters(null), | ||
| ValidationParameters = CreateValidationParameters(null), | ||
| Audience = string.Empty, | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: Should this be a valid Audience while the Audiences property on TVP or VP is null? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The current validation path considers the case where no valid audiences are provided to be invalid and throws. If you believe that having no valid audiences should be considered the same as skipping the validation, we can look into it and find out what the correct behaviour would be. |
||
| ExpectedIsValid = false, | ||
| // TVP path has a special case when ValidAudience is null or empty and ValidAudiences is null. | ||
| ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10208:"), | ||
FuPingFranco marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| // VP path has a default empty List for ValidAudiences, so it will always return IDX10206 if no audiences are provided. | ||
iNinja marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenInvalidAudienceException("IDX10206:"), | ||
iNinja marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| }, | ||
| }; | ||
|
|
||
| static TokenValidationParameters CreateTokenValidationParameters( | ||
| List<string>? audiences, | ||
| bool ignoreTrailingSlashWhenValidatingAudience = false) => | ||
|
|
||
| new TokenValidationParameters | ||
iNinja marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| { | ||
| ValidateAudience = true, | ||
| ValidateIssuer = true, | ||
| ValidateLifetime = true, | ||
| ValidateTokenReplay = true, | ||
| ValidateIssuerSigningKey = true, | ||
| IssuerSigningKey = Default.AsymmetricSigningKey, | ||
| ValidAudiences = audiences, | ||
| ValidIssuer = Default.Issuer, | ||
| IgnoreTrailingSlashWhenValidatingAudience = ignoreTrailingSlashWhenValidatingAudience, | ||
| }; | ||
|
|
||
| static ValidationParameters CreateValidationParameters( | ||
| List<string>? audiences, | ||
| bool ignoreTrailingSlashWhenValidatingAudience = false) | ||
| { | ||
| ValidationParameters validationParameters = new ValidationParameters(); | ||
| validationParameters.ValidIssuers.Add(Default.Issuer); | ||
| audiences?.ForEach(audience => validationParameters.ValidAudiences.Add(audience)); | ||
| validationParameters.IssuerSigningKeys.Add(Default.AsymmetricSigningKey); | ||
| validationParameters.IgnoreTrailingSlashWhenValidatingAudience = ignoreTrailingSlashWhenValidatingAudience; | ||
|
|
||
| return validationParameters; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| public class ValidateTokenAsyncAudienceTheoryData : TheoryDataBase | ||
| { | ||
| public ValidateTokenAsyncAudienceTheoryData(string testId) : base(testId) { } | ||
|
|
||
| public string? Audience { get; internal set; } = Default.Audience; | ||
|
|
||
| internal bool ExpectedIsValid { get; set; } = true; | ||
|
|
||
| internal TokenValidationParameters? TokenValidationParameters { get; set; } | ||
|
|
||
| internal ValidationParameters? ValidationParameters { get; set; } | ||
|
|
||
| // only set if we expect a different message on this path | ||
| internal ExpectedException? ExpectedExceptionValidationParameters { get; set; } = null; | ||
| } | ||
| } | ||
| } | ||
| #nullable restore | ||
Uh oh!
There was an error while loading. Please reload this page.