The labs on this site need JavaScript to run. The same content is written out below.
Learn › Security › Authentication
Sessions and JWT
Where is the login state stored?
A logged-in user sends requests at 10-minute intervals, and a load balancer hands the requests to the servers in turn. See what happens when you add servers or force a logout, first when the server remembers the login state (a session) and then when the client carries it (a JWT).
Conceptual model · A model that reproduces a widely used setup
Concepts in this lab
- Cookie
- A small piece of data that a server sends in a response. The browser stores it and automatically sends it back with every request to the same site. HTTP does not remember state between requests, so this data is used to keep information that must carry over, such as a login state. Because it is stored in the user's browser, you must use it on the assumption that the user can read or change its contents.
- Session
- A way to store a user's state on the server side and give the client only an identifier (the session ID) that points to that stored state, delivered in a cookie. The actual information is on the server, so the client cannot change its contents (having the session ID stolen is a different problem). When there are several servers, a common setup keeps this state in a shared store such as Redis so that any server that receives a request can find the same state.
- JSON Web Token (JWT)
- A token that carries content such as user information (claims) and has a signature attached. It has three parts: a header, a payload, and a signature. The server verifies the signature to confirm that the token was not forged, and it does not need to look up a separate store. The payload is encoded, not encrypted, so anyone can read its contents. Do not put sensitive information in it.
- Access token and refresh token
- An access token is the token that a client presents when it calls an API. Its lifetime is kept short to reduce the damage if it is stolen. A refresh token is the token used to get a new access token when the access token expires. It has a long lifetime and is stored more securely. The server can revoke a refresh token to stop new access tokens from being issued.
- HTTP as a stateless protocol
- HTTP is a communication protocol in which a client sends a request and a server responds. Stateless means that the server does not remember the content of earlier requests, so each request must carry all the information needed to process it. Because the server keeps no state, any server can take a request, which makes it easy to add servers. Information that must carry over, such as a login state, is kept with cookies, sessions, or tokens.
Try these
- Default settings — Session in server memory, 2 servers. A request that goes to a different server is no longer logged in.
- Move the session to Redis — The login stays valid whichever server the request goes to. In exchange, every request queries the store.
- Switch to JWT — The server remembers nothing. The token expires after 25 minutes.
- JWT with a forced logout — There is no way to stop a token that has already been issued until it expires.
- Session with a forced logout — If you delete the session from the store, requests are blocked starting with the very next one.
- Short access token + refresh token — A new access token is issued every 15 minutes. Even if it comes late, a forced logout takes effect within 15 minutes.
Running with the default settings
- 0 min: The user logged in on Server 1. Server 1 creates a session in its own memory and sends the session ID down in a cookie. Only Server 1 remembers this user.
- 10 min: The request went to Server 2. It sent the session ID in the cookie, but this server has no such session in its memory, so it treats the user as not logged in.
- 20 min: The request went to Server 1. This server has session abc123 in its memory, so it treats the user as logged in.
- 30 min: The request went to Server 2. It sent the session ID in the cookie, but this server has no such session in its memory, so it treats the user as not logged in.
- 40 min: The request went to Server 1. This server has session abc123 in its memory, so it treats the user as logged in.
- 50 min: The request went to Server 2. It sent the session ID in the cookie, but this server has no such session in its memory, so it treats the user as not logged in.
Result of this run
- The session exists only in the memory of the one server where the user logged in, so requests that went to other servers lost the login. To add more servers, you must either always send the same user to the same server (sticky sessions) or move the session to a shared store.
Code
// On successful login
HttpSession session = request.getSession();
session.setAttribute("memberId", member.getId());
// Response header: Set-Cookie: JSESSIONID=abc123; HttpOnly
// On every later request: find the session by the session ID in the cookie
HttpSession session = request.getSession(false);
if (session == null) {
throw new UnauthorizedException(); // 401
}
Assumptions of this model
It assumes that the load balancer sends requests to the servers in turn (the login goes to Server 1, the next request to Server 2, and so on). The session expiration time and the refresh token expiration (usually days to weeks) are assumed not to be reached within these 50 minutes. Verifying a JWT signature uses only the key the server holds, with no store. Keeping the refresh token in a server store and checking it there is a widely used setup, but no standard requires it.
Next lab: Cookies