SAML Login Cancelled Request

I have noticed when we have customers using SAML on a connection with high latency (satellite internet while on a VPN for example), Chrome (and other browsers) cancel the request. According to the Chrome documentation, this occurs when the browser determines that it no longer needs to serve up the content requested. This can happen when a redirect happens while it was waiting for a response. I am wondering if there is some kind of timeout within the SAML library causing a redirect to happen before it receives a response. We are using HTTP Post binding.

This issue can be replicated by setting Chrome’s performance internet speed settings to 150 kb/s down, 150 kb/s up, and 500 ms latency. The end result is an infinite redirect loop because the request is cancelled (you can see the cancelled requests in Chrome on the network tab of developer tools).

I have tested SAML on several other sites using the same slow speed and high latency and have not been able to reproduce the issue on those other sites (which are not using this library for SAML), so I am thinking it may be something specific to this library, though I am not 100% sure of that at this point. Do you have any suggestions or work-arounds?

Thanks in advance!

We don’t perform any timeouts of the HTTP requests or responses.
I tried reproducing the issue with the throttling settings in Chrome you used but the SSO and SLO completed successfully.
I also tried 50 kb/s up and down and a 2000 ms latency without any issues.
I tested with the ExampleIdentityProvider and ExampleServiceProvider projects.
Do you see the issue using our example projects?

[quote]
ComponentSpace - 3/11/2019
We don't perform any timeouts of the HTTP requests or responses.
I tried reproducing the issue with the throttling settings in Chrome you used but the SSO and SLO completed successfully.
I also tried 50 kb/s up and down and a 2000 ms latency without any issues.
I tested with the ExampleIdentityProvider and ExampleServiceProvider projects.
Do you see the issue using our example projects?
[/quote]

Thanks for the quick reply. I am using the MvcExampleIdentityProvider. I've attached a screenshot of the redirect process.
SAMLLogin.aspx calls InitiateSSO:

public static InitiateSSOResult InitiateSSO(ISSOConfiguration config, HttpResponseBase response) {
// Create the authentication request.
var result = new InitiateSSOResult();

try {
XmlElement authnRequestXml = CreateAuthnRequest(config.SSOUrl, Configuration.GetSSOServiceProviderName(config));

if (config.SignAuthnRequest) {
var key = GetLocalKey(config);
SAMLMessageSignature.Generate(authnRequestXml, key.PrivateKey, key, null, SAMLIdentifiers.DigestMethods.SHA256, SAMLIdentifiers.SignatureMethods.RSA_SHA256);
}

ServiceProvider.SendAuthnRequestByHTTPPost(response, config.SSOUrl, authnRequestXml, "");
}
catch (Exception e) {
result.Success = false;
result.ErrorMessage = e.GetMessageAndInnerMessage();
result.LogJsonObjects.Add("InitiateSSOError", e.GetMessageAndInnerMessage());
result.LogJsonObjects.Add("InitiateSSOException", e);
return result;
}
return new InitiateSSOResult() { Success = true};
}

You can see this hits the SSOService (which is in the MVCExampleIdentityProvider which then redirects to the login page, but rather than load the page, allowing the user to log in, the request is canceled and it sends them back to the initial page (SamlLogin.aspx) and the loop continues.

When I speed up my browser, it does the same exact process except the request is not being canceled and it presents the user with the login page.

Two potential issues I've seen:

1) We are using version 2.6.0.16 of the ComponentSpace.SAML2 dll, so I wanted to make sure that there isn't a timeout or something within that version.
2) I was wondering about the RelayState. Should I be setting that? If so, what should I set it to?


I believe I have found the issue. I needed to call response.End(); right after the SendAuthnRequestByHTTPPost.

Thanks for the update.
If the controller includes code to render HTML or otherwise update the HTTP response, you need to terminate this processing.
Calling Response.End() will do this.
If you don’t, the SAML message won’t be sent.

Regarding the other two points:
1. We don’t timeout HTTP traffic in any version of the product.
2. Relay state is optional and shouldn’t have any impact.

ServiceProvider.SendAuthnRequestByHTTPPost is part of our low-level API.
Although supported, we recommend using our high-level API as it’s much easier to use and is driven by SAML configuration (eg saml.config file).
The following code is from our MvcExampleServiceProvider and sends a SAML authn request to the IdP.


