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
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
// Licensed under the MIT License.

using System;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using Microsoft.IdentityModel.JsonWebTokens;
using Microsoft.IdentityModel.Logging;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Microsoft.IdentityModel.Tokens;
Expand Down Expand Up @@ -41,6 +43,13 @@ public static void EnableAadSigningKeyIssuerValidation(this TokenValidationParam
};
}

internal const string DontFailOnMissingTidSwitch = "Switch.Microsoft.IdentityModel.DontFailOnMissingTidValidateIssuerSigning";

private static bool DontFailOnMissingTid()
{
return (AppContext.TryGetSwitch(DontFailOnMissingTidSwitch, out bool dontFailOnMissingTid) && dontFailOnMissingTid);
}

/// <summary>
/// Validates the issuer signing key.
/// </summary>
Expand All @@ -67,9 +76,14 @@ internal static bool ValidateIssuerSigningKey(SecurityKey securityKey, SecurityT
if (string.IsNullOrWhiteSpace(signingKeyIssuer))
return true;

var tenantIdFromToken = AadIssuerValidator.GetTenantIdFromToken(securityToken);
var tenantIdFromToken = GetTid(securityToken);
if (string.IsNullOrEmpty(tenantIdFromToken))
return true;
{
if (DontFailOnMissingTid())
return true;
else
throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidIssuerException(LogMessages.IDX40009));
}

var tokenIssuer = securityToken.Issuer;

Expand All @@ -91,7 +105,7 @@ internal static bool ValidateIssuerSigningKey(SecurityKey securityKey, SecurityT

// comparing effectiveSigningKeyIssuer with v2TokenIssuer is required as well because of the following scenario:
// 1. service trusts /common/v2.0 endpoint
// 2. service receieves a v1 token that has issuer like sts.windows.net
// 2. service receives a v1 token that has issuer like sts.windows.net
// 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)
if (effectiveSigningKeyIssuer != tokenIssuer && effectiveSigningKeyIssuer != v2TokenIssuer)
Expand All @@ -101,6 +115,26 @@ internal static bool ValidateIssuerSigningKey(SecurityKey securityKey, SecurityT
return true;
}

private static string GetTid(SecurityToken securityToken)
{
if (securityToken is JsonWebToken jsonWebToken)
{
if (jsonWebToken.TryGetPayloadValue<string>("tid", out string tid))
return tid;

return string.Empty;
}
else if (securityToken is JwtSecurityToken jwtSecurityToken)
{
if ((jwtSecurityToken.Payload.TryGetValue("tid", out object tidObject) && tidObject is string tid))
return tid;

return string.Empty;
}
else
throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidIssuerException(LogMessages.IDX40010));
}

/// <summary>
/// Validates the issuer signing key certificate.
/// </summary>
Expand Down
3 changes: 3 additions & 0 deletions src/Microsoft.IdentityModel.Validators/LogMessages.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,8 @@ internal static class LogMessages
public const string IDX40005 = "IDX40005: Token issuer: '{0}', does not match the signing key issuer: '{1}'.";
public const string IDX40007 = "IDX40007: RequireSignedTokens property on ValidationParameters is set to true, but the issuer signing key is null.";
public const string IDX40008 = "IDX40008: When setting LastKnownGoodLifetime, the value must be greater than or equal to zero. value: '{0}'.";

public const string IDX40009 = "IDX40009: 'tid' claim didn't have a value.";
public const string IDX40010 = "IDX40010: The SecurityToken must be a 'JsonWebToken' or 'JwtSecurityToken'";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,15 @@
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Microsoft.IdentityModel.TestUtils;
using Microsoft.IdentityModel.Tokens;
using Microsoft.IdentityModel.Tokens.Saml2;
using Xunit;

#pragma warning disable CS3016 // Arrays as attribute arguments is not CLS-compliant

