diff --git a/src/Microsoft.IdentityModel.Tokens.Saml/Saml/SamlSecurityTokenHandler.cs b/src/Microsoft.IdentityModel.Tokens.Saml/Saml/SamlSecurityTokenHandler.cs
index e173fea1e7..608e7ff958 100644
--- a/src/Microsoft.IdentityModel.Tokens.Saml/Saml/SamlSecurityTokenHandler.cs
+++ b/src/Microsoft.IdentityModel.Tokens.Saml/Saml/SamlSecurityTokenHandler.cs
@@ -8,6 +8,7 @@
using System.Linq;
using System.Security.Claims;
using System.Text;
+using System.Threading;
using System.Threading.Tasks;
using System.Xml;
using Microsoft.IdentityModel.Abstractions;
@@ -1139,6 +1140,9 @@ public override ClaimsPrincipal ValidateToken(XmlReader reader, TokenValidationP
if (validationParameters == null)
throw LogArgumentNullException(nameof(validationParameters));
+ validationParameters = SamlTokenUtilities.PopulateValidationParametersWithCurrentConfigurationAsync(validationParameters).ConfigureAwait(false).GetAwaiter()
+ .GetResult();
+
var samlToken = ReadSamlToken(reader);
if (samlToken == null)
throw LogExceptionMessage(
@@ -1150,25 +1154,41 @@ public override ClaimsPrincipal ValidateToken(XmlReader reader, TokenValidationP
}
///
- public override Task ValidateTokenAsync(string token, TokenValidationParameters validationParameters)
+ public override async Task ValidateTokenAsync(string token, TokenValidationParameters validationParameters)
{
try
{
- var claimsPrincipal = ValidateToken(token, validationParameters, out var validatedToken);
- return Task.FromResult(new TokenValidationResult
+ if (string.IsNullOrWhiteSpace(token))
+ throw LogArgumentNullException(nameof(token));
+
+ if (validationParameters == null)
+ throw LogArgumentNullException(nameof(validationParameters));
+
+ if (token.Length > MaximumTokenSizeInBytes)
+ throw LogExceptionMessage(new ArgumentException(FormatInvariant(TokenLogMessages.IDX10209, LogHelper.MarkAsNonPII(token.Length), LogHelper.MarkAsNonPII(MaximumTokenSizeInBytes))));
+
+ validationParameters = await SamlTokenUtilities.PopulateValidationParametersWithCurrentConfigurationAsync(validationParameters).ConfigureAwait(false);
+
+ var samlToken = ValidateSignature(token, validationParameters);
+ if (samlToken == null)
+ throw LogExceptionMessage(new SecurityTokenValidationException(
+ FormatInvariant(TokenLogMessages.IDX10254, LogHelper.MarkAsNonPII(_className), LogHelper.MarkAsNonPII("ValidateToken"), LogHelper.MarkAsNonPII(GetType()), LogHelper.MarkAsNonPII("ValidateSignature"), LogHelper.MarkAsNonPII(typeof(SamlSecurityToken)))));
+
+ var claimsPrincipal = ValidateToken(samlToken, token, validationParameters, out var validatedToken);
+ return new TokenValidationResult
{
SecurityToken = validatedToken,
ClaimsIdentity = claimsPrincipal?.Identities.First(),
IsValid = true,
- });
+ };
}
catch (Exception ex)
{
- return Task.FromResult(new TokenValidationResult
+ return new TokenValidationResult
{
IsValid = false,
Exception = ex
- });
+ };
}
}
@@ -1193,6 +1213,9 @@ public override ClaimsPrincipal ValidateToken(string token, TokenValidationParam
if (token.Length > MaximumTokenSizeInBytes)
throw LogExceptionMessage(new ArgumentException(FormatInvariant(TokenLogMessages.IDX10209, LogHelper.MarkAsNonPII(token.Length), LogHelper.MarkAsNonPII(MaximumTokenSizeInBytes))));
+ validationParameters = SamlTokenUtilities.PopulateValidationParametersWithCurrentConfigurationAsync(validationParameters).ConfigureAwait(false).GetAwaiter()
+ .GetResult();
+
var samlToken = ValidateSignature(token, validationParameters);
if (samlToken == null)
throw LogExceptionMessage(new SecurityTokenValidationException(
diff --git a/src/Microsoft.IdentityModel.Tokens.Saml/Saml/SamlTokenUtilities.cs b/src/Microsoft.IdentityModel.Tokens.Saml/Saml/SamlTokenUtilities.cs
index 2677b3663e..122be3cd98 100644
--- a/src/Microsoft.IdentityModel.Tokens.Saml/Saml/SamlTokenUtilities.cs
+++ b/src/Microsoft.IdentityModel.Tokens.Saml/Saml/SamlTokenUtilities.cs
@@ -7,6 +7,9 @@
using System.Collections.Generic;
using Microsoft.IdentityModel.Xml;
using System;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
using Microsoft.IdentityModel.Logging;
using TokenLogMessages = Microsoft.IdentityModel.Tokens.LogMessages;
@@ -165,5 +168,29 @@ internal static string GetXsiTypeForValue(object value)
return null;
}
+
+ ///
+ /// Fetches current configuration from the ConfigurationManager of
+ /// and populates ValidIssuers and IssuerSigningKeys.
+ ///
+ /// the token validation parameters to update.
+ /// New TokenValidationParameters with ValidIssuers and IssuerSigningKeys updated.
+ internal static async Task PopulateValidationParametersWithCurrentConfigurationAsync(
+ TokenValidationParameters validationParameters)
+ {
+ if (validationParameters.ConfigurationManager == null)
+ {
+ return validationParameters;
+ }
+
+ var currentConfiguration = await validationParameters.ConfigurationManager.GetBaseConfigurationAsync(CancellationToken.None).ConfigureAwait(false);
+ var validationParametersCloned = validationParameters.Clone();
+ var issuers = new[] { currentConfiguration.Issuer };
+
+ validationParametersCloned.ValidIssuers = (validationParametersCloned.ValidIssuers == null ? issuers : validationParametersCloned.ValidIssuers.Concat(issuers));
+ validationParametersCloned.IssuerSigningKeys = (validationParametersCloned.IssuerSigningKeys == null ? currentConfiguration.SigningKeys : validationParametersCloned.IssuerSigningKeys.Concat(currentConfiguration.SigningKeys));
+ return validationParametersCloned;
+
+ }
}
}
diff --git a/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Saml2SecurityTokenHandler.cs b/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Saml2SecurityTokenHandler.cs
index 8ff5d48f47..a588d4d467 100644
--- a/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Saml2SecurityTokenHandler.cs
+++ b/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/Saml2SecurityTokenHandler.cs
@@ -8,6 +8,7 @@
using System.Linq;
using System.Security.Claims;
using System.Text;
+using System.Threading;
using System.Threading.Tasks;
using System.Xml;
using Microsoft.IdentityModel.Abstractions;
@@ -171,28 +172,45 @@ public virtual SecurityToken CreateToken(SecurityTokenDescriptor tokenDescriptor
}
///
- public override Task ValidateTokenAsync(string token, TokenValidationParameters validationParameters)
+ public override async Task ValidateTokenAsync(string token, TokenValidationParameters validationParameters)
{
try
{
- var claimsPrincipal = ValidateToken(token, validationParameters, out var validatedToken);
- return Task.FromResult(new TokenValidationResult
+ if (string.IsNullOrEmpty(token))
+ throw LogArgumentNullException(nameof(token));
+
+ if (validationParameters == null)
+ throw LogArgumentNullException(nameof(validationParameters));
+
+ if (token.Length > MaximumTokenSizeInBytes)
+ throw LogExceptionMessage(new ArgumentException(FormatInvariant(TokenLogMessages.IDX10209, LogHelper.MarkAsNonPII(token.Length), LogHelper.MarkAsNonPII(MaximumTokenSizeInBytes))));
+
+ validationParameters = await SamlTokenUtilities.PopulateValidationParametersWithCurrentConfigurationAsync(validationParameters).ConfigureAwait(false);
+
+ var samlToken = ValidateSignature(token, validationParameters);
+ if (samlToken == null)
+ throw LogExceptionMessage(
+ new SecurityTokenValidationException(FormatInvariant(TokenLogMessages.IDX10254, LogHelper.MarkAsNonPII(_className), LogHelper.MarkAsNonPII("ValidateToken"), LogHelper.MarkAsNonPII(_className), LogHelper.MarkAsNonPII("ValidateSignature"), LogHelper.MarkAsNonPII(typeof(Saml2SecurityToken)))));
+ var claimsPrincipal = ValidateToken(samlToken, token, validationParameters, out var validatedToken);
+
+ return new TokenValidationResult
{
SecurityToken = validatedToken,
ClaimsIdentity = claimsPrincipal?.Identities.First(),
IsValid = true,
- });
+ };
}
catch (Exception ex)
{
- return Task.FromResult(new TokenValidationResult
+ return new TokenValidationResult
{
IsValid = false,
Exception = ex
- });
+ };
}
}
+
///
/// Reads and validates a .
///
@@ -212,6 +230,8 @@ public override ClaimsPrincipal ValidateToken(XmlReader reader, TokenValidationP
if (validationParameters == null)
throw LogArgumentNullException(nameof(validationParameters));
+ validationParameters = SamlTokenUtilities.PopulateValidationParametersWithCurrentConfigurationAsync(validationParameters).ConfigureAwait(false).GetAwaiter().GetResult();
+
var samlToken = ReadSaml2Token(reader);
if (samlToken == null)
throw LogExceptionMessage(
@@ -236,21 +256,14 @@ public override ClaimsPrincipal ValidateToken(XmlReader reader, TokenValidationP
/// A representing the identity contained in the token.
public override ClaimsPrincipal ValidateToken(string token, TokenValidationParameters validationParameters, out SecurityToken validatedToken)
{
- if (string.IsNullOrEmpty(token))
- throw LogArgumentNullException(nameof(token));
-
- if (validationParameters == null)
- throw LogArgumentNullException(nameof(validationParameters));
-
- if (token.Length > MaximumTokenSizeInBytes)
- throw LogExceptionMessage(new ArgumentException(FormatInvariant(TokenLogMessages.IDX10209, LogHelper.MarkAsNonPII(token.Length), LogHelper.MarkAsNonPII(MaximumTokenSizeInBytes))));
-
- var samlToken = ValidateSignature(token, validationParameters);
- if (samlToken == null)
- throw LogExceptionMessage(
- new SecurityTokenValidationException(FormatInvariant(TokenLogMessages.IDX10254, LogHelper.MarkAsNonPII(_className), LogHelper.MarkAsNonPII("ValidateToken"), LogHelper.MarkAsNonPII(_className), LogHelper.MarkAsNonPII("ValidateSignature"), LogHelper.MarkAsNonPII(typeof(Saml2SecurityToken)))));
+ var tokenValidationResult = ValidateTokenAsync(token, validationParameters).ConfigureAwait(false).GetAwaiter().GetResult();
+ if (!tokenValidationResult.IsValid)
+ {
+ throw tokenValidationResult.Exception;
+ }
- return ValidateToken(samlToken, token, validationParameters, out validatedToken);
+ validatedToken = tokenValidationResult.SecurityToken;
+ return new ClaimsPrincipal(tokenValidationResult.ClaimsIdentity);
}
private ClaimsPrincipal ValidateToken(Saml2SecurityToken samlToken, string token, TokenValidationParameters validationParameters, out SecurityToken validatedToken)
diff --git a/test/Microsoft.IdentityModel.Tokens.Saml.Tests/Saml2SecurityTokenHandlerTests.cs b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/Saml2SecurityTokenHandlerTests.cs
index 7c7915d2f0..bf4c87f786 100644
--- a/test/Microsoft.IdentityModel.Tokens.Saml.Tests/Saml2SecurityTokenHandlerTests.cs
+++ b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/Saml2SecurityTokenHandlerTests.cs
@@ -5,6 +5,8 @@
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
+using Microsoft.IdentityModel.Protocols;
+using Microsoft.IdentityModel.Protocols.WsFederation;
using Microsoft.IdentityModel.TestUtils;
using Microsoft.IdentityModel.Tokens.Saml.Tests;
using Microsoft.IdentityModel.Xml;
@@ -672,6 +674,63 @@ public static TheoryData ValidateTokenTheoryData
Token = ReferenceTokens.Saml2Token_NoAttributes,
ValidationParameters = new TokenValidationParameters(),
},
+ new Saml2TheoryData("ReferenceTokens_Saml2Token_Valid_Issuer_ConfigurationManager")
+ {
+ ExpectedException = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10500:"),
+ Token = ReferenceTokens.Saml2Token_Valid,
+ ValidationParameters = new TokenValidationParameters
+ {
+ ConfigurationManager = new StaticConfigurationManager(new WsFederationConfiguration()
+ {
+ Issuer = "https://sts.windows.net/add29489-7269-41f4-8841-b63c95564420/",
+ }),
+ ValidateIssuerSigningKey = false,
+ ValidateIssuer = true,
+ ValidateAudience = false,
+ ValidateLifetime = false,
+ }
+ },
+ new Saml2TheoryData("ReferenceTokens_Saml2Token_Valid_IssuerSigningKey_ConfigurationManager")
+ {
+ Token = ReferenceTokens.Saml2Token_Valid,
+ ValidationParameters = new TokenValidationParameters
+ {
+ ConfigurationManager = new StaticConfigurationManager(new WsFederationConfiguration()
+ {
+ SigningKeys = { KeyingMaterial.DefaultAADSigningKey },
+ }),
+ ValidateIssuer = false,
+ ValidateAudience = false,
+ ValidateLifetime = false,
+ }
+ },
+ new Saml2TheoryData("ReferenceTokens_Saml2Token_Valid_IssuerSigningKey_and_Issuer_ConfigurationManager")
+ {
+ Token = ReferenceTokens.Saml2Token_Valid,
+ ValidationParameters = new TokenValidationParameters
+ {
+ ConfigurationManager = new StaticConfigurationManager(new WsFederationConfiguration()
+ {
+ Issuer = "https://sts.windows.net/add29489-7269-41f4-8841-b63c95564420/",
+ SigningKeys = { KeyingMaterial.DefaultAADSigningKey },
+ }),
+ ValidateIssuer = true,
+ ValidateAudience = false,
+ ValidateLifetime = false,
+ }
+ },
+ new Saml2TheoryData("ReferenceTokens_Saml2Token_Valid_NoSigningKey_ConfigurationManager")
+ {
+ ExpectedException = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10500:"),
+ Token = ReferenceTokens.Saml2Token_Valid,
+ ValidationParameters = new TokenValidationParameters
+ {
+ ConfigurationManager = new StaticConfigurationManager(new WsFederationConfiguration()),
+ ValidateIssuer = false,
+ ValidateAudience = false,
+ ValidateLifetime = false,
+ }
+ },
new Saml2TheoryData("ReferenceTokens_Saml2Token_Valid_IssuerSigningKey_set")
{
Token = ReferenceTokens.Saml2Token_Valid,
diff --git a/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlSecurityTokenHandlerTests.cs b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlSecurityTokenHandlerTests.cs
index 96bb7ebd79..c22802d852 100644
--- a/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlSecurityTokenHandlerTests.cs
+++ b/test/Microsoft.IdentityModel.Tokens.Saml.Tests/SamlSecurityTokenHandlerTests.cs
@@ -7,7 +7,10 @@
using System.IO;
using System.Security.Claims;
using System.Security.Cryptography;
+using System.Threading.Tasks;
using System.Xml;
+using Microsoft.IdentityModel.Protocols;
+using Microsoft.IdentityModel.Protocols.WsFederation;
using Microsoft.IdentityModel.TestUtils;
using Microsoft.IdentityModel.Xml;
using Xunit;
@@ -319,6 +322,23 @@ public void ValidateToken(SamlTheoryData theoryData)
TestUtilities.AssertFailIfErrors(context);
}
+ [Theory, MemberData(nameof(ValidateTokenTheoryData))]
+ public async Task ValidateTokenAsync(SamlTheoryData theoryData)
+ {
+ var context = TestUtilities.WriteHeader($"{this}.ValidateToken", theoryData);
+ var validationResult = await (theoryData.Handler as SamlSecurityTokenHandler).ValidateTokenAsync(theoryData.Token, theoryData.ValidationParameters);
+ if (validationResult.Exception != null)
+ {
+ theoryData.ExpectedException.ProcessException(validationResult.Exception, context);
+ }
+ else
+ {
+ theoryData.ExpectedException.ProcessNoException(context);
+ }
+
+ TestUtilities.AssertFailIfErrors(context);
+ }
+
public static TheoryData ValidateTokenTheoryData
{
get
@@ -427,6 +447,63 @@ public static TheoryData ValidateTokenTheoryData
Token = ReferenceTokens.SamlToken_NoAttributes,
ValidationParameters = new TokenValidationParameters(),
},
+ new SamlTheoryData("ReferenceTokens_SamlToken_Valid_Issuer_ConfigurationManager")
+ {
+ ExpectedException = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10500:"),
+ Token = ReferenceTokens.SamlToken_Valid,
+ ValidationParameters = new TokenValidationParameters
+ {
+ ConfigurationManager = new StaticConfigurationManager(new WsFederationConfiguration()
+ {
+ Issuer = "http://Default.Issuer.com",
+ }),
+ ValidateIssuerSigningKey = false,
+ ValidateIssuer = true,
+ ValidateAudience = false,
+ ValidateLifetime = false,
+ }
+ },
+ new SamlTheoryData("ReferenceTokens_SamlToken_Valid_IssuerSigningKey_ConfigurationManager")
+ {
+ Token = ReferenceTokens.SamlToken_Valid,
+ ValidationParameters = new TokenValidationParameters
+ {
+ ConfigurationManager = new StaticConfigurationManager(new WsFederationConfiguration()
+ {
+ SigningKeys = { KeyingMaterial.DefaultX509SigningCreds_2048_RsaSha2_Sha2.Key },
+ }),
+ ValidateIssuer = false,
+ ValidateAudience = false,
+ ValidateLifetime = false,
+ }
+ },
+ new SamlTheoryData("ReferenceTokens_SamlToken_Valid_IssuerSigningKey_and_Issuer_ConfigurationManager")
+ {
+ Token = ReferenceTokens.SamlToken_Valid,
+ ValidationParameters = new TokenValidationParameters
+ {
+ ConfigurationManager = new StaticConfigurationManager(new WsFederationConfiguration()
+ {
+ Issuer = "http://Default.Issuer.com",
+ SigningKeys = { KeyingMaterial.DefaultX509SigningCreds_2048_RsaSha2_Sha2.Key },
+ }),
+ ValidateIssuer = true,
+ ValidateAudience = false,
+ ValidateLifetime = false,
+ }
+ },
+ new SamlTheoryData("ReferenceTokens_SamlToken_Valid_NoSigningKey_ConfigurationManager")
+ {
+ ExpectedException = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10500:"),
+ Token = ReferenceTokens.SamlToken_Valid,
+ ValidationParameters = new TokenValidationParameters
+ {
+ ConfigurationManager = new StaticConfigurationManager(new WsFederationConfiguration()),
+ ValidateIssuer = false,
+ ValidateAudience = false,
+ ValidateLifetime = false,
+ }
+ },
new SamlTheoryData("ReferenceTokens_SamlToken_Valid_IssuerSigningKey_Set")
{
Token = ReferenceTokens.SamlToken_Valid,