The SAML message InResponseTo X doesn't match the expected InResponseTo Y

First things first, I’m aware of this post: I’m not willing to enable the DisableInResponseToCheck setting, which as you said, is not recommended.

I have the following environment:

  1. I’m using version 2.8.8 of ComponentSpace.SAML2.dll
  2. I have modified the ExampleServiceProvider site to use my Okta server and enabled SameSite=None for the ASP.Net session cookie
The scenario is as follow:
  1. Open the sample website in a tab, click the “SSO to the Identity Provider” link => Okta login page displays
  2. Open the sample website in a second tab, click the “SSO to the Identity Provider” link => Okta login page displays
  3. Go back to the first tab, and login with your Okta credentials
You receive the following error:

Unfortunately this appears to be a common scenario for our clients, looking at our logs files. How can I make this work without error?

FYI, we are not using the default HttpSSOSessionStore in prod, but a custom session store (we don’t even use sessions). I was just validating that the problem could be reproduced with the built in store in ComponentSpace.

I’m struggling to understand how the session ID should be created and persisted in a working implementation (with webfarms). When using HttpSSOSessionStore with your implementation and SameSite=Lax on the session cookie (the default), the cookie is not sent back by the browser when returning from the IdP. A new session ID is therefore generated… so I’m wondering what’s the point of this ID? Shall we just return a random number or a GUID? What are the consequences of having a fresh session ID generated each time? Any security issue?

More generally, do you have a doc on how to properly implement a custom session store?

We maintain SAML state in support of the SAML protocol. For example, as part of SP-initiated SSO the SAML response returned by the IdP has an InResponseTo field which should match the ID field of the SAML authn request sent by the SP. We save this ID in the SAML state so we can perform this check when the SAML response is received.

In v2.8.8 the SAML state is saved in the ASP.NET session. In a later release we removed the reliance on the ASP.NET session and introduced a custom SAML_SessionId cookie which, by default, is backed by an in-memory SAML session store.

The scenario you describe is problematic and we don’t recommend users do this if at all possible. The underlying problem is that the same SAML_SessionId is used for both browser tabs. Therefore, the same SAML state applies to both tabs. The sequence is as follows:

1. In tab #1, a SAML authn request with ID 1 is sent to Okta. We save ID 1 in the SAML state.
2. In tab #2, a SAML authn request with ID 2 is sent to Okta. We save ID 2, replacing ID 1, in the SAML state.
3. In tab #1, a SAML response in response to SAML authn request 1 is received.
4. An exception is thrown as we’re expecting a SAML response in response to SAML authn request 2.

The only way we could handle this is to save multiple IDs in the SAML state. However, this adds complexity for a scenario which we typically don’t see. If this is a significant issue, please contact to discuss possible options.

If you’re using a web farm, the default in-memory SAML state session store may be used if you employ sticky sessions. If that’s not the case, you need to implement a session store in a central repository such as a database. Please refer to the WebFarm Guide and the Developer Guide for more information.

OK. And what about implementing a custom session store that would return a new Session ID, valid only for the lifetime of the page? I’m confident this would eliminate this particular error, but what would be the drawbacks? Would it be equivalent to using the ‘DisableInResponseToCheck’ option? Worse?

You’d also have to implement the cookie used as the key into the custom session store (ie the session ID). If the session ID was valid only for the lifetime of the page you would also have to set DisableInResponseToCheck. The purpose of the cookie and session store is to remember session state across HTTP requests. If you changed the session ID on each request we would lose that ability and you’d have to disable the InResponseTo check and perhaps other checks as well.