SAMLServiceProvider.InitiateSSO

Greetings,

I am trying to upgrade some code to net core (from full framework) using the latest version of SAML 2.0 for asp.net core. I’ve hit a blocker when trying to convert this block of code:

SAMLHttpResponse samlHttpResponse = new SAMLHttpResponse();
SAMLServiceProvider.InitiateSSO(samlHttpResponse, null, partnerConfig.Name);
response.SamlUrl = samlHttpResponse.URL; // We return this to our SPA client


In the newer version of the lib the InitiateSSO() signature has changed and no longer takes a SAMLHttpResponse. I can’t find a way to do the same thing in the net core version and appreciate some guidance. Do I use the events like OnSamlResponseReceived? I think I saw something that said that doesn’t fire unless you’re using ReceiveSSO()?

I’ll be the first to admit I’m not an expert with SAML. I’ve tried to search the forums and read the developer guide and I can’t find anything on point. I also can’t locate an upgrade guide.

Thanks for any help!

We use dependency injection to pickup the HTTP response object rather than passing it as a parameter.

May I ask why you want to return the URL to the SPA? Why not let the ASP.NET Core app send the SAML authn request to the IdP?

We include an Angular SPA and ExampleWebApi ASP.NET Core app that demonstrate one approach for supporting SAML SSO. The ASP.NET Core app handles the SAML SSO and returns a JWT to the Angular app which is used for making authorized API calls.

Perhaps this approach might be worth considering.

If for some reason it makes more sense for you to return the URL to the SPA, let me know and I can provide some details on how this is supported.

Thank you for the reply.

To answer your question this app has what may be a very customized workflow. In this case the user is in the app and gets sent to the IDP’s login page. I’d rather not change that at this time if I can avoid it. Appreciate you sharing some details about how I can support this moving forward.

Thanks!

You will need to implement the IHttpResponse interface under the ComponentSpace.Saml2.Bindings namespace. There’s a Redirect method which provides access to the URL. You’ll also have to ensure cookies are handled correctly.

You register your implementation at start-up as follows:

builder.Services.AddTransient<IHttpResponse, CustomHttpResponse>();

If you want the default implementation to handle everything except for the redirect, your custom implementation could extend the AspNetHttpResponse class and override the Redirect method. That would leave the handling of cookies etc to the AspNetHttpResponse base class.

So I reviewed your suggestions and I’m wondering if you have any examples? If I implement IHttpResponse how do I then use this in conjunction with SAMLServiceProvider.InitiateSSO() ?

Thanks again for the guidance!

SAMLServiceProvider.InitiateSSO accesses IHttpResponse via dependency injection and calls IHttpResponse.Redirect to send the SAML authn request via a 302 redirect. The parameter to IHttpResponse.Redirect is the redirect URL. In the default implementation, we simply redirect to this URL. In your implementation you would return this URL to the SPA.

I’m not sure exactly how you return the URL to the SPA. Please elaborate so we can work out the best way to do this.

Hello again,

We return the URL to the SPA in a web api controller. Which is also where the call to SAMLServiceProvider.InitiateSSO() is at.

If I tell dependency injection to use a custom AspNetHttpResponse that overrides Redirect() how do I then surface the URL to the controller?

Also, will injecting the custom IHttpResponse change any other behavior? For instance we also use ReceiveSSO() elsewhere in our application.

Thank you.

Here’s one option for doing this.

Create a custom IHttpResponse implementation. The redirect URL is remembered but no redirect occurs.


public class MyHttpResponse : AspNetHttpResponse
{
public string? Url { get; set; }

public MyHttpResponse(IHttpContextAccessor httpContextAccessor) : base(httpContextAccessor)
{
}

public override void Redirect(string url)
{
Url = url;
}
}



Register this implementation as scoped. It must be scoped so the remembered URL is accessible by the controller within the context of this HTTP request.


builder.Services.AddScoped<IHttpResponse, MyHttpResponse>();



Use dependency injection in your controller to access the IHttpResponse implementation.


public SamlController(IHttpResponse httpResponse)
{
_httpResponse = httpResponse;
}



Call _samlServiceProvider.InitiateSsoAsync as before. Retrieve the URL from the MyHttpResponse.


await _samlServiceProvider.InitiateSsoAsync(partnerName, returnUrl);

var myHttpResponse = _httpResponse as MyHttpResponse;

var url = myHttpResponse.Url;



This will only impact sending SAML messages. The base class is used for receiving SAML messages and so this will behave as before.

My recommendation still is to have the ASP.NET Core backend handle the redirect to the IdP and not have this done by the SPA. This seems like a simpler and more logical approach.

One issue with returning the URL to the SPA is that your backend code and the SPA must handle any HTTP set-cookie headers etc correctly. It’s much easier to let the browser handle this rather than trying to make the SAML SSO flow fit into a web API call.