diff --git a/WebApps/Proxy/Microsoft.SPID.Proxy/Models/Extensions/RequestSAMLAsXMLExtensions.cs b/WebApps/Proxy/Microsoft.SPID.Proxy/Models/Extensions/RequestSAMLAsXMLExtensions.cs index cec1cba..1fc2ff5 100644 --- a/WebApps/Proxy/Microsoft.SPID.Proxy/Models/Extensions/RequestSAMLAsXMLExtensions.cs +++ b/WebApps/Proxy/Microsoft.SPID.Proxy/Models/Extensions/RequestSAMLAsXMLExtensions.cs @@ -207,6 +207,54 @@ public static XmlDocument AddExtensionsAndPurposeIfNotPresent(this XmlDocument s return samlRequest; } + public static string GetSpidLFromExtensions(this XmlDocument samlRequest, + string extensionsElementName, + string spidLElementName) + { + if (samlRequest == null || samlRequest.DocumentElement == null) + { + return null; + } + + // If the element name is prefixed, search with prefix; otherwise search only by local name + XmlNodeList extensionElements; + if (extensionsElementName.Contains(":")) + { + extensionElements = samlRequest.GetElementsByTagName(extensionsElementName); + } + else + { + extensionElements = samlRequest.GetElementsByTagName(extensionsElementName, "*"); + } + + if (extensionElements == null || extensionElements.Count == 0) + { + return null; + } + + // Look for spidL element in the first Extensions element + XmlElement extensionsElement = extensionElements[0] as XmlElement; + if (extensionsElement != null) + { + XmlNodeList spidLElements; + if (spidLElementName.Contains(":")) + { + spidLElements = extensionsElement.GetElementsByTagName(spidLElementName); + } + else + { + spidLElements = extensionsElement.GetElementsByTagName(spidLElementName, "*"); + } + + if (spidLElements != null && spidLElements.Count > 0) + { + return spidLElements[0].InnerText?.Trim(); + } + } + + return null; + } + public static string GetSpidACSFromExtensions(this XmlDocument samlRequest, string extensionsElementName, string spidACSElementName) diff --git a/WebApps/Proxy/Microsoft.SPID.Proxy/Models/Options/SPIDOptions.cs b/WebApps/Proxy/Microsoft.SPID.Proxy/Models/Options/SPIDOptions.cs index 97f4168..d52d3e8 100644 --- a/WebApps/Proxy/Microsoft.SPID.Proxy/Models/Options/SPIDOptions.cs +++ b/WebApps/Proxy/Microsoft.SPID.Proxy/Models/Options/SPIDOptions.cs @@ -21,4 +21,5 @@ public class SPIDOptions public string ExtensionsElementName { get; set; } = "samlp:Extensions"; public string PurposeElementName { get; set; } = "spid:Purpose"; + public string SpidLElementName { get; set; } = "ext:spidL"; } diff --git a/WebApps/Proxy/Microsoft.SPID.Proxy/Services/ISPIDService.cs b/WebApps/Proxy/Microsoft.SPID.Proxy/Services/ISPIDService.cs index 351a41c..8c98240 100644 --- a/WebApps/Proxy/Microsoft.SPID.Proxy/Services/ISPIDService.cs +++ b/WebApps/Proxy/Microsoft.SPID.Proxy/Services/ISPIDService.cs @@ -13,7 +13,7 @@ public interface ISPIDService int GetSPIDAttributeConsumigServiceValue(NameValueCollection refererQueryString, NameValueCollection relayQueryString, NameValueCollection wctxQueryString, XmlDocument requestAsXml); int GetCIEAttributeConsumigServiceValue(NameValueCollection refererQueryString, NameValueCollection relayQueryString, NameValueCollection wctxQueryString); - int GetSPIDLValue(NameValueCollection refererQueryString, NameValueCollection relayQueryString, NameValueCollection wctxQueryString, bool isCie); + int GetSPIDLValue(NameValueCollection refererQueryString, NameValueCollection relayQueryString, NameValueCollection wctxQueryString, bool isCie, XmlDocument requestAsXml = null); string GetComparisonValue(NameValueCollection refererQueryString, NameValueCollection relayQueryString, NameValueCollection wctxQueryString, bool isCie); string GetPurposeValue(NameValueCollection refererQueryString, NameValueCollection relayQueryString, NameValueCollection wctxQueryString); bool IsSpidLValid(NameValueCollection queryStringCollection, string origin = ""); diff --git a/WebApps/Proxy/Microsoft.SPID.Proxy/Services/Implementations/FederatorRequestService.cs b/WebApps/Proxy/Microsoft.SPID.Proxy/Services/Implementations/FederatorRequestService.cs index f47dcf1..73fda2b 100644 --- a/WebApps/Proxy/Microsoft.SPID.Proxy/Services/Implementations/FederatorRequestService.cs +++ b/WebApps/Proxy/Microsoft.SPID.Proxy/Services/Implementations/FederatorRequestService.cs @@ -147,7 +147,7 @@ private XmlDocument SetOutcomingSAMLXmlSignIn(FederatorRequest federatorRequest, string entityId = GetNewEntityId(federatorRequest); requestAsXml.ChangeIssuer(entityId); - var spidL = _spidService.GetSPIDLValue(refererQueryString, relayQueryString, wctxQueryString, federatorRequest.IsCIE()); + var spidL = _spidService.GetSPIDLValue(refererQueryString, relayQueryString, wctxQueryString, federatorRequest.IsCIE(), requestAsXml); var comparison = _spidService.GetComparisonValue(refererQueryString, relayQueryString, wctxQueryString, federatorRequest.IsCIE()); //If no RequestedAuthnContext is already present, add it diff --git a/WebApps/Proxy/Microsoft.SPID.Proxy/Services/Implementations/SPIDService.cs b/WebApps/Proxy/Microsoft.SPID.Proxy/Services/Implementations/SPIDService.cs index 88eb8da..fa4a5c0 100644 --- a/WebApps/Proxy/Microsoft.SPID.Proxy/Services/Implementations/SPIDService.cs +++ b/WebApps/Proxy/Microsoft.SPID.Proxy/Services/Implementations/SPIDService.cs @@ -267,10 +267,33 @@ public int GetCIEAttributeConsumigServiceValue(NameValueCollection refererQueryS } - public int GetSPIDLValue(NameValueCollection refererQueryString, NameValueCollection relayQueryString, NameValueCollection wctxQueryString, bool isCie) + public int GetSPIDLValue(NameValueCollection refererQueryString, NameValueCollection relayQueryString, NameValueCollection wctxQueryString, bool isCie, XmlDocument requestAsXml = null) { var spidL = isCie ? _cieOptions.DefaultSPIDL : _spidOptions.DefaultSPIDL; + // First, try to get spidL from SAMLRequest Extensions (highest priority) + if (requestAsXml != null) + { + try + { + var spidLFromExtensions = requestAsXml.GetSpidLFromExtensions( + _spidOptions.ExtensionsElementName, + _spidOptions.SpidLElementName); + + if (!string.IsNullOrWhiteSpace(spidLFromExtensions) + && int.TryParse(spidLFromExtensions, out int parsedValue) + && _spidOptions.ValidSPIDL.Contains(parsedValue)) + { + _logger.LogDebug("Using spidL from SAMLRequest Extensions: {spidLValue}", parsedValue); + return parsedValue; + } + } + catch (Exception ex) + { + _logger.LogWarning(ex, "Error extracting spidL from SAMLRequest Extensions, falling back to other methods"); + } + } + if(_spidOptions.DisableSpidLevelFromReferer) { return spidL; diff --git a/WebApps/Proxy/Microsoft.SPID.Proxy/appsettings.json b/WebApps/Proxy/Microsoft.SPID.Proxy/appsettings.json index 78c898b..e605c8e 100644 --- a/WebApps/Proxy/Microsoft.SPID.Proxy/appsettings.json +++ b/WebApps/Proxy/Microsoft.SPID.Proxy/appsettings.json @@ -64,7 +64,8 @@ "AssertionIssueInstantToleranceMins": 15, "DisableSpidLevelFromReferer": false, "ExtensionsElementName": "samlp:Extensions", - "PurposeElementName": "spid:Purpose" + "PurposeElementName": "spid:Purpose", + "SpidLElementName": "ext:spidL" }, "cie": { "DefaultSPIDL": 3,