Session management 5.9
Overview
When using Safewhere Identify, you may experience 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 a.k.a. "corrupted, expired, or misconfigured session state".
The complexity of state management
At first, A typical login flow that Identify needs to handle is:
- I want to access a service provider.
- The service provider sends a request to Identify.
- Identify forwards a request to an upstream IdP. Identify needs to store the request's id in session for later validation purposes.
- Identify receives a response from the upstream IdP. Identify needs to validate whether the response is for the request id that it saves in session.
- Identify returns a response to the service provider.
- I log out of the service provider.
- The service provider sends a logout request to Identify.
- Identify sends a logout request to the upstream IdP.
- The upstream IdP sends a logout response to Identify.
- 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. Identify uses both of them for the login session storage where the cookies for store login participants and other flag values. See the New SLO behaviors for more detailed information.
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:
- 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 tab, I enter my credentials to log in. Finally, I switch to the first tab and enter my credentials there.
- Similar to (1) but this time I access two different service providers on two separate tabs.
- Similar to (2) but this time I select different identity providers.
- For each case, I can even enter different credentials.
- 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:
- I want to access a service provider.
- The service provider sends a request to Identify.
- Identify generates a new context object with a contextID (which is no more than a GUID) and puts 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.
- Identify receives a response from the upstream IdP and needs to validate whether the response is for the request id that it saves in session. Identify will use the returned relaystate to look for the related contextID and context object. The context object contains all information that Identify needs to do validation and log me in.
- Identify returns a response to the service provider.
- I log out of the service provider.
- The service provider sends a logout request with SessionIndex to Identify.
- Identify uses the entity identifier from the request to find the corresponding "SSO context" from the cookies, then combining with the SessionIndex to look for the participant who is logging out. The participant object contains all information that Identify needs to do validation and to log me out. Identify sends a logout request to the upstream IdP.
- The upstream IdP sends a logout response to Identify.
- Identify sends a logout response to the service provider.
Thanks to contextID, Identify can now handle all the logins 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 involve even more steps in between.
The good news is that the contextID now is not a part of the SLO process. It results from a rework on SLO to separate completely the SLO and the SSO. So, the contextID problem ("invalid contextid") no longer happens at the SLO time.
However, at the login time, Identify may encounter some of the below common contextId issues.
Common causes of the contextId issues
However, maintaining states is a hard problem because so many special 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:
- Use in-proc session state with sticky session.
- Use out-of-proc Asp.Net state server.
- 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 problems. 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 in 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 doesn'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 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 causes and is also the hardest to handle. Many things can go wrong at login time:
- I enter credentials to log in or the IdP returns a response to Identify after a session has expired.
- A login is done successfully. The service provider or another service provider then requests a new token after Identify's session has expired.
In fact, we have tried to handle many such special cases. If you encounter a contextID error, we would like to receive a report with instructions on how to reproduce the problem.
Troubleshooting
If session state problem consistently happens to you, it is more likely caused by a mis-configured server:
- If you are using the web-farm (redundant) setup, check if you have set up session state correctly.
- Check if your deployment has applied a workaround or a patch for the SameSite issue.
- Check if your browser is sending cookies around correctly.
- 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:
- Step by step instructions for how to reproduce your problem.
- A browser trace (either a Fiddler trace or a HAR trace).
- Identify's logs.
- If you are using the web-farm (redundant) setup, what type of loadbalancer are you using? Are you using sticky session?
- What session state mode are you using? In-proc, SQL Server SessionState, or StateServer?
- Version of the Identify instance that you are using.
- Information about any custom components that you have: interceptor, generic authentication provider etc.