Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Microsoft.IdentityModel.Tokens/LogMessages.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ internal static class LogMessages
public const string IDX10208 = "IDX10208: Unable to validate audience. validationParameters.ValidAudience is null or whitespace and validationParameters.ValidAudiences is null.";
public const string IDX10209 = "IDX10209: Token has length: '{0}' which is larger than the MaximumTokenSizeInBytes: '{1}'.";
public const string IDX10211 = "IDX10211: Unable to validate issuer. The 'issuer' parameter is null or whitespace.";
public const string IDX10212 = "IDX10212: Issuer validation failed. Issuer: '{0}'. Did not match any: validationParameters.ValidIssuers: '{1}' or validationParameters.ConfigurationManager.CurrentConfiguration.Issuer: '{2}'. For more details, see https://aka.ms/IdentityModel/issuer-validation. ";
public const string IDX10214 = "IDX10214: Audience validation failed. Audiences: '{0}'. Did not match: validationParameters.ValidAudience: '{1}' or validationParameters.ValidAudiences: '{2}'.";
public const string IDX10222 = "IDX10222: Lifetime validation failed. The token is not yet valid. ValidFrom (UTC): '{0}', Current time (UTC): '{1}'.";
public const string IDX10223 = "IDX10223: Lifetime validation failed. The token is expired. ValidTo (UTC): '{0}', Current time (UTC): '{1}'.";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
using System.Collections.Generic;
using System.ComponentModel;
using System.Security.Claims;
using System.Threading;
using Microsoft.IdentityModel.Abstractions;
using Microsoft.IdentityModel.Logging;

Expand All @@ -21,6 +20,7 @@ internal class ValidationParameters
private string _nameClaimType = ClaimsIdentity.DefaultNameClaimType;
private string _roleClaimType = ClaimsIdentity.DefaultRoleClaimType;
private Dictionary<string, object> _instancePropertyBag;
private IList<string> _validIssuers = [];
private IList<string> _validTokenTypes = [];

private AlgorithmValidatorDelegate _algorithmValidator = Validators.ValidateAlgorithm;
Expand Down Expand Up @@ -518,10 +518,16 @@ public TypeValidatorDelegate TypeValidator
public IList<string> ValidAudiences { get; }

/// <summary>
/// Gets the <see cref="IList{String}"/> that contains valid issuers that will be used to check against the token's issuer.
/// The default is <c>null</c>.
/// Gets or sets the <see cref="IList{String}"/> that contains valid issuers that will be used to check against the token's issuer.
/// The default is an empty collection.
/// </summary>
public IList<string> ValidIssuers { get; }
/// <exception cref="ArgumentNullException">Thrown when the value is set as null.</exception>
/// <returns>The <see cref="IList{String}"/> that contains valid issuers that will be used to check against the token's 'iss' claim.</returns>
public IList<string> ValidIssuers
{
get { return _validIssuers; }
set { _validIssuers = value ?? throw new ArgumentNullException(nameof(value), "ValidIssuers cannot be set as null."); }
}

/// <summary>
/// Gets the <see cref="IList{String}"/> that contains valid types that will be used to check against the JWT header's 'typ' claim.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@ namespace Microsoft.IdentityModel.Tokens
/// </summary>
/// <param name="issuer">The issuer to validate.</param>
/// <param name="securityToken">The <see cref="SecurityToken"/> that is being validated.</param>
/// <param name="validationParameters">The <see cref="TokenValidationParameters"/> to be used for validating the token.</param>
/// <param name="validationParameters">The <see cref="ValidationParameters"/> to be used for validating the token.</param>
/// <param name="callContext"></param>
/// <param name="cancellationToken"></param>
/// <returns>A <see cref="IssuerValidationResult"/>that contains the results of validating the issuer.</returns>
/// <returns>An <see cref="IssuerValidationResult"/>that contains the results of validating the issuer.</returns>
/// <remarks>This delegate is not expected to throw.</remarks>
internal delegate Task<IssuerValidationResult> IssuerValidationDelegateAsync(
string issuer,
SecurityToken securityToken,
TokenValidationParameters validationParameters,
ValidationParameters validationParameters,
CallContext callContext,
CancellationToken cancellationToken);

