Skip to content
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

Marcinzo/issuervalidatorv11 #2503

Merged
merged 4 commits into from
Mar 19, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
using Microsoft.IdentityModel.Protocols;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Microsoft.IdentityModel.Tokens;
using static Microsoft.IdentityModel.Validators.AadIssuerValidator;

namespace Microsoft.IdentityModel.Validators
{
Expand All @@ -23,8 +24,12 @@ public class AadIssuerValidator
{
private static readonly TimeSpan LastKnownGoodConfigurationLifetime = new TimeSpan(0, 24, 0, 0);

internal const string V11EndpointSuffix = "/v1.1";
internal const string V11EndpointSuffixWithTrailingSlash = $"{V11EndpointSuffix}/";

internal const string V2EndpointSuffix = "/v2.0";
internal const string V2EndpointSuffixWithTrailingSlash = $"{V2EndpointSuffix}/";

internal const string TenantIdTemplate = "{tenantid}";

private Func<string, BaseConfigurationManager> _configurationManagerProvider;
Expand All @@ -35,7 +40,8 @@ internal AadIssuerValidator(
{
HttpClient = httpClient;
AadAuthority = aadAuthority.TrimEnd('/');
IsV2Authority = aadAuthority.Contains(V2EndpointSuffix);
AadAuthorityVersion = GetProtocolVersion(AadAuthority);
SetupAuthorities(AadAuthority, AadAuthorityVersion);
}

internal AadIssuerValidator(
Expand All @@ -51,11 +57,12 @@ internal AadIssuerValidator(
}

private HttpClient HttpClient { get; }
private string _aadAuthorityV1;
private string _aadAuthorityV2;

private BaseConfigurationManager _configurationManagerV1;
private BaseConfigurationManager _configurationManagerV11;
private BaseConfigurationManager _configurationManagerV2;
private IssuerLastKnownGood _issuerLKGV1;
private IssuerLastKnownGood _issuerLKGV11;
private IssuerLastKnownGood _issuerLKGV2;

internal BaseConfigurationManager ConfigurationManagerV1
Expand All @@ -74,6 +81,22 @@ internal BaseConfigurationManager ConfigurationManagerV1
}
}

internal BaseConfigurationManager ConfigurationManagerV11
{
get
{
if (_configurationManagerV11 == null)
_configurationManagerV11 = CreateConfigManager(AadAuthorityV11);

return _configurationManagerV11;
}

set
{
_configurationManagerV11 = value;
}
}

internal BaseConfigurationManager ConfigurationManagerV2
{
get
Expand All @@ -92,30 +115,54 @@ internal BaseConfigurationManager ConfigurationManagerV2

internal string AadAuthorityV1
{
get
{
if (_aadAuthorityV1 == null)
_aadAuthorityV1 = IsV2Authority ? CreateV1Authority(AadAuthority) : AadAuthority;
get;
private set;
}

return _aadAuthorityV1;
}
internal string AadAuthorityV11
{
get;
private set;
}

internal string AadAuthorityV2
{
get
{
if (_aadAuthorityV2 == null)
_aadAuthorityV2 = IsV2Authority ? AadAuthority : AadAuthority + V2EndpointSuffix;
get;
private set;
}

return _aadAuthorityV2;
private void SetupAuthorities(string aadAuthority, ProtocolVersion version)
{
switch (version)
{
case ProtocolVersion.V1:
AadAuthorityV1 = aadAuthority;
AadAuthorityV11 = AadAuthorityV1 + V11EndpointSuffix;
AadAuthorityV2 = AadAuthorityV1 + V2EndpointSuffix;
break;

case ProtocolVersion.V11:
AadAuthorityV1 = CreateV1Authority(AadAuthority, V11EndpointSuffix);
AadAuthorityV11 = aadAuthority;
AadAuthorityV2 = AadAuthorityV1 + V2EndpointSuffix;
break;

case ProtocolVersion.V2:
AadAuthorityV1 = CreateV1Authority(AadAuthority);
AadAuthorityV11 = AadAuthorityV1 + V11EndpointSuffix;
AadAuthorityV2 = aadAuthority;
break;

default:
throw new InvalidOperationException("Unsupported protocol version.");
}
}

internal string AadIssuerV1 { get; set; }
internal string AadIssuerV2 { get; set; }
internal string AadAuthority { get; set; }
internal bool IsV2Authority { get; set; }
internal ProtocolVersion AadAuthorityVersion { get; set; }

internal static readonly IDictionary<string, AadIssuerValidator> s_issuerValidators = new ConcurrentDictionary<string, AadIssuerValidator>();

/// <summary>
Expand Down Expand Up @@ -196,14 +243,14 @@ internal async ValueTask<string> ValidateAsync(

try
{
var isV2Issuer = IsV2Issuer(securityToken);
var effectiveConfigurationManager = GetEffectiveConfigurationManager(isV2Issuer);
var issuerVersion = GetTokenIssuerVersion(securityToken);
var effectiveConfigurationManager = GetEffectiveConfigurationManager(issuerVersion);

string aadIssuer = null;
if (validationParameters.ValidateWithLKG)
{
// returns null if LKG issuer expired
aadIssuer = GetEffectiveLKGIssuer(isV2Issuer);
aadIssuer = GetEffectiveLKGIssuer(issuerVersion);
}
else
{
Expand All @@ -217,7 +264,7 @@ internal async ValueTask<string> ValidateAsync(

// The original LKG assignment behavior for previous self-state management.
if (isIssuerValid && !validationParameters.ValidateWithLKG)
SetEffectiveLKGIssuer(aadIssuer, isV2Issuer, effectiveConfigurationManager.LastKnownGoodLifetime);
SetEffectiveLKGIssuer(aadIssuer, issuerVersion, effectiveConfigurationManager.LastKnownGoodLifetime);

if (isIssuerValid)
return issuer;
Expand Down Expand Up @@ -305,12 +352,13 @@ internal static AadIssuerValidator GetAadIssuerValidator(string aadAuthority, Ht
return s_issuerValidators[aadAuthority];
}

private static string CreateV1Authority(string aadV2Authority)

private static string CreateV1Authority(string aadV2Authority, string suffixToReplace = V2EndpointSuffix)
{
if (aadV2Authority.Contains(AadIssuerValidatorConstants.Organizations))
return aadV2Authority.Replace($"{AadIssuerValidatorConstants.Organizations}{V2EndpointSuffix}", AadIssuerValidatorConstants.Common);
if (suffixToReplace == V2EndpointSuffix && aadV2Authority.Contains(AadIssuerValidatorConstants.Organizations))
return aadV2Authority.Replace($"{AadIssuerValidatorConstants.Organizations}{suffixToReplace}", AadIssuerValidatorConstants.Common);

return aadV2Authority.Replace(V2EndpointSuffix, string.Empty);
return aadV2Authority.Replace(suffixToReplace, string.Empty);
}

private ConfigurationManager<OpenIdConnectConfiguration> CreateConfigManager(
Expand Down Expand Up @@ -350,23 +398,48 @@ private static bool IsValidIssuer(string validIssuerTemplate, string tenantId, s
}
}

private void SetEffectiveLKGIssuer(string aadIssuer, bool isV2Issuer, TimeSpan lastKnownGoodLifetime)
private void SetEffectiveLKGIssuer(string aadIssuer, ProtocolVersion protocolVersion, TimeSpan lastKnownGoodLifetime)
{
var issuerLKG = new IssuerLastKnownGood
{
Issuer = aadIssuer,
LastKnownGoodLifetime = lastKnownGoodLifetime
};

if (isV2Issuer)
_issuerLKGV2 = issuerLKG;
else
_issuerLKGV1 = issuerLKG;
switch (protocolVersion)
{
case ProtocolVersion.V1:
_issuerLKGV1 = issuerLKG;
break;

case ProtocolVersion.V11:
_issuerLKGV11 = issuerLKG;
break;

case ProtocolVersion.V2:
_issuerLKGV2 = issuerLKG;
break;
}
}

private string GetEffectiveLKGIssuer(bool isV2Issuer)
private string GetEffectiveLKGIssuer(ProtocolVersion protocolVersion)
{
var effectiveLKGIssuer = isV2Issuer ? _issuerLKGV2 : _issuerLKGV1;
IssuerLastKnownGood effectiveLKGIssuer = null;
switch (protocolVersion)
{
case ProtocolVersion.V1:
effectiveLKGIssuer = _issuerLKGV1;
break;

case ProtocolVersion.V11:
effectiveLKGIssuer = _issuerLKGV11;
break;

case ProtocolVersion.V2:
effectiveLKGIssuer = _issuerLKGV2;
break;
}

if (effectiveLKGIssuer != null && effectiveLKGIssuer.IsValid)
{
return effectiveLKGIssuer.Issuer;
Expand All @@ -375,25 +448,80 @@ private string GetEffectiveLKGIssuer(bool isV2Issuer)
return null;
}

private static bool IsV2Issuer(SecurityToken securityToken)
private static ProtocolVersion GetTokenIssuerVersion(SecurityToken securityToken)
{
return securityToken.Issuer.EndsWith(V2EndpointSuffixWithTrailingSlash, StringComparison.OrdinalIgnoreCase) ||
securityToken.Issuer.EndsWith(V2EndpointSuffix, StringComparison.OrdinalIgnoreCase);
if (securityToken.Issuer.EndsWith(V2EndpointSuffixWithTrailingSlash, StringComparison.OrdinalIgnoreCase) ||
securityToken.Issuer.EndsWith(V2EndpointSuffix, StringComparison.OrdinalIgnoreCase))
return ProtocolVersion.V2;

if (securityToken.Issuer.EndsWith(V11EndpointSuffixWithTrailingSlash, StringComparison.OrdinalIgnoreCase) ||
securityToken.Issuer.EndsWith(V11EndpointSuffix, StringComparison.OrdinalIgnoreCase))
return ProtocolVersion.V11;

return ProtocolVersion.V1;
}

private BaseConfigurationManager GetEffectiveConfigurationManager(bool isV2Issuer)
private BaseConfigurationManager GetEffectiveConfigurationManager(ProtocolVersion protocolVersion)
{
if (_configurationManagerProvider != null)
{
var aadAuthority = isV2Issuer ? AadAuthorityV2 : AadAuthorityV1;
var configurationManager = _configurationManagerProvider(aadAuthority);
string aadAuthority = GetAuthority(protocolVersion);


var configurationManager = _configurationManagerProvider(aadAuthority);
if (configurationManager != null)
return configurationManager;
}

// If no provider or provider returned null, fallback to previous strategy
return isV2Issuer ? ConfigurationManagerV2 : ConfigurationManagerV1;
// If no provider or provider returned null, fallback to previous strategy
return GetConfigurationManager(protocolVersion);
}

private BaseConfigurationManager GetConfigurationManager(ProtocolVersion protocolVersion)
{
switch (protocolVersion)
{
case ProtocolVersion.V1:
return ConfigurationManagerV1;

case ProtocolVersion.V11:
return ConfigurationManagerV11;

case ProtocolVersion.V2:
return ConfigurationManagerV2;

default:
return ConfigurationManagerV1;
}
}

private string GetAuthority(ProtocolVersion protocolVersion)
{
switch (protocolVersion)
{
case ProtocolVersion.V1:
return AadAuthorityV1;

case ProtocolVersion.V11:
return AadAuthorityV11;

case ProtocolVersion.V2:
return AadAuthorityV2;

default:
return AadAuthorityV1;
}
}

private static ProtocolVersion GetProtocolVersion(string aadAuthority)
{
if (aadAuthority.Contains(V2EndpointSuffix))
return ProtocolVersion.V2;

if (aadAuthority.Contains(V11EndpointSuffix))
return ProtocolVersion.V11;

return ProtocolVersion.V1;
}

private static async Task<BaseConfiguration> GetBaseConfiguration(BaseConfigurationManager configurationManager, TokenValidationParameters validationParameters)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

namespace Microsoft.IdentityModel.Validators
{
/// <summary>
/// AAD protocol version.
/// </summary>
public enum ProtocolVersion
{
/// <summary>
/// Protocol version 1.0
/// </summary>
V1,

/// <summary>
/// Protocol version 1.1
/// </summary>
V11,

/// <summary>
/// Protocol version 2.0
/// </summary>
V2
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@

[assembly: SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Needs to be ignored", Scope = "member", Target = "~M:Microsoft.IdentityModel.Validators.AadIssuerValidator.IsValidIssuer(System.String,System.String,System.String)~System.Boolean")]
#if NET6_0_OR_GREATER
[assembly: SuppressMessage("Globalization", "CA1307:Specify StringComparison", Justification = "Adding StringComparison.Ordinal adds a performance penalty.", Scope = "member", Target = "~M:Microsoft.IdentityModel.Validators.AadIssuerValidator.CreateV1Authority(System.String)~System.String")]
[assembly: SuppressMessage("Globalization", "CA1307:Specify StringComparison", Justification = "Adding StringComparison.Ordinal adds a performance penalty.", Scope = "member", Target = "~M:Microsoft.IdentityModel.Validators.AadIssuerValidator.IsValidIssuer(System.String,System.String,System.String)~System.Boolean")]
[assembly: SuppressMessage("Globalization", "CA1307:Specify StringComparison", Justification = "Adding StringComparison.Ordinal adds a performance penalty.", Scope = "member", Target = "~M:Microsoft.IdentityModel.Validators.AadIssuerValidator.#ctor(System.Net.Http.HttpClient,System.String)")]
[assembly: SuppressMessage("Globalization", "CA1307:Specify StringComparison", Justification = "Adding StringComparison.Ordinal adds a performance penalty.", Scope = "member", Target = "~M:Microsoft.IdentityModel.Validators.AadIssuerValidator.CreateV1Authority(System.String,System.String)~System.String")]
[assembly: SuppressMessage("Globalization", "CA1307:Specify StringComparison", Justification = "Adding StringComparison.Ordinal adds a performance penalty.", Scope = "member", Target = "~M:Microsoft.IdentityModel.Validators.AadIssuerValidator.GetProtocolVersion(System.String)~Microsoft.IdentityModel.Validators.AadIssuerValidator.ProtocolVersion")]
[assembly: SuppressMessage("Globalization", "CA1307:Specify StringComparison", Justification = "Adding StringComparison.Ordinal adds a performance penalty.", Scope = "member", Target = "~M:Microsoft.IdentityModel.Validators.AadIssuerValidator.GetProtocolVersion(System.String)~Microsoft.IdentityModel.Validators.ProtocolVersion")]
#endif

Loading
Loading