How to Handle Sessions with Cookies and Tokens
An application is usually an orchestration of several components, requiring more advanced session management than a single instance.
A session can be considered as the user’s activity within an application in a given time frame. It starts when the user first interacts with the application and ends when the user stops being active (explicitly or implicitly). A session can be anonymous (unauthenticated) or authenticated, but when we discuss sessions, we usually refer to the user’s authenticated session(s).
An authenticated session allows an application to identify a user and perform authorization based on the session data, without requiring the user to go through the authentication process repeatedly.
The user can end a session implicitly, such as by closing the browser or client, or explicitly by initiating a logout. Features like “remember me” commonly prevent the session from being discarded when quitting the browser. An authenticated session can also expire and become invalid.
A Session Is Not a Singleton
An authenticated session starts after the user has successfully authenticated. When using OpenID Connect, the user authenticates to the OpenID Connect provider. After successful authentication, the OpenID Connect provider returns the ID token to the client, which verifies the token and creates a session. The client also receives an access token and maybe even a refresh token used to call downstream APIs. At this stage, there are three sessions involved:
- A session with the client
- A session with the OpenID Connect provider
- A session with the API
Whenever the user interacts with the application, it will make use of the client session. The session with the OpenID Connect provider can be reused over different clients and applications, providing a single sign-on capability (SSO). These sessions are often represented by cookies.
The application can use access and refresh tokens to call a downstream API on behalf of the user. Since the API uses access tokens for authorization decisions (which was part of the definition of an authenticated session), the tokens are the building blocks for another session, the API session.
Tools for Maintaining Sessions
After receiving and verifying the ID token, the client can choose to issue a session cookie to keep track of the user. It can store session data from the ID token in memory or server side. For example, a client may want to keep the username, authentication time or authentication method and then discard the ID token.
Another type of application may just save the ID token in memory to represent the session. If required, the client also stores the access tokens and, optionally, the refresh tokens in the session. Thus, when managing and maintaining a session for an application, keep the following tools in mind:
- Session data in the backend
Although SSO is within the realm of the OpenID Connect provider, it can still be used by an application to seamlessly access a user session. If used, the user may not even notice that a client renewed its session. Access tokens, together with refresh tokens, can have a similar effect. When combined with refresh tokens, access tokens can be long lived and valid longer than a client session. Consequently, a client may be able to call an API on behalf of the user despite its own session with the user being expired.
Life Cycle Design
Handling sessions and securing data is pretty straightforward if you think of the application as a single instance of a program. A short-lived access token is used as an API credential, and refresh tokens can be used to retrieve new access tokens without user interaction. When using rolling refresh tokens, a new refresh token is issued together with the access token, which reduces the risks of stolen tokens.
However, an application is usually an orchestration of several components. It might include one or more clients, such as web, desktop or mobile, as well as other APIs in the backend and an API gateway or reverse proxy in between.
The user might interact with several components of the application at the same time. Consequently, there are trade-offs when dealing with session management, and in some use cases, it can be challenging to enable the preferred behaviors.
A simple stateless design can be a good starting point, using short-lived access tokens that will expire shortly and do not need to be revoked. In some applications, it’s OK for a user to log in from different clients (devices) simultaneously, whereas others want to restrict that (think of a paywall). In the latter case, it must be possible to revoke a session at once.
When it comes to revoking access tokens, take the token format into account. Opaque tokens using the phantom token flow can be revoked, though the gateway will need to be notified of the revocation event. Meanwhile, JSON Web Tokens (JWTs) are self-contained, so an API will not know if a JWT was revoked since it does not contact the authorization server on every request.
Curity Resources for Session Management
For an in-depth guide to session management concepts, including advanced techniques such as subscribing to revocation events, see the “Session Management Techniques” solution brief.