Expand All @@ -37,19 +37,15 @@ public static partial class Validators
/// </summary>
/// <param name="issuer">The issuer to validate</param>
/// <param name="securityToken">The <see cref="SecurityToken"/> that is being validated.</param>
/// <param name="validationParameters">The <see cref="TokenValidationParameters"/> to be used for validating the token.</param>
/// <param name="validationParameters">The <see cref="ValidationParameters"/> to be used for validating the token.</param>
/// <param name="callContext"></param>
/// <param name="cancellationToken"></param>
/// <returns>The issuer to use when creating the "Claim"(s) in a "ClaimsIdentity".</returns>
/// <exception cref="ArgumentNullException">If 'validationParameters' is null.</exception>
/// <exception cref="ArgumentNullException">If 'issuer' is null or whitespace and <see cref="TokenValidationParameters.ValidateIssuer"/> is true.</exception>
/// <exception cref="SecurityTokenInvalidIssuerException">If <see cref="TokenValidationParameters.ValidIssuer"/> is null or whitespace and <see cref="TokenValidationParameters.ValidIssuers"/> is null.</exception>
/// <exception cref="SecurityTokenInvalidIssuerException">If 'issuer' failed to matched either <see cref="TokenValidationParameters.ValidIssuer"/> or one of <see cref="TokenValidationParameters.ValidIssuers"/>.</exception>
/// <returns>An <see cref="IssuerValidationResult"/>that contains the results of validating the issuer.</returns>
/// <remarks>An EXACT match is required.</remarks>
internal static async Task<IssuerValidationResult> ValidateIssuerAsync(
string issuer,
SecurityToken securityToken,
TokenValidationParameters validationParameters,
ValidationParameters validationParameters,
CallContext callContext,
CancellationToken cancellationToken)
{
Expand Down Expand Up @@ -95,10 +91,8 @@ internal static async Task<IssuerValidationResult> ValidateIssuerAsync(
if (validationParameters.ConfigurationManager != null)
configuration = await validationParameters.ConfigurationManager.GetBaseConfigurationAsync(cancellationToken).ConfigureAwait(false);

// Throw if all possible places to validate against are null or empty
if (string.IsNullOrWhiteSpace(validationParameters.ValidIssuer)
&& validationParameters.ValidIssuers.IsNullOrEmpty()
&& string.IsNullOrWhiteSpace(configuration?.Issuer))
// Return failed IssuerValidationResult if all possible places to validate against are null or empty.
if (validationParameters.ValidIssuers.IsNullOrEmpty() && string.IsNullOrWhiteSpace(configuration?.Issuer))
{
return new IssuerValidationResult(
issuer,
Expand Down Expand Up @@ -126,25 +120,19 @@ internal static async Task<IssuerValidationResult> ValidateIssuerAsync(
}
}

if (string.Equals(validationParameters.ValidIssuer, issuer))
if (validationParameters.ValidIssuers.Count != 0)
{
return new IssuerValidationResult(issuer,
IssuerValidationResult.ValidationSource.IssuerIsValidIssuer);
}

if (validationParameters.ValidIssuers != null)
{
foreach (string str in validationParameters.ValidIssuers)
for (int i = 0; i < validationParameters.ValidIssuers.Count; i++)
{
if (string.IsNullOrEmpty(str))
if (string.IsNullOrEmpty(validationParameters.ValidIssuers[i]))
{
if (LogHelper.IsEnabled(EventLogLevel.Informational))
LogHelper.LogInformation(LogMessages.IDX10262);

continue;
}

if (string.Equals(str, issuer))
if (string.Equals(validationParameters.ValidIssuers[i], issuer))
{
if (LogHelper.IsEnabled(EventLogLevel.Informational))
LogHelper.LogInformation(LogMessages.IDX10236, LogHelper.MarkAsNonPII(issuer));
Expand All @@ -160,9 +148,8 @@ internal static async Task<IssuerValidationResult> ValidateIssuerAsync(
ValidationFailureType.IssuerValidationFailed,
new ExceptionDetail(
new MessageDetail(
LogMessages.IDX10205,
LogMessages.IDX10212,
LogHelper.MarkAsNonPII(issuer),
LogHelper.MarkAsNonPII(validationParameters.ValidIssuer ?? "null"),
LogHelper.MarkAsNonPII(Utility.SerializeAsSingleCommaDelimitedString(validationParameters.ValidIssuers)),
LogHelper.MarkAsNonPII(configuration?.Issuer)),
typeof(SecurityTokenInvalidIssuerException),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public static TheoryData<IssuerValidatorTheoryData> AsyncIssuerValidatorTestCase
theoryData.Add(new IssuerValidatorTheoryData
{
Issuer = null,
ValidationParameters = new TokenValidationParameters(),
ValidationParameters = new ValidationParameters(),
});

return theoryData;
Expand All @@ -52,7 +52,7 @@ public static TheoryData<IssuerValidatorTheoryData> AsyncIssuerValidatorTestCase
public class IssuerValidatorTheoryData : TheoryDataBase
{
public string Issuer { get; set; }
public TokenValidationParameters ValidationParameters { get; set; }
internal ValidationParameters ValidationParameters { get; set; }
public SecurityToken SecurityToken { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,26 +56,8 @@ public static TheoryData<IssuerValidationResultsTheoryData> IssuerValdationResul

string validIssuer = Guid.NewGuid().ToString();
string issClaim = Guid.NewGuid().ToString();
theoryData.Add(new IssuerValidationResultsTheoryData("Invalid_Issuer")
{
ExpectedException = ExpectedException.SecurityTokenInvalidIssuerException("IDX10205:"),
Issuer = issClaim,
IssuerValidationResult = new IssuerValidationResult(
issClaim,
ValidationFailureType.IssuerValidationFailed,
new ExceptionDetail(
new MessageDetail(
LogMessages.IDX10205,
LogHelper.MarkAsNonPII(issClaim),
LogHelper.MarkAsNonPII(validIssuer),
LogHelper.MarkAsNonPII(Utility.SerializeAsSingleCommaDelimitedString(null)),
LogHelper.MarkAsNonPII(null)),
typeof(SecurityTokenInvalidIssuerException),
new StackFrame(true))),
IsValid = false,
SecurityToken = JsonUtilities.CreateUnsignedJsonWebToken(JwtRegisteredClaimNames.Iss, issClaim),
ValidationParameters = new TokenValidationParameters { ValidIssuer = validIssuer }
});
var validConfig = new OpenIdConnectConfiguration() { Issuer = issClaim };
string[] validIssuers = new string[] { validIssuer };

theoryData.Add(new IssuerValidationResultsTheoryData("NULL_Issuer")
{
Expand All @@ -94,7 +76,7 @@ public static TheoryData<IssuerValidationResultsTheoryData> IssuerValdationResul
new StackFrame(true))),
IsValid = false,
SecurityToken = JsonUtilities.CreateUnsignedJsonWebToken(JwtRegisteredClaimNames.Iss, issClaim),
ValidationParameters = new TokenValidationParameters(),
ValidationParameters = new ValidationParameters()
});

theoryData.Add(new IssuerValidationResultsTheoryData("NULL_ValidationParameters")
Expand Down Expand Up @@ -132,10 +114,9 @@ public static TheoryData<IssuerValidationResultsTheoryData> IssuerValdationResul
null)),
IsValid = false,
SecurityToken = null,
ValidationParameters = new TokenValidationParameters()
ValidationParameters = new ValidationParameters()
});

var validConfig = new OpenIdConnectConfiguration() { Issuer = issClaim };
theoryData.Add(new IssuerValidationResultsTheoryData("Valid_FromConfig")
{
ExpectedException = ExpectedException.NoExceptionExpected,
Expand All @@ -145,40 +126,45 @@ public static TheoryData<IssuerValidationResultsTheoryData> IssuerValdationResul
IssuerValidationResult.ValidationSource.IssuerIsConfigurationIssuer),
IsValid = true,
SecurityToken = JsonUtilities.CreateUnsignedJsonWebToken(JwtRegisteredClaimNames.Iss, issClaim),
ValidationParameters = new TokenValidationParameters()
ValidationParameters = new ValidationParameters()
{
ConfigurationManager = new MockConfigurationManager<OpenIdConnectConfiguration>(validConfig)
}
});

theoryData.Add(new IssuerValidationResultsTheoryData("Valid_FromValidationParametersValidIssuer")
theoryData.Add(new IssuerValidationResultsTheoryData("Valid_FromValidationParametersValidIssuers")
{
ExpectedException = ExpectedException.NoExceptionExpected,
Issuer = issClaim,
IssuerValidationResult = new IssuerValidationResult(
issClaim,
IssuerValidationResult.ValidationSource.IssuerIsValidIssuer),
IssuerValidationResult.ValidationSource.IssuerIsAmongValidIssuers),
IsValid = true,
SecurityToken = JsonUtilities.CreateUnsignedJsonWebToken(JwtRegisteredClaimNames.Iss, issClaim),
ValidationParameters = new TokenValidationParameters()
ValidationParameters = new ValidationParameters()
{
ValidIssuer = issClaim
ValidIssuers = [issClaim]
}
});

theoryData.Add(new IssuerValidationResultsTheoryData("Valid_FromValidationParametersValidIssuers")
theoryData.Add(new IssuerValidationResultsTheoryData("Invalid_Issuer")
{
ExpectedException = ExpectedException.NoExceptionExpected,
ExpectedException = ExpectedException.SecurityTokenInvalidIssuerException("IDX10212:"),
Issuer = issClaim,
IssuerValidationResult = new IssuerValidationResult(
issClaim,
IssuerValidationResult.ValidationSource.IssuerIsAmongValidIssuers),
IsValid = true,
ValidationFailureType.IssuerValidationFailed,
new ExceptionDetail(
new MessageDetail(
LogMessages.IDX10212,
LogHelper.MarkAsNonPII(issClaim),
LogHelper.MarkAsNonPII(Utility.SerializeAsSingleCommaDelimitedString(validIssuers)),
LogHelper.MarkAsNonPII(null)),
typeof(SecurityTokenInvalidIssuerException),
new StackFrame(true))),
IsValid = false,
SecurityToken = JsonUtilities.CreateUnsignedJsonWebToken(JwtRegisteredClaimNames.Iss, issClaim),
ValidationParameters = new TokenValidationParameters()
{
ValidIssuers = [issClaim]
}
ValidationParameters = new ValidationParameters { ValidIssuers = [validIssuer] }
});

return theoryData;
Expand All @@ -202,7 +188,7 @@ public IssuerValidationResultsTheoryData(string testId) : base(testId)

public SecurityToken SecurityToken { get; set; }

public TokenValidationParameters ValidationParameters { get; set; }
internal ValidationParameters ValidationParameters { get; set; }

internal ValidationFailureType ValidationFailureType { get; set; }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,25 @@ public void SetValidators_NullValue_ThrowsArgumentNullException()
Assert.Throws<ArgumentNullException>(() => validationParameters.AudienceValidator = null);
}

[Fact]
public void ValidIssuers_GetSet_ValidIssuersList()
{
var validationParameters = new ValidationParameters();
var validIssuers = new List<string> { "issuer1", "issuer2" };

validationParameters.ValidIssuers = validIssuers;

Assert.Equal(validIssuers, validationParameters.ValidIssuers);
}

[Fact]
public void ValidIssuers_SetNull_ThrowsArgumentNullException()
{
var validationParameters = new ValidationParameters();

Assert.Throws<ArgumentNullException>(() => validationParameters.ValidIssuers = null);
}

[Fact]
public void ValidTypes_Get_ReturnsValidTokenTypes()
{
Expand Down