From 23808d5c7b11c3e0e9f202e48129c054e2b4f7ab Mon Sep 17 00:00:00 2001
From: Victor Hernandez Guzman
<11362043+victorm-hernandez@users.noreply.github.com>
Date: Tue, 20 Jun 2023 20:22:09 -0700
Subject: [PATCH] (feature): Add ActiveTokenEndpoint to
WsFederationConfiguration (#2100)
Added WsFederationConfigurationValidator, updates to WsFederationMetadataSerializer/WsFederationConfiguration
Added support to the WsFederationMetadataSerializer to read the
SecurityTokenSerivceEndpoint element from WS-Federation metadata and
exposed it via the WsFederationConfiguration object as ActiveTokenEndpoint.
Added WsFederationConfigurationValidator and related test cases.
---
.../OpenIdConnectConfiguration.cs | 6 +
.../WsFederationConfigurationValidator.cs | 205 +++++++++++++
.../LogMessages.cs | 30 +-
.../SecurityTokenServiceTypeRoleDescriptor.cs | 14 +-
.../WsFederationConstants.cs | 2 +
.../WsFederationMetadataSerializer.cs | 105 ++++++-
.../BaseConfiguration.cs | 8 +-
.../OpenIdConnectConfigurationTests.cs | 4 +-
...WsFederationConfigurationRetrieverTests.cs | 145 ++++++++--
...WsFederationConfigurationValidatorTests.cs | 269 ++++++++++++++++++
.../IdentityComparer.cs | 11 +
.../ReferenceMetadata.cs | 161 +++++++++--
12 files changed, 900 insertions(+), 60 deletions(-)
create mode 100644 src/Microsoft.IdentityModel.Protocols.WsFederation/Configuration/WsFederationConfigurationValidator.cs
create mode 100644 test/Microsoft.IdentityModel.Protocols.WsFederation.Tests/WsFederationConfigurationValidatorTests.cs
diff --git a/src/Microsoft.IdentityModel.Protocols.OpenIdConnect/Configuration/OpenIdConnectConfiguration.cs b/src/Microsoft.IdentityModel.Protocols.OpenIdConnect/Configuration/OpenIdConnectConfiguration.cs
index 521d8c17c8..77252a50fc 100644
--- a/src/Microsoft.IdentityModel.Protocols.OpenIdConnect/Configuration/OpenIdConnectConfiguration.cs
+++ b/src/Microsoft.IdentityModel.Protocols.OpenIdConnect/Configuration/OpenIdConnectConfiguration.cs
@@ -317,6 +317,12 @@ public OpenIdConnectConfiguration(string json)
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = OpenIdProviderMetadataNames.TokenEndpoint, Required = Required.Default)]
public override string TokenEndpoint { get; set; }
+ ///
+ /// This base class property is not used in OpenIdConnect.
+ ///
+ [JsonIgnore]
+ public override string ActiveTokenEndpoint { get; set; }
+
///
/// Gets the collection of 'token_endpoint_auth_methods_supported'.
///
diff --git a/src/Microsoft.IdentityModel.Protocols.WsFederation/Configuration/WsFederationConfigurationValidator.cs b/src/Microsoft.IdentityModel.Protocols.WsFederation/Configuration/WsFederationConfigurationValidator.cs
new file mode 100644
index 0000000000..d5d5275e02
--- /dev/null
+++ b/src/Microsoft.IdentityModel.Protocols.WsFederation/Configuration/WsFederationConfigurationValidator.cs
@@ -0,0 +1,205 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Security.Cryptography;
+using System.Security.Cryptography.X509Certificates;
+using Microsoft.IdentityModel.Tokens;
+using Microsoft.IdentityModel.Xml;
+using static Microsoft.IdentityModel.Logging.LogHelper;
+
+namespace Microsoft.IdentityModel.Protocols.WsFederation
+{
+ ///
+ /// Defines a class for validating the WsFederationConfiguration.
+ ///
+ public class WsFederationConfigurationValidator : IConfigurationValidator
+ {
+ ///
+ /// Validates a WsFederationConfiguration.
+ ///
+ /// WsFederationConfiguration to validate
+ /// A containing the validation result.
+ /// If the provided configuration is null
+ public ConfigurationValidationResult Validate(WsFederationConfiguration configuration)
+ {
+ if (configuration == null)
+ throw LogArgumentNullException(nameof(configuration));
+
+ if (string.IsNullOrWhiteSpace(configuration.Issuer))
+ {
+ return new ConfigurationValidationResult
+ {
+ ErrorMessage = LogMessages.IDX22700,
+ Succeeded = false
+ };
+ }
+
+ if (configuration.Signature == null)
+ {
+ return new ConfigurationValidationResult
+ {
+ ErrorMessage = LogMessages.IDX22701,
+ Succeeded = false
+ };
+ }
+
+ if (configuration.Signature.KeyInfo == null)
+ {
+ return new ConfigurationValidationResult
+ {
+ ErrorMessage = LogMessages.IDX22702,
+ Succeeded = false
+ };
+ }
+
+ if (string.IsNullOrWhiteSpace(configuration.Signature.SignatureValue))
+ {
+ return new ConfigurationValidationResult
+ {
+ ErrorMessage = LogMessages.IDX22703,
+ Succeeded = false
+ };
+ }
+
+ if (configuration.Signature.SignedInfo == null)
+ {
+ return new ConfigurationValidationResult
+ {
+ ErrorMessage = LogMessages.IDX22704,
+ Succeeded = false
+ };
+ }
+
+ if (string.IsNullOrWhiteSpace(configuration.Signature.SignedInfo.SignatureMethod))
+ {
+ return new ConfigurationValidationResult
+ {
+ ErrorMessage = LogMessages.IDX22705,
+ Succeeded = false
+ };
+ }
+
+ if (configuration.Signature.SignedInfo.References == null || configuration.Signature.SignedInfo.References.Count == 0)
+ {
+ return new ConfigurationValidationResult
+ {
+ ErrorMessage = LogMessages.IDX22706,
+ Succeeded = false
+ };
+ }
+
+ if (string.IsNullOrWhiteSpace(configuration.ActiveTokenEndpoint))
+ {
+ return new ConfigurationValidationResult
+ {
+ ErrorMessage = LogMessages.IDX22707,
+ Succeeded = false
+ };
+ }
+
+ if (!Uri.IsWellFormedUriString(configuration.ActiveTokenEndpoint, UriKind.Absolute))
+ {
+ return new ConfigurationValidationResult
+ {
+ ErrorMessage = LogMessages.IDX22708,
+ Succeeded = false
+ };
+ }
+
+ if (string.IsNullOrWhiteSpace(configuration.TokenEndpoint))
+ {
+ return new ConfigurationValidationResult
+ {
+ ErrorMessage = LogMessages.IDX22709,
+ Succeeded = false
+ };
+ }
+
+ if (!Uri.IsWellFormedUriString(configuration.TokenEndpoint, UriKind.Absolute))
+ {
+ return new ConfigurationValidationResult
+ {
+ ErrorMessage = LogMessages.IDX22710,
+ Succeeded = false
+ };
+ }
+
+ if (configuration.SigningKeys == null || configuration.SigningKeys.Count == 0)
+ {
+ return new ConfigurationValidationResult
+ {
+ ErrorMessage = LogMessages.IDX22711,
+ Succeeded = false
+ };
+ }
+
+ // Get the thumbprint of the cert used to sign the metadata
+ string signingKeyId = string.Empty;
+ var signatureX509Data = configuration.Signature.KeyInfo.X509Data.GetEnumerator();
+
+ if (signatureX509Data.MoveNext())
+ {
+ var signatureCertData = signatureX509Data.Current.Certificates.GetEnumerator();
+ if (signatureCertData.MoveNext() && !string.IsNullOrWhiteSpace(signatureCertData.Current))
+ {
+ X509Certificate2 cert = null;
+
+ try
+ {
+ cert = new X509Certificate2(Convert.FromBase64String(signatureCertData.Current));
+ signingKeyId = cert.Thumbprint;
+ }
+ catch (CryptographicException)
+ {
+ return new ConfigurationValidationResult
+ {
+ ErrorMessage = LogMessages.IDX22712,
+ Succeeded = false
+ };
+ }
+ finally
+ {
+ if (cert != null)
+ {
+ ((IDisposable)cert).Dispose();
+ }
+ }
+ }
+ }
+
+ // We know the key used to sign the doc is part of the token signing keys as per the spec.
+ // http://docs.oasis-open.org/wsfed/federation/v1.2/os/ws-federation-1.2-spec-os.html#_Toc223174958:~:text=%3C/fed%3ATargetScopes%20%3E-,3.1.15%20%5BSignature%5D%20Property,-The%20OPTIONAL%20%5BSignature
+ // If the metadata is for a token issuer then the key used to sign issued tokens SHOULD
+ // be used to sign this document. This means that if a is specified,
+ // it SHOULD be used to sign this document.
+
+ foreach (SecurityKey key in configuration.SigningKeys)
+ {
+ if (key == null || key.CryptoProviderFactory == null || signingKeyId != key.KeyId)
+ continue;
+
+ try
+ {
+ configuration.Signature.Verify(key, key.CryptoProviderFactory);
+
+ return new ConfigurationValidationResult
+ {
+ Succeeded = true
+ };
+ }
+ catch (XmlValidationException)
+ {
+ // We know the signature is invalid at this point
+ break;
+ }
+ }
+
+ return new ConfigurationValidationResult
+ {
+ ErrorMessage = LogMessages.IDX22713,
+ Succeeded = false
+ };
+ }
+ }
+}
diff --git a/src/Microsoft.IdentityModel.Protocols.WsFederation/LogMessages.cs b/src/Microsoft.IdentityModel.Protocols.WsFederation/LogMessages.cs
index 2194fd704a..6c34b9ac0c 100644
--- a/src/Microsoft.IdentityModel.Protocols.WsFederation/LogMessages.cs
+++ b/src/Microsoft.IdentityModel.Protocols.WsFederation/LogMessages.cs
@@ -23,17 +23,35 @@ internal static class LogMessages
internal const string IDX22904 = "IDX22904: Wresult does not contain a 'RequestedSecurityToken' element.";
// xml metadata messages
- internal const string IDX22800 = "IDX22800: Exception thrown while reading WsFedereationMetadata. Element '{0}'. Caught exception: '{1}'.";
- internal const string IDX22801 = "IDX22801: entityID attribute is not found in EntityDescriptor element in metadata file.";
+ internal const string IDX22800 = "IDX22800: Exception thrown while reading WsFederationMetadata. Element '{0}'. Caught exception: '{1}'.";
+ internal const string IDX22801 = "IDX22801: 'entityID' attribute is not found in EntityDescriptor element in metadata file.";
internal const string IDX22802 = "IDX22802: Current name '{0} and namespace '{1}' do not match the expected name '{2}' and namespace '{3}'.";
- internal const string IDX22803 = "IDX22803: Token reference address is missing in SecurityTokenServiceEndpoint in metadata file.";
- internal const string IDX22804 = "IDX22804: Security token type role descriptor is expected.";
- internal const string IDX22806 = "IDX22806: Key descriptor for signing is missing in security token service type RoleDescriptor.";
- internal const string IDX22807 = "IDX22807: Token endpoint is missing in security token service type RoleDescriptor.";
+ internal const string IDX22803 = "IDX22803: Token reference address is missing in 'PassiveRequestorEndpoint' in metadata file.";
+ internal const string IDX22804 = "IDX22804: 'SecurityTokenServiceTypeRoleDescriptor' is expected.";
+ internal const string IDX22806 = "IDX22806: Key descriptor for signing is missing in 'SecurityTokenServiceTypeRoleDescriptor'.";
+ internal const string IDX22807 = "IDX22807: Token endpoint is missing in 'SecurityTokenServiceTypeRoleDescriptor'.";
internal const string IDX22808 = "IDX22808: 'Use' attribute is missing in KeyDescriptor.";
internal const string IDX22810 = "IDX22810: 'Issuer' value is missing in wsfederationconfiguration.";
internal const string IDX22811 = "IDX22811: 'TokenEndpoint' value is missing in wsfederationconfiguration.";
internal const string IDX22812 = "IDX22812: Element: '{0}' was an empty element. 'TokenEndpoint' value is missing in wsfederationconfiguration.";
+ internal const string IDX22813 = "IDX22813: 'ActiveTokenEndpoint' is missing in 'SecurityTokenServiceTypeRoleDescriptor'.";
+ internal const string IDX22814 = "IDX22814: Token reference address is missing in 'SecurityTokenServiceEndpoint' in metadata.";
+
+ // WsFederationConfigurationValidator messages
+ internal const string IDX22700 = "IDX22700: The Issuer property is null or empty.";
+ internal const string IDX22701 = "IDX22701: The Signature property is null.";
+ internal const string IDX22702 = "IDX22702: The Signature's KeyInfo property is null.";
+ internal const string IDX22703 = "IDX22703: The Signature's SignatureValue property is null or empty.";
+ internal const string IDX22704 = "IDX22704: The Signature.SignedInfo property is null or empty.";
+ internal const string IDX22705 = "IDX22705: The Signature.SignedInfo.SignatureMethod property is null or empty.";
+ internal const string IDX22706 = "IDX22706: The Signature.SignedInfo.References property is null or an empty collection.";
+ internal const string IDX22707 = "IDX22707: The ActiveTokenEndpoint property is not defined.";
+ internal const string IDX22708 = "IDX22708: The ActiveTokenEndpoint property is not a valid URI.";
+ internal const string IDX22709 = "IDX22709: The TokenEndpoint property is not defined.";
+ internal const string IDX22710 = "IDX22710: The TokenEndpoint property is not a valid URI.";
+ internal const string IDX22711 = "IDX22711: The SigningKeys is null or an empty collection.";
+ internal const string IDX22712 = "IDX22712: Could not identify the thumbprint of the key used to sign the metadata.";
+ internal const string IDX22713 = "IDX22713: Metadata signature validation failed.";
#pragma warning restore 1591
}
diff --git a/src/Microsoft.IdentityModel.Protocols.WsFederation/SecurityTokenServiceTypeRoleDescriptor.cs b/src/Microsoft.IdentityModel.Protocols.WsFederation/SecurityTokenServiceTypeRoleDescriptor.cs
index a2ab06e3ff..2bddf7dd21 100644
--- a/src/Microsoft.IdentityModel.Protocols.WsFederation/SecurityTokenServiceTypeRoleDescriptor.cs
+++ b/src/Microsoft.IdentityModel.Protocols.WsFederation/SecurityTokenServiceTypeRoleDescriptor.cs
@@ -21,13 +21,23 @@ public List KeyInfos
} = new List();
///
- /// Token endpoint
+ /// Passive Requestor Token endpoint
+ /// fed:PassiveRequestorEndpoint, https://docs.oasis-open.org/wsfed/federation/v1.2/os/ws-federation-1.2-spec-os.html#:~:text=fed%3ASecurityTokenServiceType/fed%3APassiveRequestorEndpoint
///
public string TokenEndpoint
{
get;
set;
}
+
+ ///
+ /// Active Requestor Token Endpoint
+ /// fed:SecurityTokenServiceType, http://docs.oasis-open.org/wsfed/federation/v1.2/os/ws-federation-1.2-spec-os.html#:~:text=fed%3ASecurityTokenSerivceEndpoint
+ ///
+ public string ActiveTokenEndpoint
+ {
+ get;
+ set;
+ }
}
}
-
diff --git a/src/Microsoft.IdentityModel.Protocols.WsFederation/WsFederationConstants.cs b/src/Microsoft.IdentityModel.Protocols.WsFederation/WsFederationConstants.cs
index 94a5022d70..44900ef645 100644
--- a/src/Microsoft.IdentityModel.Protocols.WsFederation/WsFederationConstants.cs
+++ b/src/Microsoft.IdentityModel.Protocols.WsFederation/WsFederationConstants.cs
@@ -5,6 +5,7 @@ namespace Microsoft.IdentityModel.Protocols.WsFederation
{
///
/// Constants for WsFederation.
+ /// As defined in the http://docs.oasis-open.org/wsfed/federation/v1.2/os/ws-federation-1.2-spec-os.html
///
public static class WsFederationConstants
{
@@ -93,6 +94,7 @@ public static class Elements
public const string KeyDescriptor = "KeyDescriptor";
public const string RoleDescriptor = "RoleDescriptor";
public const string PassiveRequestorEndpoint = "PassiveRequestorEndpoint";
+ public const string SecurityTokenServiceEndpoint = "SecurityTokenServiceEndpoint";
public const string SpssoDescriptor = "SPSSODescriptor";
}
diff --git a/src/Microsoft.IdentityModel.Protocols.WsFederation/WsFederationMetadataSerializer.cs b/src/Microsoft.IdentityModel.Protocols.WsFederation/WsFederationMetadataSerializer.cs
index 68ec35bbee..5bc0929711 100644
--- a/src/Microsoft.IdentityModel.Protocols.WsFederation/WsFederationMetadataSerializer.cs
+++ b/src/Microsoft.IdentityModel.Protocols.WsFederation/WsFederationMetadataSerializer.cs
@@ -106,7 +106,9 @@ protected virtual WsFederationConfiguration ReadEntityDescriptor(XmlReader reade
}
}
}
+
configuration.TokenEndpoint = roleDescriptor.TokenEndpoint;
+ configuration.ActiveTokenEndpoint = roleDescriptor.ActiveTokenEndpoint;
}
else
{
@@ -177,6 +179,8 @@ protected virtual SecurityTokenServiceTypeRoleDescriptor ReadSecurityTokenServic
roleDescriptor.KeyInfos.Add(ReadKeyDescriptorForSigning(reader));
else if (reader.IsStartElement(Elements.PassiveRequestorEndpoint, Namespace))
roleDescriptor.TokenEndpoint = ReadPassiveRequestorEndpoint(reader);
+ else if (reader.IsStartElement(Elements.SecurityTokenServiceEndpoint, Namespace))
+ roleDescriptor.ActiveTokenEndpoint = ReadSecurityTokenServiceEndpoint(reader);
else
reader.ReadOuterXml();
}
@@ -191,6 +195,9 @@ protected virtual SecurityTokenServiceTypeRoleDescriptor ReadSecurityTokenServic
if (string.IsNullOrEmpty(roleDescriptor.TokenEndpoint))
LogHelper.LogWarning(LogMessages.IDX22807);
+ if (string.IsNullOrEmpty(roleDescriptor.ActiveTokenEndpoint))
+ LogHelper.LogWarning(LogMessages.IDX22813);
+
return roleDescriptor;
}
@@ -211,6 +218,7 @@ protected virtual string ReadPassiveRequestorEndpoint(XmlReader reader)
reader.ReadStartElement();
reader.MoveToContent();
+ //
XmlUtil.CheckReaderOnEntry(reader, WsAddressing.Elements.EndpointReference, WsAddressing.Namespace);
if (reader.IsEmptyElement)
throw XmlUtil.LogReadException(LogMessages.IDX22812, WsAddressing.Elements.EndpointReference);
@@ -221,7 +229,9 @@ protected virtual string ReadPassiveRequestorEndpoint(XmlReader reader)
if (reader.IsEmptyElement)
throw XmlUtil.LogReadException(LogMessages.IDX22803);
+ //
XmlUtil.CheckReaderOnEntry(reader, WsAddressing.Elements.Address, WsAddressing.Namespace);
+
if (reader.IsEmptyElement)
throw XmlUtil.LogReadException(LogMessages.IDX22812, WsAddressing.Elements.Address);
@@ -248,6 +258,75 @@ protected virtual string ReadPassiveRequestorEndpoint(XmlReader reader)
return tokenEndpoint;
}
+ ///
+ /// Read fed:SecurityTokenServiceEndpoint element from metadata XML.
+ ///
+ /// used to read SecurityTokenServiceEndpoint.
+ /// Active token endpoint string
+ /// If an error occurs while reading the SecurityTokenServiceEndpoint
+ protected virtual string ReadSecurityTokenServiceEndpoint(XmlReader reader)
+ {
+ XmlUtil.CheckReaderOnEntry(reader, Elements.SecurityTokenServiceEndpoint, Namespace);
+
+ //
+ if (reader.IsEmptyElement)
+ throw XmlUtil.LogReadException(LogMessages.IDX22812, Elements.SecurityTokenServiceEndpoint);
+
+ reader.ReadStartElement();
+ reader.MoveToContent();
+
+ //
+ XmlUtil.CheckReaderOnEntry(reader, WsAddressing.Elements.EndpointReference, WsAddressing.Namespace);
+ if (reader.IsEmptyElement)
+ throw XmlUtil.LogReadException(LogMessages.IDX22812, WsAddressing.Elements.EndpointReference);
+
+ reader.ReadStartElement(WsAddressing.Elements.EndpointReference, WsAddressing.Namespace);
+ reader.MoveToContent();
+
+ if (reader.IsEmptyElement)
+ throw XmlUtil.LogReadException(LogMessages.IDX22814);
+
+ string tokenEndpoint = null;
+
+ while (reader.IsStartElement())
+ {
+ if (reader.IsStartElement(WsAddressing.Elements.Address, WsAddressing.Namespace))
+ {
+ //
+ XmlUtil.CheckReaderOnEntry(reader, WsAddressing.Elements.Address, WsAddressing.Namespace);
+
+ if (reader.IsEmptyElement)
+ throw XmlUtil.LogReadException(LogMessages.IDX22812, WsAddressing.Elements.Address);
+
+ reader.ReadStartElement(WsAddressing.Elements.Address, WsAddressing.Namespace);
+ reader.MoveToContent();
+
+ tokenEndpoint = Trim(reader.ReadContentAsString());
+
+ if (string.IsNullOrEmpty(tokenEndpoint))
+ throw XmlUtil.LogReadException(LogMessages.IDX22814);
+
+ //
+ reader.MoveToContent();
+ reader.ReadEndElement();
+ }
+ else
+ {
+ reader.ReadOuterXml();
+ }
+ }
+
+ //
+ reader.MoveToContent();
+ reader.ReadEndElement();
+
+ //
+ reader.MoveToContent();
+ reader.ReadEndElement();
+
+ return tokenEndpoint;
+ }
+
private static bool IsSecurityTokenServiceTypeRoleDescriptor(XmlReader reader)
{
if (reader == null || !reader.IsStartElement(Elements.RoleDescriptor, MetadataNamespace))
@@ -301,7 +380,7 @@ public void WriteMetadata(XmlWriter writer, WsFederationConfiguration configurat
if (string.IsNullOrEmpty(configuration.TokenEndpoint))
throw XmlUtil.LogWriteException(LogMessages.IDX22811);
-
+
if (configuration.SigningCredentials != null)
writer = new EnvelopedSignatureWriter(writer, configuration.SigningCredentials, "id");
@@ -334,6 +413,30 @@ public void WriteMetadata(XmlWriter writer, WsFederationConfiguration configurat
}
}
+ if (!string.IsNullOrEmpty(configuration.ActiveTokenEndpoint))
+ {
+ //
+ writer.WriteStartElement(PreferredPrefix, Elements.SecurityTokenServiceEndpoint, Namespace);
+
+ //
+ writer.WriteStartElement(WsAddressing.PreferredPrefix, WsAddressing.Elements.EndpointReference, WsAddressing.Namespace);
+
+ //
+ writer.WriteStartElement(WsAddressing.PreferredPrefix, WsAddressing.Elements.Address, WsAddressing.Namespace);
+
+ // write TokenEndpoint
+ writer.WriteString(configuration.ActiveTokenEndpoint);
+
+ //
+ writer.WriteEndElement();
+
+ //
+ writer.WriteEndElement();
+
+ //
+ writer.WriteEndElement();
+ }
+
//
writer.WriteStartElement(PreferredPrefix, Elements.PassiveRequestorEndpoint, Namespace);
diff --git a/src/Microsoft.IdentityModel.Tokens/BaseConfiguration.cs b/src/Microsoft.IdentityModel.Tokens/BaseConfiguration.cs
index bebbcbb95d..85f1a7578c 100644
--- a/src/Microsoft.IdentityModel.Tokens/BaseConfiguration.cs
+++ b/src/Microsoft.IdentityModel.Tokens/BaseConfiguration.cs
@@ -27,11 +27,17 @@ public virtual ICollection SigningKeys
///
/// Gets or sets the token endpoint specified via the metadata endpoint.
- /// This can be the fed:SecurityTokenServiceType in WS-Federation, http://docs.oasis-open.org/wsfed/federation/v1.2/os/ws-federation-1.2-spec-os.html#:~:text=fed%3ASecurityTokenSerivceEndpoint
+ /// This is the fed:PassiveRequestorEndpoint in WS-Federation, https://docs.oasis-open.org/wsfed/federation/v1.2/os/ws-federation-1.2-spec-os.html#:~:text=fed%3ASecurityTokenServiceType/fed%3APassiveRequestorEndpoint
/// Or the token_endpoint in the OIDC metadata.
///
public virtual string TokenEndpoint { get; set; }
+ ///
+ /// Gets or sets the token endpoint specified via the metadata endpoint.
+ /// This is the fed:SecurityTokenServiceType in WS-Federation, http://docs.oasis-open.org/wsfed/federation/v1.2/os/ws-federation-1.2-spec-os.html#:~:text=fed%3ASecurityTokenSerivceEndpoint
+ ///
+ public virtual string ActiveTokenEndpoint { get; set; }
+
///
/// Gets the that the IdentityProvider indicates are to be used in order to decrypt tokens.
///
diff --git a/test/Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests/OpenIdConnectConfigurationTests.cs b/test/Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests/OpenIdConnectConfigurationTests.cs
index 98cdd75625..04e6b2072e 100644
--- a/test/Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests/OpenIdConnectConfigurationTests.cs
+++ b/test/Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests/OpenIdConnectConfigurationTests.cs
@@ -108,8 +108,8 @@ public void GetSets()
OpenIdConnectConfiguration configuration = new OpenIdConnectConfiguration();
Type type = typeof(OpenIdConnectConfiguration);
PropertyInfo[] properties = type.GetProperties();
- if (properties.Length != 48)
- Assert.True(false, "Number of properties has changed from 47 to: " + properties.Length + ", adjust tests");
+ if (properties.Length != 49)
+ Assert.True(false, "Number of properties has changed from 49 to: " + properties.Length + ", adjust tests");
TestUtilities.CallAllPublicInstanceAndStaticPropertyGets(configuration, "OpenIdConnectConfiguration_GetSets");
diff --git a/test/Microsoft.IdentityModel.Protocols.WsFederation.Tests/WsFederationConfigurationRetrieverTests.cs b/test/Microsoft.IdentityModel.Protocols.WsFederation.Tests/WsFederationConfigurationRetrieverTests.cs
index 6caaeed3e6..f32304f90a 100644
--- a/test/Microsoft.IdentityModel.Protocols.WsFederation.Tests/WsFederationConfigurationRetrieverTests.cs
+++ b/test/Microsoft.IdentityModel.Protocols.WsFederation.Tests/WsFederationConfigurationRetrieverTests.cs
@@ -23,11 +23,10 @@ public class WsFederationConfigurationRetrieverTests
public void ReadMetadata(WsFederationMetadataTheoryData theoryData)
{
var context = TestUtilities.WriteHeader($"{this}.ReadMetadata", theoryData);
+ var configuration = new WsFederationConfiguration();
+
try
{
- var config = ReferenceMetadata.AADCommonEndpoint;
- var configuration = new WsFederationConfiguration();
-
if (!string.IsNullOrEmpty(theoryData.Metadata))
{
var reader = XmlReader.Create(new StringReader(theoryData.Metadata));
@@ -38,9 +37,9 @@ public void ReadMetadata(WsFederationMetadataTheoryData theoryData)
var reader = XmlReader.Create(theoryData.MetadataPath);
configuration = theoryData.Serializer.ReadMetadata(reader);
}
-
- if (theoryData.SigingKey != null)
- configuration.Signature.Verify(theoryData.SigingKey, theoryData.SigingKey.CryptoProviderFactory);
+
+ if (theoryData.SigningKey != null)
+ configuration.Signature.Verify(theoryData.SigningKey, theoryData.SigningKey.CryptoProviderFactory);
theoryData.ExpectedException.ProcessNoException(context);
IdentityComparer.AreWsFederationConfigurationsEqual(configuration, theoryData.Configuration, context);
@@ -64,38 +63,79 @@ public static TheoryData ReadMetadataTheoryData
{
new WsFederationMetadataTheoryData
{
+ // Base case for common scenario (not tenant specific).
+ // All data is present as expected.
Configuration = ReferenceMetadata.AADCommonEndpoint,
First = true,
Metadata = ReferenceMetadata.AADCommonMetadata,
- SigingKey = ReferenceMetadata.MetadataSigningKey,
+ SigningKey = ReferenceMetadata.MetadataSigningKey,
TestId = nameof(ReferenceMetadata.AADCommonMetadata)
},
new WsFederationMetadataTheoryData
{
+ // Only EntityDescriptor tag, empty XML.
Configuration = ReferenceMetadata.EmptyEntityDescriptor,
Metadata = ReferenceMetadata.MetadataEmptyEntityDescriptor,
TestId = nameof(ReferenceMetadata.MetadataEmptyEntityDescriptor)
},
+ // ---------------------------------------------------------------------------------------------------------------------
+ // Passive Requestor variations (EntityDescriptor\RoleDescriptor\PassiveRequestorEndpoint)
+ // ---------------------------------------------------------------------------------------------------------------------
new WsFederationMetadataTheoryData
{
+ // Empty EntityDescriptor\RoleDescriptor\PassiveRequestorEndpoint tag
+ // Error Message: "IDX22812: Element: '{0}' was an empty element. 'TokenEndpoint' value is missing in wsfederationconfiguration.";
ExpectedException = new ExpectedException(typeof(XmlReadException), "IDX22812:"),
Metadata = ReferenceMetadata.MetadataEmptyPassiveRequestorEndpoint,
TestId = nameof(ReferenceMetadata.MetadataEmptyPassiveRequestorEndpoint)
},
new WsFederationMetadataTheoryData
{
+ // Empty EntityDescriptor\RoleDescriptor\PassiveRequestorEndpoint\EndpointReference\Address tag
+ // Error Message: "IDX22803: Token reference address is missing in PassiveRequestorEndpoint in metadata file."
ExpectedException = new ExpectedException(typeof(XmlReadException), "IDX22803:"),
- Metadata = ReferenceMetadata.MetadataEmptyEndpointAddress,
- TestId = nameof(ReferenceMetadata.MetadataEmptyEndpointAddress)
+ Metadata = ReferenceMetadata.MetadataEmptyPassiveRequestorEndpointAddress,
+ TestId = nameof(ReferenceMetadata.MetadataEmptyPassiveRequestorEndpointAddress)
+ },
+ new WsFederationMetadataTheoryData
+ {
+ // Empty EntityDescriptor\RoleDescriptor\PassiveRequestorEndpoint\EndpointReference
+ // Error Message: Element: '{0}' was an empty element. 'TokenEndpoint' value is missing in wsfederationconfiguration.";
+ ExpectedException = new ExpectedException(typeof(XmlReadException), "IDX22812:"),
+ Metadata = ReferenceMetadata.MetadataEmptyPassiveRequestorEndpointReference,
+ TestId = nameof(ReferenceMetadata.MetadataEmptyPassiveRequestorEndpointReference)
+ },
+ // ---------------------------------------------------------------------------------------------------------------------
+ // SecurityTokenServiceEndpoint variations (EntityDescriptor\RoleDescriptor\SecurityTokenServiceEndpoint)
+ // ---------------------------------------------------------------------------------------------------------------------
+ new WsFederationMetadataTheoryData
+ {
+ // Empty EntityDescriptor\RoleDescriptor\SecurityTokenServiceEndpoint tag
+ // Error Message: "IDX22812: Element: '{0}' was an empty element. 'TokenEndpoint' value is missing in wsfederationconfiguration.";
+ ExpectedException = new ExpectedException(typeof(XmlReadException), "IDX22812:"),
+ Metadata = ReferenceMetadata.MetadataEmptySecurityTokenServiceEndpoint,
+ TestId = nameof(ReferenceMetadata.MetadataEmptySecurityTokenServiceEndpoint)
+ },
+ new WsFederationMetadataTheoryData
+ {
+ // Empty EntityDescriptor\RoleDescriptor\SecurityTokenServiceEndpoint\EndpointReference\Address tag
+ // Error Message: "IDX22814: Token reference address is missing in SecurityTokenServiceEndpoint in metadata file."
+ ExpectedException = new ExpectedException(typeof(XmlReadException), "IDX22814:"),
+ Metadata = ReferenceMetadata.MetadataEmptySecurityTokenServiceEndpointAddress,
+ TestId = nameof(ReferenceMetadata.MetadataEmptySecurityTokenServiceEndpointAddress)
},
new WsFederationMetadataTheoryData
{
+ // Empty EntityDescriptor\RoleDescriptor\SecurityTokenServiceEndpoint\EndpointReference
+ // Error Message: "IDX22812: Element: '{0}' was an empty element. 'TokenEndpoint' value is missing in wsfederationconfiguration.";
ExpectedException = new ExpectedException(typeof(XmlReadException), "IDX22812:"),
- Metadata = ReferenceMetadata.MetadataEmptyEndpointReference,
- TestId = nameof(ReferenceMetadata.MetadataEmptyEndpointReference)
+ Metadata = ReferenceMetadata.MetadataEmptySecurityTokenServiceEndpointReference,
+ TestId = nameof(ReferenceMetadata.MetadataEmptySecurityTokenServiceEndpointReference)
},
new WsFederationMetadataTheoryData
{
+ // Base case for tenant specific scenario (tenant 268da1a1-9db4-48b9-b1fe-683250ba90cc).
+ // All data is present as expected.
Configuration = ReferenceMetadata.AADCommonFormated,
Metadata = ReferenceMetadata.AADCommonMetadataFormated,
TestId = nameof(ReferenceMetadata.AADCommonMetadataFormated)
@@ -105,17 +145,19 @@ public static TheoryData ReadMetadataTheoryData
ExpectedException = new ExpectedException(typeof(XmlValidationException), "IDX30200:"),
Configuration = ReferenceMetadata.AADCommonFormated,
Metadata = ReferenceMetadata.AADCommonMetadataFormated,
- SigingKey = ReferenceMetadata.MetadataSigningKey,
+ SigningKey = ReferenceMetadata.MetadataSigningKey,
TestId = nameof(ReferenceMetadata.AADCommonMetadataFormated) + " Signature Failure"
},
new WsFederationMetadataTheoryData
{
+ // Validate that the presence of spaces or new lines does not affect the parsing of the XML content.
Configuration = ReferenceMetadata.AADCommonFormated,
Metadata = ReferenceMetadata.MetadataWithBlanks,
TestId = nameof(ReferenceMetadata.MetadataWithBlanks)
},
new WsFederationMetadataTheoryData
{
+ // EntityDescriptor\RoleDescriptor\KeyDescriptor is missing. Validate the resulting Configuration object only includes the data present.
Configuration = new WsFederationConfiguration
{
Issuer = ReferenceMetadata.Issuer,
@@ -126,54 +168,80 @@ public static TheoryData ReadMetadataTheoryData
},
new WsFederationMetadataTheoryData
{
+ // EntityDescriptor\@entityID attribute (issuer) is missing from EntityDescriptor tag.
+ // Error Message: IDX22801: entityID attribute is not found in EntityDescriptor element in metadata file.
ExpectedException = new ExpectedException(typeof(XmlReadException), "IDX22801:"),
Metadata = ReferenceMetadata.MetadataNoIssuer,
TestId = nameof(ReferenceMetadata.MetadataNoIssuer)
},
new WsFederationMetadataTheoryData
{
+ // EntityDescriptor\RoleDescriptor\PassiveRequestorEndpoint\EndpointReference\Address Empty Address value (white space and new line only)
+ // Error Message: "IDX22803: Token reference address is missing in PassiveRequestorEndpoint in metadata file.";
ExpectedException = new ExpectedException(typeof(XmlReadException), "IDX22803:"),
- Metadata = ReferenceMetadata.MetadataNoTokenUri,
- TestId = nameof(ReferenceMetadata.MetadataNoTokenUri)
+ Metadata = ReferenceMetadata.MetadataNoPassiveRequestorEndpointUri,
+ TestId = nameof(ReferenceMetadata.MetadataNoPassiveRequestorEndpointUri)
},
new WsFederationMetadataTheoryData
{
+ // EntityDescriptor\RoleDescriptor\SecurityTokenServiceEndpoint\EndpointReference\Address Empty Address value (white space and new line only)
+ // Error Message: "IDX22814: Token reference address is missing in SecurityTokenServiceEndpoint in metadata.";
+ ExpectedException = new ExpectedException(typeof(XmlReadException), "IDX22814:"),
+ Metadata = ReferenceMetadata.MetadataNoSecurityTokenServiceEndpointUri,
+ TestId = nameof(ReferenceMetadata.MetadataNoSecurityTokenServiceEndpointUri)
+ },
+ new WsFederationMetadataTheoryData
+ {
+ // KeyDescriptor\KeyInfo\X509Data tag holds invalid certificate data.
+ // Error Message: "IDX22800: Exception thrown while reading WsFedereationMetadata. Element '{0}'. Caught exception: '{1}'.";
ExpectedException = new ExpectedException(typeof(XmlReadException), "IDX22800:", typeof(FormatException)),
Metadata = ReferenceMetadata.MetadataMalformedCertificate,
TestId = nameof(ReferenceMetadata.MetadataMalformedCertificate)
},
+ // ---------------------------------------------------------------------------------------------------------------------
+ // XML Signature validation (EntityDescriptor\Signature)
+ // ---------------------------------------------------------------------------------------------------------------------
new WsFederationMetadataTheoryData
{
+ // Invalid XML signature. Unknown element before
+ // Error Message: "IDX30025: Unable to read XML. Expecting XmlReader to be at EndElement: '{0}'. Found XmlNode 'type.name': '{1}.{2}'.";
ExpectedException = new ExpectedException(typeof(XmlReadException), "IDX30025:"),
Metadata = ReferenceMetadata.MetadataUnknownElementBeforeSignatureEndElement,
TestId = nameof(ReferenceMetadata.MetadataUnknownElementBeforeSignatureEndElement)
},
new WsFederationMetadataTheoryData
{
+ // Invalid XML signature. SignedInfo tag is missing.
+ // Error Message: "IDX30011: Unable to read XML. Expecting XmlReader to be at ns.element: '{0}.{1}', found: '{2}.{3}'."
ExpectedException = new ExpectedException(typeof(XmlReadException), "IDX30011:"),
Metadata = ReferenceMetadata.MetadataNoSignedInfoInSignature,
TestId = nameof(ReferenceMetadata.MetadataNoSignedInfoInSignature)
},
new WsFederationMetadataTheoryData
{
+ // EntityDescriptor tag missing.
+ // Error Message: "IDX30011: Unable to read XML. Expecting XmlReader to be at ns.element: '{0}.{1}', found: '{2}.{3}'."
ExpectedException = new ExpectedException(typeof(XmlReadException), "IDX30011:"),
Metadata = ReferenceMetadata.MetadataNoEntityDescriptor,
TestId = nameof(ReferenceMetadata.MetadataNoEntityDescriptor)
},
new WsFederationMetadataTheoryData
{
+ // EntityDescriptor\RoleDescriptor tag missing.
Configuration = ReferenceMetadata.NoRoleDescriptor,
Metadata = ReferenceMetadata.MetadataNoRoleDescriptor,
TestId = nameof(ReferenceMetadata.MetadataNoRoleDescriptor)
},
new WsFederationMetadataTheoryData
{
+ // EntityDescriptor\RoleDescriptor\KeyDescriptor\KeyInfo tag missing.
ExpectedException = new ExpectedException(typeof(XmlReadException), "IDX22802:"),
Metadata = ReferenceMetadata.MetadataNoKeyInfoInKeyDescriptor,
TestId = nameof(ReferenceMetadata.MetadataNoKeyInfoInKeyDescriptor)
},
new WsFederationMetadataTheoryData
{
+ // EntityDescriptor\RoleDescriptor\PassiveRequestorEndpoint tag is missing.
Configuration = new WsFederationConfiguration
{
Issuer = ReferenceMetadata.Issuer
@@ -183,55 +251,74 @@ public static TheoryData ReadMetadataTheoryData
},
new WsFederationMetadataTheoryData
{
+ // EntityDescriptor\RoleDescriptor\PassiveRequestorEndpoint\EndpointReference tag is missing.
+ // Error Message: "IDX30011: Unable to read XML. Expecting XmlReader to be at ns.element: '{0}.{1}', found: '{2}.{3}'."
ExpectedException = new ExpectedException(typeof(XmlReadException), "IDX30011:"),
Metadata = ReferenceMetadata.MetadataNoEndpointReference,
TestId = nameof(ReferenceMetadata.MetadataNoEndpointReference)
},
new WsFederationMetadataTheoryData
{
+ // EntityDescriptor\RoleDescriptor\PassiveRequestorEndpoint\EndpointReference\Address tag is missing.
+ // Error Message: "IDX30011: Unable to read XML. Expecting XmlReader to be at ns.element: '{0}.{1}', found: '{2}.{3}'."
ExpectedException = new ExpectedException(typeof(XmlReadException), "IDX30011:"),
Metadata = ReferenceMetadata.MetadataNoAddressInEndpointReference,
TestId = nameof(ReferenceMetadata.MetadataNoAddressInEndpointReference)
},
+ // ---------------------------------------------------------------------------------------------------------------------
+ // Active Directory Federation Services
+ // ---------------------------------------------------------------------------------------------------------------------
new WsFederationMetadataTheoryData
{
+ // Base case for Active Directory Federation Services V2.
+ // All data present and valid.
Metadata = ReferenceMetadata.AdfsV2Metadata,
- SigingKey = ReferenceMetadata.AdfsV2MetadataSigningKey,
+ SigningKey = ReferenceMetadata.AdfsV2MetadataSigningKey,
Configuration = ReferenceMetadata.AdfsV2Endpoint,
TestId = nameof(ReferenceMetadata.AdfsV2Metadata)
},
new WsFederationMetadataTheoryData
{
+ // Base case for Active Directory Federation Services V3.
+ // All data present and valid.
Metadata = ReferenceMetadata.AdfsV3Metadata,
- SigingKey = ReferenceMetadata.AdfsV3MetadataSigningKey,
+ SigningKey = ReferenceMetadata.AdfsV3MetadataSigningKey,
Configuration = ReferenceMetadata.AdfsV3Endpoint,
TestId = nameof(ReferenceMetadata.AdfsV3Metadata)
},
new WsFederationMetadataTheoryData
{
+ // Base case for Active Directory Federation Services V4.
+ // All data present and valid.
Metadata = ReferenceMetadata.AdfsV4Metadata,
- SigingKey = ReferenceMetadata.AdfsV4MetadataSigningKey,
+ SigningKey = ReferenceMetadata.AdfsV4MetadataSigningKey,
Configuration = ReferenceMetadata.AdfsV4Endpoint,
TestId = nameof(ReferenceMetadata.AdfsV4Metadata)
},
new WsFederationMetadataTheoryData
{
+ // Base case for Active Directory Federation Services V2.
+ // All data present and valid.
MetadataPath = Path.Combine(Directory.GetCurrentDirectory(), "../../../adfs-v2-metadata.xml"),
- SigingKey = ReferenceMetadata.AdfsV2MetadataSigningKey,
+ SigningKey = ReferenceMetadata.AdfsV2MetadataSigningKey,
Configuration = ReferenceMetadata.AdfsV2Endpoint,
TestId = "AdfsV2Metadata from xml file"
},
new WsFederationMetadataTheoryData
{
+ // Base case for Active Directory Federation Services V3.
+ // All data present and valid.
MetadataPath = Path.Combine(Directory.GetCurrentDirectory(), "../../../adfs-v3-metadata.xml"),
- SigingKey = ReferenceMetadata.AdfsV3MetadataSigningKey,
+ SigningKey = ReferenceMetadata.AdfsV3MetadataSigningKey,
Configuration = ReferenceMetadata.AdfsV3Endpoint,
TestId = "AdfsV3Metadata from xml file"
},
new WsFederationMetadataTheoryData
{
+ // Base case for Active Directory Federation Services V4.
+ // All data present and valid.
MetadataPath = Path.Combine(Directory.GetCurrentDirectory(), "../../../adfs-v4-metadata.xml"),
- SigingKey = ReferenceMetadata.AdfsV4MetadataSigningKey,
+ SigningKey = ReferenceMetadata.AdfsV4MetadataSigningKey,
Configuration = ReferenceMetadata.AdfsV4Endpoint,
TestId = "AdfsV4Metadata from xml file"
}
@@ -492,6 +579,8 @@ public void WriteMetadata(WsFederationMetadataTheoryData theoryData)
// remove the signature and do the comparison
configuration.Signature = null;
+ theoryData.Configuration.Signature = null;
+
theoryData.ExpectedException.ProcessNoException(context);
IdentityComparer.AreWsFederationConfigurationsEqual(configuration, theoryData.Configuration, context);
}
@@ -540,7 +629,19 @@ public static TheoryData WriteMetadataTheoryData
ExpectedException = new ExpectedException(typeof(XmlWriteException), "IDX22811:"),
Configuration = ReferenceMetadata.AADCommonFormatedNoTokenEndpoint,
TestId = nameof(ReferenceMetadata.AADCommonFormatedNoTokenEndpoint)
- }
+ },
+ new WsFederationMetadataTheoryData
+ {
+ // The active token endpoint is optional at this point and should not throw an exception if missing.
+ Configuration = ReferenceMetadata.AADCommonFormatedNoActiveTokenEndpoint,
+ TestId = nameof(ReferenceMetadata.AADCommonFormatedNoActiveTokenEndpoint)
+ },
+ new WsFederationMetadataTheoryData
+ {
+ // All data is present including signature and SigningCredentials (required for signature validation)
+ Configuration = ReferenceMetadata.AADCommonFormated,
+ TestId = nameof(ReferenceMetadata.AADCommonFormated)
+ },
};
}
}
@@ -555,7 +656,7 @@ public class WsFederationMetadataTheoryData : TheoryDataBase
public WsFederationMetadataSerializer Serializer { get; set; } = new WsFederationMetadataSerializer();
- public SecurityKey SigingKey { get; set; }
+ public SecurityKey SigningKey { get; set; }
public override string ToString()
{
diff --git a/test/Microsoft.IdentityModel.Protocols.WsFederation.Tests/WsFederationConfigurationValidatorTests.cs b/test/Microsoft.IdentityModel.Protocols.WsFederation.Tests/WsFederationConfigurationValidatorTests.cs
new file mode 100644
index 0000000000..f3610e8ca9
--- /dev/null
+++ b/test/Microsoft.IdentityModel.Protocols.WsFederation.Tests/WsFederationConfigurationValidatorTests.cs
@@ -0,0 +1,269 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.IO;
+using System.Xml;
+using Microsoft.IdentityModel.TestUtils;
+using Xunit;
+
+#pragma warning disable CS3016 // Arrays as attribute arguments is not CLS-compliant
+
+namespace Microsoft.IdentityModel.Protocols.WsFederation.Tests
+{
+ ///
+ /// WS Federation Configuration Validator tests.
+ ///
+ public class WsFederationConfigurationValidatorTests
+ {
+ [Theory, MemberData(nameof(ValidateConfigurationTheoryData))]
+ public void ValidateConfiguration(WsFederationConfigurationTheoryData theoryData)
+ {
+ var context = TestUtilities.WriteHeader($"{this}.ValidateConfiguration", theoryData);
+ var validator = new WsFederationConfigurationValidator();
+ var configToValidate = theoryData.Configuration;
+
+ if (!string.IsNullOrEmpty(theoryData.Metadata))
+ {
+ var reader = XmlReader.Create(new StringReader(theoryData.Metadata));
+ configToValidate = new WsFederationMetadataSerializer().ReadMetadata(reader);
+ }
+
+ try
+ {
+ var result = validator.Validate(configToValidate);
+ theoryData.ExpectedException.ProcessNoException(context);
+ IdentityComparer.AreConfigurationValidationResultEqual(result, theoryData.ExpectedResult, context);
+ }
+ catch (Exception ex)
+ {
+ theoryData.ExpectedException.ProcessException(ex, context);
+ }
+
+ TestUtilities.AssertFailIfErrors(context);
+ }
+
+ public static TheoryData ValidateConfigurationTheoryData
+ {
+ get
+ {
+ return new TheoryData
+ {
+ new WsFederationConfigurationTheoryData
+ {
+ // Base case for common scenario. All data is present as expected.
+ Metadata = ReferenceMetadata.AADCommonMetadata,
+ ExpectedResult = new ConfigurationValidationResult
+ {
+ Succeeded = true
+ },
+ TestId = nameof(ReferenceMetadata.AADCommonMetadata)
+ },
+ new WsFederationConfigurationTheoryData
+ {
+ // Base case for Active Directory Federation Services V4. All data is present as expected.
+ Metadata = ReferenceMetadata.AdfsV4Metadata,
+ ExpectedResult = new ConfigurationValidationResult
+ {
+ Succeeded = true
+ },
+ TestId = nameof(ReferenceMetadata.AdfsV4Metadata)
+ },
+ new WsFederationConfigurationTheoryData
+ {
+ Configuration =null,
+ ExpectedException = new ExpectedException(typeof(ArgumentNullException), "IDX10000:"),
+ TestId = "NullConfiguration"
+ },
+ new WsFederationConfigurationTheoryData
+ {
+ Configuration = ((Func)(() =>{
+ var config = ReferenceMetadata.AdfsV4Endpoint;
+ config.Issuer = null;
+ return config;
+ }))(),
+ ExpectedResult = new ConfigurationValidationResult
+ {
+ Succeeded = false,
+ ErrorMessage = LogMessages.IDX22700
+ },
+ TestId = "NullIssuer"
+ },
+ new WsFederationConfigurationTheoryData
+ {
+ Configuration = ((Func)(() =>{
+ var config = ReferenceMetadata.AdfsV4Endpoint;
+ config.Signature.KeyInfo = null;
+ return config;
+ }))(),
+ ExpectedResult = new ConfigurationValidationResult
+ {
+ Succeeded = false,
+ ErrorMessage = LogMessages.IDX22702
+ },
+ TestId = "NullSignatureKeyInfo"
+ },
+ new WsFederationConfigurationTheoryData
+ {
+ Configuration = ((Func)(() =>{
+ var config = ReferenceMetadata.AdfsV4Endpoint;
+ config.Signature.SignatureValue = " ";
+ return config;
+ }))(),
+ ExpectedResult = new ConfigurationValidationResult
+ {
+ Succeeded = false,
+ ErrorMessage = LogMessages.IDX22703
+ },
+ TestId = "EmptySignatureValue"
+ },
+ new WsFederationConfigurationTheoryData
+ {
+ Configuration = ((Func)(() =>{
+ var config = ReferenceMetadata.AdfsV4Endpoint;
+ config.Signature.SignedInfo.SignatureMethod = " ";
+ return config;
+ }))(),
+ ExpectedResult = new ConfigurationValidationResult
+ {
+ Succeeded = false,
+ ErrorMessage = LogMessages.IDX22705
+ },
+ TestId = "EmptySignatureMethod"
+ },
+ new WsFederationConfigurationTheoryData
+ {
+ Configuration = ((Func)(() =>{
+ var config = ReferenceMetadata.AdfsV4Endpoint;
+ config.Signature.SignedInfo.References.Clear();
+ return config;
+ }))(),
+ ExpectedResult = new ConfigurationValidationResult
+ {
+ Succeeded = false,
+ ErrorMessage = LogMessages.IDX22706
+ },
+ TestId = "NoSignatureReferences"
+ },
+ new WsFederationConfigurationTheoryData
+ {
+ Configuration = ((Func)(() =>{
+ var config = ReferenceMetadata.AdfsV4Endpoint;
+ config.ActiveTokenEndpoint = string.Empty;
+ return config;
+ }))(),
+ ExpectedResult = new ConfigurationValidationResult
+ {
+ Succeeded = false,
+ ErrorMessage = LogMessages.IDX22707
+ },
+ TestId = "EmptyActiveTokenEndpoint"
+ },
+ new WsFederationConfigurationTheoryData
+ {
+ Configuration = ((Func)(() =>{
+ var config = ReferenceMetadata.AdfsV4Endpoint;
+ config.ActiveTokenEndpoint = "SomeRandomValue@here";
+ return config;
+ }))(),
+ ExpectedResult = new ConfigurationValidationResult
+ {
+ Succeeded = false,
+ ErrorMessage = LogMessages.IDX22708
+ },
+ TestId = "InvalidActiveTokenEndpointUri"
+ },
+ new WsFederationConfigurationTheoryData
+ {
+ Configuration = ((Func)(() =>{
+ var config = ReferenceMetadata.AdfsV4Endpoint;
+ config.TokenEndpoint = string.Empty;
+ return config;
+ }))(),
+ ExpectedResult = new ConfigurationValidationResult
+ {
+ Succeeded = false,
+ ErrorMessage = LogMessages.IDX22709
+ },
+ TestId = "EmptyTokenEndpoint"
+ },
+ new WsFederationConfigurationTheoryData
+ {
+ Configuration = ((Func)(() =>{
+ var config = ReferenceMetadata.AdfsV4Endpoint;
+ config.TokenEndpoint = "SomeStringThatIsNotAUrl!";
+ return config;
+ }))(),
+ ExpectedResult = new ConfigurationValidationResult
+ {
+ Succeeded = false,
+ ErrorMessage = LogMessages.IDX22710
+ },
+ TestId = "InvalidTokenEndpointUri"
+ },
+ new WsFederationConfigurationTheoryData
+ {
+ Configuration = ((Func)(() =>{
+ var config = ReferenceMetadata.AdfsV4Endpoint;
+ config.SigningKeys.Clear();
+ return config;
+ }))(),
+ ExpectedResult = new ConfigurationValidationResult
+ {
+ Succeeded = false,
+ ErrorMessage = LogMessages.IDX22711
+ },
+ TestId = "NoSigningKeys"
+ },
+ new WsFederationConfigurationTheoryData
+ {
+ Metadata = ReferenceMetadata.AdfsV4Metadata.Replace(@"https://fs.msidlab11.com/adfs/ls/",
+ @"https://fs.malicious.com/adfs/ls/"),
+ ExpectedResult = new ConfigurationValidationResult
+ {
+ Succeeded = false,
+ ErrorMessage = LogMessages.IDX22713
+ },
+ TestId = "TamperedMetadata-TokenEndpoints-PassiveRequestor"
+ },
+ new WsFederationConfigurationTheoryData
+ {
+ Metadata = ReferenceMetadata.AADCommonMetadata.Replace(@"https://login.microsoftonline.com/common/wsfed",
+ @"https://login.malicious.com/common/wsfed"),
+ ExpectedResult = new ConfigurationValidationResult
+ {
+ Succeeded = false,
+ ErrorMessage = LogMessages.IDX22713
+ },
+ TestId = "TamperedMetadata-TokenEndpoints-ActiveRequestor"
+ },
+ new WsFederationConfigurationTheoryData
+ {
+ Metadata = ReferenceMetadata.AADCommonMetadata.Replace(@"",
+ @"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"),
+ ExpectedResult = new ConfigurationValidationResult
+ {
+ Succeeded = false,
+ ErrorMessage = LogMessages.IDX22713
+ },
+ TestId = "TamperedMetadata-ExtraMaliciousKey"
+ }
+ };
+ }
+ }
+
+ public class WsFederationConfigurationTheoryData : TheoryDataBase
+ {
+ public WsFederationConfiguration Configuration { get; set; }
+
+ public string Metadata { get; set; }
+
+ public ConfigurationValidationResult ExpectedResult { get; set; }
+
+ public override string ToString()
+ {
+ return $"TestId: {TestId}, {ExpectedException}";
+ }
+ }
+ }
+}
diff --git a/test/Microsoft.IdentityModel.TestUtils/IdentityComparer.cs b/test/Microsoft.IdentityModel.TestUtils/IdentityComparer.cs
index 6533b83b55..d7c2777a79 100644
--- a/test/Microsoft.IdentityModel.TestUtils/IdentityComparer.cs
+++ b/test/Microsoft.IdentityModel.TestUtils/IdentityComparer.cs
@@ -997,6 +997,17 @@ public static bool AreX509Certificate2Equal(object object1, object object2, Comp
return context.Merge(localContext);
}
+#if !CrossVersionTokenValidation
+ public static bool AreConfigurationValidationResultEqual(ConfigurationValidationResult result1, ConfigurationValidationResult result2, CompareContext context)
+ {
+ var localContext = new CompareContext(context);
+ if (ContinueCheckingEquality(result1, result2, localContext))
+ CompareAllPublicProperties(result1, result2, localContext);
+
+ return context.Merge(localContext);
+ }
+#endif
+
public static string BuildStringDiff(string label, object str1, object str2)
{
return (label ?? "label") + ": '" + GetString(str1) + "', '" + GetString(str2) + "'";
diff --git a/test/Microsoft.IdentityModel.TestUtils/ReferenceMetadata.cs b/test/Microsoft.IdentityModel.TestUtils/ReferenceMetadata.cs
index 0144d89643..ecb68b40dd 100644
--- a/test/Microsoft.IdentityModel.TestUtils/ReferenceMetadata.cs
+++ b/test/Microsoft.IdentityModel.TestUtils/ReferenceMetadata.cs
@@ -56,7 +56,6 @@ public static X509Certificate2 X509Certificate3
{
get => new X509Certificate2(Convert.FromBase64String(X509CertificateData3));
}
-
public static X509Certificate2 X509CertificateAdfsV2
{
get => new X509Certificate2(Convert.FromBase64String(X509CertificateDataAdfsV2));
@@ -141,7 +140,8 @@ public static WsFederationConfiguration AADCommonEndpoint
SignatureValue = AADCommonMetadataSignatureValue,
SignedInfo = AADCommonSignedInfo
},
- TokenEndpoint = TokenEndpointForCommon
+ TokenEndpoint = TokenEndpointForCommon,
+ ActiveTokenEndpoint = ActiveTokenEndpointForCommon
};
configuration.KeyInfos.Add(keyInfo1);
@@ -181,7 +181,8 @@ public static WsFederationConfiguration AADCommonFormated
KeyInfo = keyInfo1,
SignatureValue = AADCommonMetadataSignatureValue,
},
- TokenEndpoint = "https://login.microsoftonline.com/268da1a1-9db4-48b9-b1fe-683250ba90cc/wsfed"
+ TokenEndpoint = "https://login.microsoftonline.com/268da1a1-9db4-48b9-b1fe-683250ba90cc/wsfed",
+ ActiveTokenEndpoint = "https://login.microsoftonline.com/268da1a1-9db4-48b9-b1fe-683250ba90cc/wsfed"
};
configuration.KeyInfos.Add(keyInfo1);
@@ -227,6 +228,16 @@ public static WsFederationConfiguration AADCommonFormatedNoTokenEndpoint
}
}
+ public static WsFederationConfiguration AADCommonFormatedNoActiveTokenEndpoint
+ {
+ get
+ {
+ var configuration = AADCommonFormated;
+ configuration.ActiveTokenEndpoint = null;
+ return configuration;
+ }
+ }
+
public static WsFederationConfiguration AdfsV2Endpoint
{
get
@@ -243,7 +254,8 @@ public static WsFederationConfiguration AdfsV2Endpoint
KeyInfo = keyInfo,
SignatureValue = AdfsV2SignatureValue,
},
- TokenEndpoint = "https://fs.msidlab7.com/adfs/ls/"
+ TokenEndpoint = "https://fs.msidlab7.com/adfs/ls/",
+ ActiveTokenEndpoint = "https://fs.msidlab7.com/adfs/services/trust/2005/certificatemixed"
};
configuration.KeyInfos.Add(keyInfo);
@@ -271,7 +283,8 @@ public static WsFederationConfiguration AdfsV3Endpoint
Prefix = "ds",
SignatureValue = AdfsV3SignatureValue,
},
- TokenEndpoint = "https://fs.msidlab2.com/adfs/ls/"
+ TokenEndpoint = "https://fs.msidlab2.com/adfs/ls/",
+ ActiveTokenEndpoint = "https://fs.msidlab2.com/adfs/services/trust/2005/certificatemixed"
};
configuration.KeyInfos.Add(keyInfo);
@@ -299,7 +312,8 @@ public static WsFederationConfiguration AdfsV4Endpoint
Prefix = "ds",
SignatureValue = AdfsV4SignatureValue,
},
- TokenEndpoint = "https://fs.msidlab11.com/adfs/ls/"
+ TokenEndpoint = "https://fs.msidlab11.com/adfs/ls/",
+ ActiveTokenEndpoint = "https://fs.msidlab11.com/adfs/services/trust/2005/certificatemixed"
};
configuration.KeyInfos.Add(keyInfo);
@@ -438,6 +452,8 @@ public static SignedInfo AdfsV4SignedInfo
public static string TokenEndpointForCommon { get => @"https://login.microsoftonline.com/common/wsfed"; }
+ public static string ActiveTokenEndpointForCommon { get => @"https://login.microsoftonline.com/common/wsfed"; }
+
public static string KeyDescriptorNoKeyUse
{
get
@@ -731,7 +747,20 @@ public static string MetadataEmptyPassiveRequestorEndpoint
}
}
- public static string MetadataEmptyEndpointReference
+ public static string MetadataEmptySecurityTokenServiceEndpoint
+ {
+ get
+ {
+ return
+ @"
+
+
+
+ ";
+ }
+ }
+
+ public static string MetadataEmptyPassiveRequestorEndpointReference
{
get
{
@@ -746,7 +775,22 @@ public static string MetadataEmptyEndpointReference
}
}
- public static string MetadataEmptyEndpointAddress
+ public static string MetadataEmptySecurityTokenServiceEndpointReference
+ {
+ get
+ {
+ return
+ @"
+
+
+
+
+
+ ";
+ }
+ }
+
+ public static string MetadataEmptyPassiveRequestorEndpointAddress
{
get
{
@@ -763,6 +807,23 @@ public static string MetadataEmptyEndpointAddress
}
}
+ public static string MetadataEmptySecurityTokenServiceEndpointAddress
+ {
+ get
+ {
+ return
+ @"
+
+
+
+
+
+
+
+ ";
+ }
+ }
+
public static string MetadataMalformedCertificate
{
get
@@ -1043,7 +1104,7 @@ public static string MetadataNoSignedInfoInSignature
}
}
- public static string MetadataNoTokenUri
+ public static string MetadataNoPassiveRequestorEndpointUri
{
get
{
@@ -1086,15 +1147,58 @@ public static string MetadataNoTokenUri
}
}
- public static string MetadataWithBlanks
+ public static string MetadataNoSecurityTokenServiceEndpointUri
{
get
{
return
@"
-
+
+
+
+
+
+
+
+
+ i6nrvd1p0+HbCCrFBN5z3jrCe/56R3DlWYQanX6cygM=
+
+
+
+ gdmviHtNhy8FQ6gSbyovhzMBxioMs6hoHYYzoyjS4DxHqhLgaPrRe948NKfXRYe4o1syVp+cZaGTcRzlPmCFOxH1zjY9qPUT2tCsJ1aCUCoiepu0uYGkWKV9CifHt7+aixQEufxM06iwZcMdfXPF3lqqdOoC7pRTcPlBJo6m6odXmjIcHPpsBGtkJuS7W6JULFhzBC9ytS0asrVaEZhVijP95QM0SZRL/pnJp1gOtKYKsQV246lV8tHFfFIddtklVYTvhlagjVUHsUtUhfwrt/5i/Rnr40qMNx/H10ZClTAQXthQH3GnzObAmhfoMNS1hAMpnX4BEhBOAqHHv2jyPA==
+
+
+
+
+ MIIDBTCCAe2gAwIBAgIQY4RNIR0dX6dBZggnkhCRoDANBgkqhkiG9w0BAQsFADAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MB4XDTE3MDIxMzAwMDAwMFoXDTE5MDIxNDAwMDAwMFowLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMBEizU1OJms31S/ry7iav/IICYVtQ2MRPhHhYknHImtU03sgVk1Xxub4GD7R15i9UWIGbzYSGKaUtGU9lP55wrfLpDjQjEgaXi4fE6mcZBwa9qc22is23B6R67KMcVyxyDWei+IP3sKmCcMX7Ibsg+ubZUpvKGxXZ27YgqFTPqCT2znD7K81YKfy+SVg3uW6epW114yZzClTQlarptYuE2mujxjZtx7ZUlwc9AhVi8CeiLwGO1wzTmpd/uctpner6oc335rvdJikNmc1cFKCK+2irew1bgUJHuN+LJA0y5iVXKvojiKZ2Ii7QKXn19Ssg1FoJ3x2NWA06wc0CnruLsCAwEAAaMhMB8wHQYDVR0OBBYEFDAr/HCMaGqmcDJa5oualVdWAEBEMA0GCSqGSIb3DQEBCwUAA4IBAQAiUke5mA86R/X4visjceUlv5jVzCn/SIq6Gm9/wCqtSxYvifRXxwNpQTOyvHhrY/IJLRUp2g9/fDELYd65t9Dp+N8SznhfB6/Cl7P7FRo99rIlj/q7JXa8UB/vLJPDlr+NREvAkMwUs1sDhL3kSuNBoxrbLC5Jo4es+juQLXd9HcRraE4U3UZVhUS2xqjFOfaGsCbJEqqkjihssruofaxdKT1CPzPMANfREFJznNzkpJt4H0aMDgVzq69NxZ7t1JiIuc43xRjeiixQMRGMi1mAB75fTyfFJ/rWQ5J/9kh0HMZVtHsqICBF1tHMTMIK5rwoweY0cuCIpN7A/zMOQtoD
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ";
+ }
+ }
+
+ public static string MetadataWithBlanks
+ {
+ get
+ {
+ return
+ @"
+
+
+
@@ -1108,17 +1212,17 @@ public static string MetadataWithBlanks
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
-
+
- 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
+ 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
-
+
@@ -1128,34 +1232,39 @@ public static string MetadataWithBlanks
-
+
MIIDBTCCAe2gAwIBAgIQXxLnqm1cOoVGe62j7W7wZzANBgkqhkiG9w0BAQsFADAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MB4XDTE3MDMyNjAwMDAwMFoXDTE5MDMyNzAwMDAwMFowLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKJGarCm4IF0/Gz5Xx/zyZwD2rdJJZtO2Ukk1Oz+Br1sLVY8I5vj5esB+lotmLEblA9N/w188vmTvykaEzUl49NA4s86x44SW6WtdQbGJ0IjpQJUalUMyy91vIBkK/7K3nBXeVBsRk7tm528leoQ05/aZ+1ycJBIU+1oGYThv8MOjyHAlXJmCaGXwXTisZ+hHjcwlMk/+ZEutHflKLIpPUNEi7j4Xw+zp9UKo5pzWIr/iJ4HjvCkFofW90AMF2xp8dMhpbVcfJGS/Ii3J66LuNLCH/HtSZ42FO+tnRL/nNzzFWUhGT92Q5VFVngfWJ3PAg1zz8I1wowLD2fiB2udGXcCAwEAAaMhMB8wHQYDVR0OBBYEFFXPbFXjmMR3BluF+2MeSXd1NQ3rMA0GCSqGSIb3DQEBCwUAA4IBAQAsd3wGVilJxDtbY1K2oAsWLdNJgmCaYdrtdlAsjGlarSQSzBH0Ybf78fcPX//DYaLXlvaEGKVKp0jPq+RnJ17oP/RJpJTwVXPGRIlZopLIgnKpWlS/PS0uKAdNvLmz1zbGSILdcF+Qf41OozD4QNsS1c9YbDO4vpC9v8x3PVjfJvJwPonzNoOsLXA+8IONSXwCApsnmrwepKu8sifsFYSwgrwxRPGTEAjkdzRJ0yMqiY/VoJ7lqJ/FBJqqAjGPGq/yI9rVoG+mbO1amrIDWHHTKgfbKk0bXGtVUbsayyHR5jSgadmkLBh5AaN/HcgDK/jINrnpiQ+/2ewH/8qLaQ3B
-
-
+
+
-
+
-
+
-
- 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
+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
+
+
+
+ https://login.microsoftonline.com/268da1a1-9db4-48b9-b1fe-683250ba90cc/wsfed
+
+
+
https://login.microsoftonline.com/268da1a1-9db4-48b9-b1fe-683250ba90cc/wsfed
-
-
-
+
+
+