-
Notifications
You must be signed in to change notification settings - Fork 432
Extensibility tests: Audience #2861
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
Merged
Merged
Changes from 3 commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
38ce048
Added extensibility tests for JWT audience validation
iNinja 7b95af3
Merge branch 'dev' into iinglese/extensibility-tests-audience
iNinja ca1f870
Added default exception to ValidationError. Added log message for it.…
iNinja 9602826
Addressed PR feedback. Separated theory data for tests into individua…
iNinja 34fb602
Addressed PR comments: removed unnecessary default values from test data
iNinja 4dc4478
Merge branch 'dev' into iinglese/extensibility-tests-audience
iNinja 1504356
Resolved conflict with IM.Tokens/InternalAPI.Unshipped.txt
iNinja 0458ecb
Updated tests after changes in dev
iNinja File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| const Microsoft.IdentityModel.Tokens.LogMessages.IDX10002 = "IDX10002: Unknown exception type returned. Type: '{0}'. Message: '{1}'." -> string | ||
| Microsoft.IdentityModel.Tokens.AudienceValidationError.InvalidAudiences.get -> System.Collections.Generic.IList<string> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
241 changes: 241 additions & 0 deletions
241
...JsonWebTokens.Tests/JsonWebTokenHandler.ValidateTokenAsyncTests.Audience.Extensibility.cs
FuPingFranco marked this conversation as resolved.
Show resolved
Hide resolved
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,241 @@ | ||
| // Copyright (c) Microsoft Corporation. All rights reserved. | ||
| // Licensed under the MIT License. | ||
|
|
||
| #nullable enable | ||
| using System; | ||
| using System.Collections.Generic; | ||
| using System.Diagnostics; | ||
| 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_Audience_ExtensibilityTestCases), DisableDiscoveryEnumeration = true)] | ||
| public async Task ValidateTokenAsync_Audience_Extensibility(ValidateTokenAsyncAudienceExtensibilityTheoryData theoryData) | ||
| { | ||
| var context = TestUtilities.WriteHeader($"{this}.{nameof(ValidateTokenAsync_Audience_Extensibility)}", theoryData); | ||
|
|
||
| string jwtString = CreateTokenWithAudience(theoryData.Audience); | ||
| var handler = new JsonWebTokenHandler(); | ||
|
|
||
| ValidationResult<ValidatedToken> validationResult; | ||
|
|
||
| if (theoryData.ThrownException is null) | ||
| { | ||
| validationResult = await handler.ValidateTokenAsync( | ||
| jwtString, theoryData.ValidationParameters!, theoryData.CallContext, CancellationToken.None); | ||
| } | ||
| else | ||
| { | ||
| // The exception is thrown by the delegate, so we catch it here. | ||
| // Outside of testing, this could be a catch block in the calling code. | ||
| var exception = await Assert.ThrowsAsync<CustomInvalidAudienceException>(async () => | ||
| { | ||
| validationResult = await handler.ValidateTokenAsync( | ||
| jwtString, theoryData.ValidationParameters!, theoryData.CallContext, CancellationToken.None); | ||
| }); | ||
|
|
||
| theoryData.ThrownException.ProcessException(exception, context); | ||
| return; | ||
| } | ||
|
|
||
| if (validationResult.IsSuccess != theoryData.ExpectedIsValid) | ||
| context.AddDiff($"validationResult.IsSuccess != theoryData.ExpectedIsValid"); | ||
|
|
||
| if (validationResult.IsSuccess) | ||
| { | ||
| theoryData.ExpectedException.ProcessNoException(context); | ||
|
|
||
| IdentityComparer.AreStringsEqual(validationResult.UnwrapResult().ValidatedAudience, theoryData.Audience, context); | ||
| } | ||
| else | ||
| { | ||
| theoryData.ExpectedException.ProcessException(validationResult.UnwrapError().GetException(), context); | ||
|
|
||
| if (validationResult.UnwrapError().GetException() is SecurityTokenInvalidAudienceException audienceException) | ||
| { | ||
| if (theoryData.ExpectedInvalidAudience is not null) | ||
| IdentityComparer.AreStringsEqual(audienceException.InvalidAudience, theoryData.ExpectedInvalidAudience, context); | ||
| } | ||
|
|
||
| TestUtilities.AssertFailIfErrors(context); | ||
| } | ||
| } | ||
|
|
||
| public static TheoryData<ValidateTokenAsyncAudienceExtensibilityTheoryData> ValidateTokenAsync_Audience_ExtensibilityTestCases | ||
| { | ||
| get | ||
| { | ||
| return new TheoryData<ValidateTokenAsyncAudienceExtensibilityTheoryData> | ||
| { | ||
| new ValidateTokenAsyncAudienceExtensibilityTheoryData("DefaultDelegate_Valid_AudiencesMatch") | ||
iNinja marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| { | ||
| Audience = Default.Audience, | ||
iNinja marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| ValidationParameters = CreateValidationParameters([Default.Audience], audienceValidationDelegate: null), | ||
| }, | ||
| new ValidateTokenAsyncAudienceExtensibilityTheoryData("DefaultDelegate_Invalid_AudiencesDontMatch") | ||
| { | ||
| ValidationParameters = CreateValidationParameters([Default.Audience], audienceValidationDelegate: null), | ||
| Audience = "CustomAudience", | ||
| ExpectedIsValid = false, | ||
| ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10215:"), | ||
| }, | ||
| new ValidateTokenAsyncAudienceExtensibilityTheoryData("CustomDelegate_Valid_DelegateReturnsAudience") | ||
| { | ||
| Audience = Default.Audience, | ||
| ValidationParameters = CreateValidationParameters([Default.Audience], audienceValidationDelegate: delegate | ||
| (IList<string> audiences, | ||
| SecurityToken? securityToken, | ||
| ValidationParameters validationParameters, | ||
| CallContext callContext) | ||
| { | ||
| return "CustomAudience"; | ||
| }), | ||
| }, | ||
| new ValidateTokenAsyncAudienceExtensibilityTheoryData( | ||
| "CustomDelegate_Invalid_DelegateReturnsValidationErrorWithDefaultExceptionType") | ||
| { | ||
| Audience = Default.Audience, | ||
| ValidationParameters = CreateValidationParameters([Default.Audience], audienceValidationDelegate: delegate | ||
| (IList<string> audiences, | ||
| SecurityToken? securityToken, | ||
| ValidationParameters validationParameters, | ||
| CallContext callContext) | ||
| { | ||
| return new AudienceValidationError( | ||
| new MessageDetail("Custom message from the delegate."), | ||
| typeof(SecurityTokenInvalidAudienceException), | ||
| new StackFrame(true), | ||
| [Default.Audience]); | ||
| }), | ||
| ExpectedIsValid = false, | ||
| ExpectedException = new ExpectedException(typeof(SecurityTokenInvalidAudienceException), "Custom message from the delegate."), | ||
| ExpectedInvalidAudience = Default.Audience, | ||
| }, | ||
| new ValidateTokenAsyncAudienceExtensibilityTheoryData( | ||
| "CustomDelegate_Invalid_DelegateReturnsValidationErrorWithCustomExceptionType_NoCustomValidationError") | ||
| { | ||
| Audience = Default.Audience, | ||
| ValidationParameters = CreateValidationParameters([Default.Audience], audienceValidationDelegate: delegate | ||
| (IList<string> audiences, | ||
| SecurityToken? securityToken, | ||
| ValidationParameters validationParameters, | ||
| CallContext callContext) | ||
| { | ||
| return new AudienceValidationError( | ||
| new MessageDetail("Custom message from the delegate."), | ||
| typeof(CustomInvalidAudienceException), | ||
| new StackFrame(true), | ||
| [Default.Audience]); | ||
| }), | ||
| ExpectedIsValid = false, | ||
| // The delegate returns a custom exception but does not implement a custom ValidationError. | ||
| ExpectedException = ExpectedException.SecurityTokenException("IDX10002:"), | ||
| ExpectedInvalidAudience = Default.Audience, | ||
| }, | ||
| new ValidateTokenAsyncAudienceExtensibilityTheoryData( | ||
| "CustomDelegate_Invalid_DelegateReturnsValidationErrorWithCustomExceptionType_CustomValidationErrorUsed") | ||
| { | ||
| Audience = Default.Audience, | ||
| ValidationParameters = CreateValidationParameters([Default.Audience], audienceValidationDelegate: delegate | ||
| (IList<string> audiences, | ||
| SecurityToken? securityToken, | ||
| ValidationParameters validationParameters, | ||
| CallContext callContext) | ||
| { | ||
| return new CustomAudienceValidationError( | ||
| new MessageDetail("Custom message from the delegate."), | ||
| typeof(CustomInvalidAudienceException), | ||
| new StackFrame(true), | ||
| [Default.Audience]); | ||
| }), | ||
| ExpectedIsValid = false, | ||
| // The delegate uses a custom validation error that implements GetException to return the custom exception. | ||
| ExpectedException = new ExpectedException(typeof(CustomInvalidAudienceException), "Custom message from the delegate."), | ||
| ExpectedInvalidAudience = Default.Audience, | ||
| }, | ||
| new ValidateTokenAsyncAudienceExtensibilityTheoryData("CustomDelegate_Invalid_DelegateThrows") | ||
| { | ||
| Audience = Default.Audience, | ||
| ValidationParameters = CreateValidationParameters([Default.Audience], audienceValidationDelegate: delegate | ||
| (IList<string> audiences, | ||
| SecurityToken? securityToken, | ||
| ValidationParameters validationParameters, | ||
| CallContext callContext) | ||
| { | ||
| throw new CustomInvalidAudienceException("Custom exception from the delegate."); | ||
| }), | ||
| ExpectedIsValid = false, | ||
| ThrownException = new ExpectedException(typeof(CustomInvalidAudienceException), "Custom exception from the delegate."), | ||
| }, | ||
| }; | ||
|
|
||
| static ValidationParameters CreateValidationParameters( | ||
| List<string>? audiences, | ||
| AudienceValidationDelegate? audienceValidationDelegate) | ||
| { | ||
| ValidationParameters validationParameters = new ValidationParameters(); | ||
| audiences?.ForEach(audience => validationParameters.ValidAudiences.Add(audience)); | ||
|
|
||
| if (audienceValidationDelegate is not null) | ||
| validationParameters.AudienceValidator = audienceValidationDelegate; | ||
|
|
||
| // Skip all validations except audience | ||
| validationParameters.AlgorithmValidator = SkipValidationDelegates.SkipAlgorithmValidation; | ||
| validationParameters.IssuerValidatorAsync = SkipValidationDelegates.SkipIssuerValidation; | ||
| validationParameters.IssuerSigningKeyValidator = SkipValidationDelegates.SkipIssuerSigningKeyValidation; | ||
| validationParameters.LifetimeValidator = SkipValidationDelegates.SkipLifetimeValidation; | ||
| validationParameters.SignatureValidator = SkipValidationDelegates.SkipSignatureValidation; | ||
| validationParameters.TypeValidator = SkipValidationDelegates.SkipTokenTypeValidation; | ||
|
|
||
| return validationParameters; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| public class ValidateTokenAsyncAudienceExtensibilityTheoryData : ValidateTokenAsyncBaseTheoryData | ||
| { | ||
| public ValidateTokenAsyncAudienceExtensibilityTheoryData(string testId) : base(testId) { } | ||
|
|
||
| public string? Audience { get; internal set; } = Default.Audience; | ||
|
|
||
| public string? ExpectedInvalidAudience { get; internal set; } = null; | ||
|
|
||
| internal AudienceValidationDelegate? AudienceValidationDelegate { get; set; } | ||
|
|
||
| public ExpectedException? ThrownException { get; internal set; } = null; | ||
| } | ||
|
|
||
| private class CustomInvalidAudienceException : SecurityTokenInvalidAudienceException | ||
| { | ||
| public CustomInvalidAudienceException(string message) | ||
| : base(message) | ||
| { | ||
| } | ||
| } | ||
|
|
||
| private class CustomAudienceValidationError : AudienceValidationError | ||
| { | ||
| public CustomAudienceValidationError(MessageDetail messageDetail, | ||
| Type exceptionType, | ||
| StackFrame stackFrame, | ||
| IList<string>? invalidAudiences) : base(messageDetail, exceptionType, stackFrame, invalidAudiences) | ||
| { | ||
| } | ||
|
|
||
| internal override Exception GetException() | ||
| { | ||
| if (ExceptionType == typeof(CustomInvalidAudienceException)) | ||
| return new CustomInvalidAudienceException(MessageDetail.Message) { InvalidAudience = Utility.SerializeAsSingleCommaDelimitedString(InvalidAudiences) }; | ||
|
|
||
| return base.GetException(); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| #nullable restore | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let make sure we have a tracking GitHub issue, otherwise we might forget. agree on an aka.ms link
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#2907