Session management from 5.11

Overview

When using Safewhere Identify, you sometimes get some sort of error about contextId or other state-related errors. So what is contextId and why is it causing so many problems? In fact, Safewhere Identify is a stateful web application and lying at the root of the problem are typical session state issues. An alternative, a more generic but is also easier to understand, would have been "corrupted, expired, for misconfigured session state".

The complexity of state management

At first, a typical login flow that Identify needs to handle is:

  1. I want to access a service provider.
  2. The service provider sends a request to Identify.
  3. Identify forwards a request to an upstream IdP. Identify needs to store the request's id in session for validation purpose later.
  4. Identify receives a response from the upstream IdP. Identify needs to validate if the response is for the request id that it saves in session.
  5. Identify returns a response to the service provider.
  6. I log out of the service provider.
  7. The service provider sends a logout request to Identify.
  8. Identify sends a logout request to the upstream IdP.
  9. The upstream IdP sends a logout response to Identify.
  10. Identify sends a logout response to the service provider.

In order to support steps (4)-(10), Identify needs to maintain "states" of the login session somewhere. There are two typical storages for maintaining states in Asp.Net: Asp.Net session state and cookies. We ruled out cookies mainly because it would be too easy to end up with problems due to cookies' size limit. Therefore, we chose Asp.Net session state.

But then why do we need contextID? For example, when we store the request's id in a "RequestId" session key, don't we just need to look it up from session state by using the "RequestId" key? Since tab-browsing is a thing now, there can be dozens of other cases as well as edge cases:

  1. I open a tab, access to a service provider and leave the login page open. after that, I open another tab and end up at the same login page. On that very same tabe, I enter my credentials to log in. Finally, I switch to the first tab, enter my credentials there.
  2. Similar to (1) but this time I access two different service providers on two separate tabs.
  3. Similar to (2) but this time I select different identity providers.
  4. For each case, I can even enter different credentials.
  5. Custom components that can render views such as interceptors are also relevant here.

Because tabs don't have IDs, we had to invent the concept of "contextID" for every request made from a service provider to Identify. When a service provider sends an authentication request to Identify, it initiates a new "context" inside the same browser's (Asp.Net) session state. The typical login flow is:

  1. I want to access a service provider.
  2. The service provider sends a request to Identify.
  3. Identify generates a new context object with a contextID (which is no more than a GUID) and put it in session state. Identify forwards a request to an upstream IdP with a relaystate. The request id and relaystate are attached to the context object.
  4. Identify receives a response from the upstream IdP and it needs to validate if the response is for the request id that it saves in session. Identify will use the returned relaystate to look up for the related contextID and context object. The context object contains all information that Identify needs to do validation and log me in.
  5. Identify returns a response to the service provider.
  6. I log out of the service provider.
  7. The service provider sends a logout request with SessionIndex to Identify.
  8. Identify uses the SessionIndex to look up for the related contextID and context object. The context object contains all information that Identify needs to do validation and log me out. Identify sends a logout request to the upstream IdP.
  9. The upstream IdP sends a logout response to Identify.
  10. Identify sends a logout response to the service provider.

Thanks to contextID, Identify can now handle all the logins and logouts done in separate tabs. Please note that although I use SAML 2.0 for the login flow above, all other protocols and identity providers work the same way. It is also worth noting that a login can have even more steps in between.

Common causes of the contextId issues

However, maintaining states is a hard problem because so many edge cases can happen in real life.

Misconfigured web-farm

As a rule of thumb, when deploying Identify on a web-farm setup, you need to configure session state correctly. We recommend one of the following 3 options:

  1. Use in-proc session state with sticky session.
  2. Use out-of-proc Asp.Net state server.
  3. Or use out-of-proc SQL Session state.

Not using stick session for (1) or configuring (2) or (3) wrong is a common cause of session state problem. For instance, if your deployment uses in-proc session state without sticky session, Identify may create the context object on one server but receive response from the Identity Provider on the other server and end up with a session-related error. It's just unfortunate that the error it shows on the UI is about "contextID" instead of about "invalid session state".

Browser doesn't send the Asp.Net's Identify_SessionId cookie around

Asp.Net stores its session id in a cookie named Identify_SessionId. Session problems can happen when your browser don't send the cookie to Identify correctly. This can happen because (of):

  • A browser is used in high-secure mode, for example the default setting of IE on a Windows server.
  • Samesite cookies.

Non-compliant parties (service providers/identity providers)

When a party doesn't follow a specification, for example when a SAML 2.0 IdP doesn't return a relay state to Identify, Identify can fail to look up for the related context.

Custom components

A custom component such as an interceptor or a generic authentication provider, needs to transfer contextID around properly as demonstrated in our sample code.

Session expiry

Session expiry is one of the most common cause and is also the hardest to handle. Many things can go wrong at both login and logout time:

  1. I enter credentials to log in or the IdP returns a response to Identify after a session has expired.
  2. A login is done successfully. The service provider or another service provider is then requests for a new token after Identify's session has expired.
  3. I performs logout after my service provider's session or Identify's session has expired.

In fact, we have tried to handle many such edge cases. If you still sometimes encounter a contextID error, we would like to receive a report with instructions to reproduce the problem.

Troubleshooting

If session state problem consistently happens for you, it is more likely caused by mis-configured server:

  1. If you are using the web-farm (redundant) setup, check if you have set up session state correctly.
  2. Check if your deployment has applied a workaround or a patch for the SameSite issue.
  3. Check if your browser is sending cookies around correctly.
  4. Check if the service providers and identity providers that are connecting to Identify conform to SAML 2.0/WSFed/OpenID connect specifications. More specifically, check if they are sending relaystate/state/wctx/SessionIndex around correctly.

If you have found nothing wrong, you can send us a support request. We would like to have the following information included in the request:

  1. Step by step instructions for how to reproduce your problem.
  2. A browser trace (either a Fiddler trace or a HAR trace).
  3. Identify's logs.
  4. If you are using the web-farm (redundant) setup, what type of loadbalancer are you using? Are you using sticky session?
  5. What session state mode are you using? In-proc, SQL Server SessionState, or StateServer?
  6. Version of the Identify instance that you are using.
  7. Information about any custom components that you have: interceptor, generic authentication provider etc.