When JWTs Meet Sessions: A Pragmatic Approach to Device Management
#Security

When JWTs Meet Sessions: A Pragmatic Approach to Device Management

Backend Reporter
3 min read

A developer shares their experience of adding session state to a JWT-based authentication system to enable device management and remote logout functionality.

Recently, I was working on an application where a new requirement came up: users should be able to see all the devices they are logged in from and log out from a specific device if needed. This was something I hadn't implemented before, which made it an interesting challenge.

At that point, the application was using JWT-based authentication. After a successful login, a JWT was generated and stored on client's side, and every request sent this token back to the server for authentication. This approach worked well and had a major advantage: the server didn't need to store any authentication state. That made the system simple, scalable, and easy to reason about.

But it also came with a limitation.

The Limitation of Stateless JWTs

JWTs are stateless by design. Once a token is issued, the server has no native way to know how many devices a user is logged in from, which device a request is coming from, or whether access from a specific device should be revoked. As soon as features like device management or remote logout are needed, this lack of state becomes a real problem.

At that point, it became clear that the backend needed some form of state.

Introducing Session Storage

The solution I ended up with was introducing a sessions table. Each successful login creates a session record representing a single device or login instance. Along with the user reference, the session stores lightweight metadata such as device information, approximate location, login time, and last activity. This gives the system visibility into active logins without becoming overly complex.

At this point, the system becomes session-aware, but JWTs are still kept in place.

JWTs with a Session Reference

Instead of replacing JWTs, a small change was made: the generated JWT now includes a sessionId in its payload. The token is still signed, still validated as usual, but now it also carries a reference to server-side state.

On each authenticated request, the server validates the JWT and then checks whether the referenced session still exists. If it does, the request is allowed. If it doesn't, the request is rejected.

This single check enables remote logout. When a user logs out from a specific device, the corresponding session record is deleted. Any future request from that device will fail authentication, even if the JWT itself hasn't expired yet.

This also made it possible to list the devices a user is logged in from. Since the sessions table stores the userId, a single user logging in from multiple devices simply results in multiple session entries. Logging out from a specific device is then just a matter of deleting the corresponding session record. The next time that device makes a request, authentication fails.

Featured image

The Trade-offs

This hybrid approach gives you the best of both worlds: JWTs handle the stateless authentication flow, while the session table provides the state needed for device management. The trade-off is that you're now storing state on the server, which means you need to handle session cleanup, potential scaling considerations for the sessions table, and the additional database query on each request.

However, for many applications, this is a reasonable compromise. The sessions table can be relatively small (only active sessions), and the query is typically fast. You get the scalability benefits of JWTs for the majority of your authentication flow, while gaining the control needed for modern security features.

Why Not Just Use Sessions?

You might wonder why not abandon JWTs entirely and go with traditional session-based authentication. The answer is that JWTs still offer benefits: they're self-contained, can include useful claims, work well with microservices architectures, and don't require server-side storage for every active token. By keeping JWTs and adding session state only for the features that need it, you maintain those benefits while solving the device management problem.

This experience taught me that authentication patterns aren't always one-size-fits-all. Sometimes the best solution is a pragmatic hybrid that takes the strengths of different approaches and combines them to meet your specific requirements.

Comments

Loading comments...