Saml2 question with custom IDP

Product: PowerShell Universal
Version: 4.5.0

We are currently testing the SAML2 integration with our internal IDP. The setup we are using is as follows,

Set-PSUAuthenticationMethod -Type "Saml2" -CallbackPath "https://universalinstance:9443" -EntityId "https://universalinstance:9443" -IdentityProviderEntityId "{MetadataIdpURL}" -SigningKey "cert.crt" -SingleSignOnServiceUrl "{SingleSignOnURL}"

Upon running the tests we found that our IDP returned us a 400 Bad request error in the response. When collecting traces of the SAML request we find that the idp us the AuthRequest apparently is correct,

<saml2p:AuthnRequest xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol"
 xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion"
 ID="id2b5ce5112ebe47ea89b1d3f7a5550f2d"
 Version="2.0"
 IssueInstant="2025-01-13T14:01:40Z"
 Destination="{SingleSignOnURL}"
 AssertionConsumerServiceURL="https://universalinstance:9443/Saml2/Acs"
 >
 <collection2:Issues>https://universalinstance:9443</collection2:Issues>
</saml2p:AuthnRequest>

In the request NameIDFormat attribute not appears, is it necessary to add it manually or something?
Checking the documentation we see that the Set-PSUAuthenticationMethod cmdlet provides for the “-configure” parameter but following the documentation that is referenced for “SP.OPTIONS” we see no way to include it.
Our metadata includes the following NameIDFormat,

urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified
urn:oasis:names:tc:SAML:2.0:nameid-format:persistent
urn:oasis:names:tc:SAML:2.0:nameid-format:transient

For the .net release Saml2/Sustainsys.Saml2/Configuration/RequestedAttributeElement.cs that seems to use PSU we observe

...
 const string nameFormat = "nameFormat";

 /// <summary>
 /// Format of the Name property, one of the standard Uris in the saml specification.
 /// </summary>
 [ConfigurationProperty(nameFormat, DefaultValue="urn:oasis:names:tc:SAML:2.0:attname-format:unspecified")]
 public Uri NameFormat
 {
 get
 {
 return ( Uri ) base [ nameFormat ] ;
 } }
 } }

...

Is there any way to change this behavior? or maybe something is escaping us?

Thanks for your help!

You can try this workaround:

-Configure {
    $args[0].SPOptions.NameIdPolicy = [Sustainsys.Saml2.Saml2P.Saml2NameIdPolicy]::new($null, "Persistent")
}

We could also add an option to the SAML config to allow you to just specify this as a parameter to the auth configuration.

Hello @adam, thanks for quickly response. I have tried the suggested workaround and, unfortunately, when accessing PSU it throws an error 500. In the logs we observe the following,

2025-01-14 06:20:02.362 +00:00 [ERR] Connection id "0HN9KCDKF4T2M", Request id "0HN9KCDKF4T2M:00000067": An unhandled exception was thrown by the application.
System.InvalidOperationException: No authenticationScheme was specified, and there was no DefaultChallengeScheme found. The default schemes can be set using either AddAuthentication(string defaultScheme) or AddAuthentication(Action<AuthenticationOptions> configureOptions).
   at Microsoft.AspNetCore.Authentication.AuthenticationService.ChallengeAsync(HttpContext context, String scheme, AuthenticationProperties properties)
   at Universal.Server.UniversalDashboardStartup.<>c.<<Configure>b__5_0>d.MoveNext() in C:\actions-runner\_work\universal\universal\src\Universal.Server\UniversalDashboardStartup.cs:line 97
--- End of stack trace from previous location ---
   at Microsoft.AspNetCore.Diagnostics.StatusCodePagesMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.ResponseCompression.ResponseCompressionMiddleware.InvokeCore(HttpContext context)
   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ProcessRequests[TContext](IHttpApplication`1 application)
2025-01-14 06:20:02.363 +00:00 [INF] Request finished HTTP/2 GET https://xxxxxxx:9443/ - - - 500 0 - 5.7729ms
2025-01-14 06:20:02.898 +00:00 [INF] Request starting HTTP/2 GET https://xxxxxxx:9443/ - -
2025-01-14 06:20:02.902 +00:00 [ERR] Connection id "0HN9KCDKF4T2M", Request id "0HN9KCDKF4T2M:00000069": An unhandled exception was thrown by the application.
System.InvalidOperationException: No authenticationScheme was specified, and there was no DefaultChallengeScheme found. The default schemes can be set using either AddAuthentication(string defaultScheme) or AddAuthentication(Action<AuthenticationOptions> configureOptions).
   at Microsoft.AspNetCore.Authentication.AuthenticationService.ChallengeAsync(HttpContext context, String scheme, AuthenticationProperties properties)
   at Universal.Server.UniversalDashboardStartup.<>c.<<Configure>b__5_0>d.MoveNext() in C:\actions-runner\_work\universal\universal\src\Universal.Server\UniversalDashboardStartup.cs:line 97
--- End of stack trace from previous location ---
   at Microsoft.AspNetCore.Diagnostics.StatusCodePagesMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.ResponseCompression.ResponseCompressionMiddleware.InvokeCore(HttpContext context)
   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ProcessRequests[TContext](IHttpApplication`1 application)

Perhaps access to those “hidden” properties is not possible?

Thanks!

I’m not exactly sure. It should work but it seems like it may be throwing an exception while attempting to configure it. I’ve opened a feature request to just add this as an option.

Thanks @adam