public ActionResult InitiateSingleSignOn(string returnUrl = null)
{
// To login automatically at the service provider, initiate single sign-on to the identity provider (SP-initiated SSO).
// The return URL is remembered as SAML relay state.
var partnerName = WebConfigurationManager.AppSettings[“PartnerName”];

SAMLServiceProvider.InitiateSSO(Response, partnerName, returnUrl);

return new EmptyResult();
}



Thanks for the input. That makes sense. It turns out that the SAML post is sent and it works fine on moderately fast internet. The issue comes when there is high latency on the network connection in which case, the post does post, but before the response comes back from the identity provider, the page will serve up a new HTTP response causing the browser to cancel the receiving of the response from the identity provider.

For the high level example that you sent, does that use HTTP post binding, or how would I go about specifying that if it doesn’t?

You specify which binding to use through SAML configuration (eg a saml.config file).
The SAMLServiceProvider.InitiateSSO call will use HTTP-Redirect by default unless HTTP-Post is specified in the configuration.
Here’s an example section of a saml.config specifying that HTTP-Post should be used.


<PartnerIdentityProvider
Name=“<a href=“https://ExampleIdentityProvider””>https://ExampleIdentityProvider"
Description=“Example Identity Provider”
SignAuthnRequest=“true”
SingleSignOnServiceUrl=“<a href=“https://localhost:44390/SAML/SSOService.aspx””>https://localhost:44390/SAML/SSOService.aspx"
SingleSignOnServiceBinding=“urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST”
SingleLogoutServiceUrl=“<a href=“https://localhost:44390/SAML/SLOService.aspx””>https://localhost:44390/SAML/SLOService.aspx"
PartnerCertificateFile=“Certificates\idp.cer”/>



Is there a reason you want to use HTTP-Post to send the SAML authn request?

[quote]
ComponentSpace - 3/13/2019
You specify which binding to use through SAML configuration (eg a saml.config file).
The SAMLServiceProvider.InitiateSSO call will use HTTP-Redirect by default unless HTTP-Post is specified in the configuration.
Here's an example section of a saml.config specifying that HTTP-Post should be used.


<PartnerIdentityProvider
Name="https://ExampleIdentityProvider"
Description="Example Identity Provider"
SignAuthnRequest="true"
SingleSignOnServiceUrl="https://localhost:44390/SAML/SSOService.aspx"
SingleSignOnServiceBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
SingleLogoutServiceUrl="https://localhost:44390/SAML/SLOService.aspx"
PartnerCertificateFile="Certificates\idp.cer"/>



Is there a reason you want to use HTTP-Post to send the SAML authn request?
[/quote]

In our case, we have multiple clients authenticating with SAML against different servers, so we need the settings to all be configurable through the database for each client rather than a single saml.config file.

That’s supported too.
SAML configuration may be specified using one of the following approaches:

(1) using a saml.config file in the application’s directory that’s loaded automatically
(2) programmatically by calling the SAML configuration API
(3) programmatically by implementing the ISAMLConfigurationResolver interface

The saml.config file is the simplest approach and requires no additional coding.

If SAML configuration information is stored in a database, it must be set programmatically.

If the SAML configuration changes infrequently, it may be set using the SAML configuration API,
typically at application start-up.

If the SAML configuration changes frequently, it’s better to implement the ISAMLConfigurationResolver interface
for the on-demand retrieval of SAML configuration information.

These APIs are demonstrated in the Global.asax of the ExampleServiceProvider project.

[quote]
ComponentSpace - 3/13/2019
That's supported too.
SAML configuration may be specified using one of the following approaches:

(1) using a saml.config file in the application's directory that's loaded automatically
(2) programmatically by calling the SAML configuration API
(3) programmatically by implementing the ISAMLConfigurationResolver interface

The saml.config file is the simplest approach and requires no additional coding.

If SAML configuration information is stored in a database, it must be set programmatically.

If the SAML configuration changes infrequently, it may be set using the SAML configuration API,
typically at application start-up.

If the SAML configuration changes frequently, it's better to implement the ISAMLConfigurationResolver interface
for the on-demand retrieval of SAML configuration information.

These APIs are demonstrated in the Global.asax of the ExampleServiceProvider project.

[/quote]

Thank you! It sounds like the ISAMLConfigurationResolver would be the right approach for our use-case because they could change it at any time without the application being restarted.

You’re welcome.