diff --git a/src/Microsoft.IdentityModel.Validators/AadIssuerValidator/AadIssuerValidator.cs b/src/Microsoft.IdentityModel.Validators/AadIssuerValidator/AadIssuerValidator.cs index b6cb627328..484e00f357 100644 --- a/src/Microsoft.IdentityModel.Validators/AadIssuerValidator/AadIssuerValidator.cs +++ b/src/Microsoft.IdentityModel.Validators/AadIssuerValidator/AadIssuerValidator.cs @@ -382,54 +382,34 @@ private ConfigurationManager CreateConfigManager( } } - private static bool IsValidIssuer(string validIssuerTemplate, string tenantId, string actualIssuer) + internal static bool IsValidIssuer(string validIssuerTemplate, string tenantId, string actualIssuer) { if (string.IsNullOrEmpty(validIssuerTemplate)) return false; + ReadOnlySpan validIssuerTemplateSpan = validIssuerTemplate.AsSpan(); + ReadOnlySpan actualIssuerSpan = actualIssuer.AsSpan(); int indexOfTenantIdTemplate = validIssuerTemplate.IndexOf(TenantIdTemplate, StringComparison.Ordinal); - if (indexOfTenantIdTemplate >= 0) + + if (indexOfTenantIdTemplate >= 0 && actualIssuer.Length > indexOfTenantIdTemplate) { - return IssuersWithTemplatesAreEqual(validIssuerTemplate.AsSpan(), TenantIdTemplate.AsSpan(), indexOfTenantIdTemplate, actualIssuer.AsSpan(), tenantId.AsSpan()); + // ensure the first part of the validIssuerTemplate matches the first part of actualIssuer + if (!validIssuerTemplateSpan.Slice(0, indexOfTenantIdTemplate).SequenceEqual(actualIssuerSpan.Slice(0, indexOfTenantIdTemplate))) + return false; + + // ensure that actualIssuer contains the tenantId from indexOfTenantIdTemplate for the length of tenantId.Length + if (!actualIssuerSpan.Slice(indexOfTenantIdTemplate, tenantId.Length).SequenceEqual(tenantId.AsSpan())) + return false; + + // ensure the second halves are equal + return validIssuerTemplateSpan.Slice(indexOfTenantIdTemplate + TenantIdTemplate.Length).SequenceEqual(actualIssuerSpan.Slice(indexOfTenantIdTemplate + tenantId.Length)); } else { - return validIssuerTemplate == actualIssuer; + return validIssuerTemplateSpan.SequenceEqual(actualIssuerSpan); } } - /// - /// Compare two Issuers with templates without string allocations. - /// This function replaces: issuer1.Replace(issuer1Template, tenantId) == issuer2 - /// Example: - /// issuer1 = "https://login.microsoftonline.com/{tenantid}/v2.0" - /// issuer1Template = "{tenantid}" - /// issuer2 = "https://login.microsoftonline.com/12345678/v2.0" - /// tenantId = "12345678" - /// - internal static bool IssuersWithTemplatesAreEqual(ReadOnlySpan issuer1, ReadOnlySpan issuer1Template, int templateStartIndex, ReadOnlySpan issuer2, ReadOnlySpan tenantId) - { - if (templateStartIndex == -1) - return false; - - // ensure the first part of the issuer1 matches the first part of issuer2 - if (!issuer1.Slice(0, templateStartIndex).SequenceEqual(issuer2.Slice(0, templateStartIndex))) - return false; - - // ensure that issuer2 contains the tenantId from templateStartIndex for the length of tenantId.Length - if (!issuer2.Slice(templateStartIndex, tenantId.Length).SequenceEqual(tenantId)) - return false; - - int secondHalfIssuer1StartIndex = templateStartIndex + issuer1Template.Length; - int secondHalfIssuer2StartIndex = templateStartIndex + tenantId.Length; - - // ensure the second halves are equal - if (!issuer1.Slice(secondHalfIssuer1StartIndex).SequenceEqual(issuer2.Slice(secondHalfIssuer2StartIndex))) - return false; - - return true; - } - private void SetEffectiveLKGIssuer(string aadIssuer, ProtocolVersion protocolVersion, TimeSpan lastKnownGoodLifetime) { var issuerLKG = new IssuerLastKnownGood diff --git a/src/Microsoft.IdentityModel.Validators/AadTokenValidationParametersExtension.cs b/src/Microsoft.IdentityModel.Validators/AadTokenValidationParametersExtension.cs index 04e055fcec..c890232ed8 100644 --- a/src/Microsoft.IdentityModel.Validators/AadTokenValidationParametersExtension.cs +++ b/src/Microsoft.IdentityModel.Validators/AadTokenValidationParametersExtension.cs @@ -86,8 +86,8 @@ internal static bool ValidateIssuerSigningKey(SecurityKey securityKey, SecurityT // 3. signing key issuers will never match sts.windows.net as v1 endpoint doesn't have issuers attached to keys // v2TokenIssuer is the representation of Token.Issuer (if it was a v2 issuer) int templateStartIndex = signingKeyIssuer.IndexOf(AadIssuerValidator.TenantIdTemplate, StringComparison.Ordinal); - if (!AadIssuerValidator.IssuersWithTemplatesAreEqual(signingKeyIssuer.AsSpan(), AadIssuerValidator.TenantIdTemplate.AsSpan(), templateStartIndex, tokenIssuer.AsSpan(), tenantIdFromToken.AsSpan()) - && !AadIssuerValidator.IssuersWithTemplatesAreEqual(signingKeyIssuer.AsSpan(), AadIssuerValidator.TenantIdTemplate.AsSpan(), templateStartIndex, openIdConnectConfiguration.Issuer == null ? [] : openIdConnectConfiguration.Issuer.AsSpan(), tenantIdFromToken.AsSpan())) + if (!AadIssuerValidator.IsValidIssuer(signingKeyIssuer, tenantIdFromToken, tokenIssuer) + && !AadIssuerValidator.IsValidIssuer(signingKeyIssuer, tenantIdFromToken, openIdConnectConfiguration.Issuer == null ? string.Empty : openIdConnectConfiguration.Issuer)) { string effectiveSigningKeyIssuer = templateStartIndex > -1 ? CreateIssuer(signingKeyIssuer, AadIssuerValidator.TenantIdTemplate, tenantIdFromToken, templateStartIndex) : signingKeyIssuer; throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidIssuerException(LogHelper.FormatInvariant(LogMessages.IDX40005, LogHelper.MarkAsNonPII(tokenIssuer), LogHelper.MarkAsNonPII(effectiveSigningKeyIssuer)))); diff --git a/test/Microsoft.IdentityModel.Validators.Tests/AadIssuerValidatorTests.cs b/test/Microsoft.IdentityModel.Validators.Tests/AadIssuerValidatorTests.cs index 4acf55a0fa..9e9be589c8 100644 --- a/test/Microsoft.IdentityModel.Validators.Tests/AadIssuerValidatorTests.cs +++ b/test/Microsoft.IdentityModel.Validators.Tests/AadIssuerValidatorTests.cs @@ -1,46 +1,23 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -using System; using Xunit; namespace Microsoft.IdentityModel.Validators.Tests { public class AadIssuerValidatorTests { - [Fact] - public static void IssuersWithTemplatesAreEqualTests_EqualIssuers() - { - // arrange - var issuer1Template = "{tenantId}"; - var issuer1 = ValidatorConstants.AadInstance + issuer1Template; - var issuer2Template = ValidatorConstants.TenantIdAsGuid; - var issuer2 = ValidatorConstants.AadInstance + issuer2Template; - int templateStartIndex = issuer1.IndexOf(issuer1Template); - - // act - var result = AadIssuerValidator.IssuersWithTemplatesAreEqual( - issuer1.AsSpan(), issuer1Template.AsSpan(), templateStartIndex, issuer2.AsSpan(), issuer2Template.AsSpan()); - - // assert - Assert.True(result); - } - - [Fact] - public static void IssuersWithTemplatesAreEqualTests_UnequalIssuers() + [Theory] + [InlineData(ValidatorConstants.AadInstance + AadIssuerValidator.TenantIdTemplate, ValidatorConstants.AadInstance + ValidatorConstants.TenantIdAsGuid, true)] + [InlineData(ValidatorConstants.AadInstancePPE + AadIssuerValidator.TenantIdTemplate, ValidatorConstants.AadInstance + ValidatorConstants.TenantIdAsGuid, false)] + [InlineData("", ValidatorConstants.AadInstance + ValidatorConstants.TenantIdAsGuid, false)] + [InlineData(ValidatorConstants.AadInstance + AadIssuerValidator.TenantIdTemplate, "", false)] + public static void IsValidIssuer_CanValidateTemplatedIssuers(string templatedIssuer, string issuer, bool expectedResult) { - // arrange - var issuer1Template = "{tenantId}"; - var issuer1 = ValidatorConstants.AadInstancePPE + issuer1Template; - var issuer2Template = ValidatorConstants.TenantIdAsGuid; - var issuer2 = ValidatorConstants.AadInstance + issuer2Template; - int templateStartIndex = issuer1.IndexOf(issuer1Template); - // act - var result = AadIssuerValidator.IssuersWithTemplatesAreEqual( - issuer1.AsSpan(), issuer1Template.AsSpan(), templateStartIndex, issuer2.AsSpan(), issuer2Template.AsSpan()); + var result = AadIssuerValidator.IsValidIssuer(templatedIssuer, ValidatorConstants.TenantIdAsGuid, issuer); // assert - Assert.False(result); + Assert.Equal(expectedResult, result); } } }