namespace Microsoft.IdentityModel.Validators.Tests
{
// Serialize as one of the tests depends on static state (app context)
[Collection(nameof(AadSigningKeyIssuerValidatorTests))]
public class AadSigningKeyIssuerValidatorTests
{
[Theory, MemberData(nameof(EnableAadSigningKeyIssuerValidationTestCases))]
Expand Down Expand Up @@ -167,13 +170,15 @@ public void ValidateIssuerSigningKeyTests(AadSigningKeyIssuerTheoryData theoryDa

try
{
theoryData.SetupAction?.Invoke();
var result = AadTokenValidationParametersExtension.ValidateIssuerSigningKey(theoryData.SecurityKey, theoryData.SecurityToken, theoryData.OpenIdConnectConfiguration);
theoryData.ExpectedException.ProcessNoException(context);
Assert.True(result);
}
catch (Exception ex)
{
theoryData.ExpectedException.ProcessException(ex, context);
theoryData.TearDownAction?.Invoke();
}

TestUtilities.AssertFailIfErrors(context);
Expand Down Expand Up @@ -294,7 +299,17 @@ public static TheoryData<AadSigningKeyIssuerTheoryData> ValidateIssuerSigningKey
TestId = "MissingTenantIdClaimInToken",
SecurityKey = KeyingMaterial.JsonWebKeyP256,
SecurityToken = new JwtSecurityToken(),
OpenIdConnectConfiguration = mockConfiguration
OpenIdConnectConfiguration = mockConfiguration,
ExpectedException = ExpectedException.SecurityTokenInvalidIssuerException("IDX40009")
});

theoryData.Add(new AadSigningKeyIssuerTheoryData
{
TestId = "WrongSecurityKeyType",
SecurityKey = KeyingMaterial.JsonWebKeyP256,
SecurityToken = new Saml2SecurityToken(new Saml2Assertion(new Saml2NameIdentifier("nameIdentifier"))),
OpenIdConnectConfiguration = mockConfiguration,
ExpectedException = ExpectedException.SecurityTokenInvalidIssuerException("IDX40010")
});

theoryData.Add(new AadSigningKeyIssuerTheoryData
Expand All @@ -321,6 +336,28 @@ public static TheoryData<AadSigningKeyIssuerTheoryData> ValidateIssuerSigningKey
ExpectedException = ExpectedException.SecurityTokenInvalidIssuerException("IDX40004")
});

theoryData.Add(new AadSigningKeyIssuerTheoryData
{
TestId = "Doesnt_Fail_With_Switch",
SecurityKey = KeyingMaterial.JsonWebKeyP256,
SecurityToken = new JwtSecurityToken(),
OpenIdConnectConfiguration = mockConfiguration,
SetupAction = () => AppContext.SetSwitch(AadTokenValidationParametersExtension.DontFailOnMissingTidSwitch, true),
TearDownAction = () => AppContext.SetSwitch(AadTokenValidationParametersExtension.DontFailOnMissingTidSwitch, false)
});

theoryData.Add(new AadSigningKeyIssuerTheoryData
{
TestId = "Fail_With_Switch_True",
SecurityKey = KeyingMaterial.JsonWebKeyP256,
SecurityToken = new JwtSecurityToken(),
OpenIdConnectConfiguration = mockConfiguration,
ExpectedException = ExpectedException.SecurityTokenInvalidIssuerException("IDX40009"),
SetupAction = () => AppContext.SetSwitch(AadTokenValidationParametersExtension.DontFailOnMissingTidSwitch, false),
TearDownAction = () => AppContext.SetSwitch(AadTokenValidationParametersExtension.DontFailOnMissingTidSwitch, isEnabled: false)
});


return theoryData;
}
}
Expand All @@ -346,6 +383,10 @@ public class AadSigningKeyIssuerTheoryData : TheoryDataBase
public bool SetDelegateUsingConfig { get; set; } = false;

public bool SetDelegateWithoutConfig { get; set; } = false;

public Action SetupAction { get; set; }

public Action TearDownAction { get; set; }
}
}
}
Expand Down