JWT and one(!) session per user / no concurrent sessions
Asked Answered
I

5

18

Our current app uses HTTP sessions and we'd like to replace that with JWT.

The setup allows only a single session per user. This means:

  1. User signs in at Device 1
    • User is logged in at Device 1 (new Session created)
  2. User signs in at Device 2
    • User is logged in at Device 2 (new Session created)
    • User is not logged in at Device 1 (Session got destroyed)

This works because there's a server-side relation between session id and user id.


Using JWT I could imagine to have some counter inside the user database, which gets increased with every login, i.e.:

  1. User signs in at Device 1
    • JWT tokens signature contains counter+1 (and save new counter to database)
  2. User signs in at Device 2
    • JWT's signature contains counter+1 and it gets increased and saved to db.

Now with every request I have to check if the incoming signature is correct for the current counter value.

This somehow makes it stateful. :(

But ... one of JWT's benefits is, that there's no need to access any database or session store for validating the token.


Is there some other solution for preventing concurrent logins? Maybe something that works without database access and keeps it stateless?

Inanna answered 23/2, 2015 at 21:22 Comment(1)
You might be able to create a custom claim in the JWT when you sign and send the client the JWT. When they send the token back to you, verify that the specific claim like 'device_id', for example, hasn't changed. That's a bit tricky, but you really may have to do some work with sessions on the server side even though you're trying to avoid that.Orthoclase
M
45

You are very close to the solution.

To do this you need the following:
1. Include iat in the token (Time when the token was issued)
2. Somewhere store the time when the user last logged in, for example in the user's profile.

Now when validating the token, do an extra check: iat (Issued At) must be at or later than the last login time. This implicitly invalidates older tokens.

Matrimonial answered 21/7, 2017 at 16:11 Comment(5)
Thank you. This comment answer was helpful.Maples
Excellent strategy!Barm
This solution makes the token validation stateful.Faradmeter
"Stateful" means keeping session information in the server. Storing the state in the DB, which is what I suggested, does NOT make it a sateful solution.Matrimonial
However if you are using any sort of microservice architecture or clustering, autoscaling etc, keeping track of this in the server will need an additional layer of complexity in case the new login request gets load balanced to a different server instance for example.Pillowcase
W
3

First of all, when using JWTs for session, it's important to define:

  • Stateless Token: Contains all the session data inside the token. This way you don't need to store it anywhere.
  • Steteful Token: Contains a session id. When the server receives the token he will need to retrieve the information about the session.

If you are using a stateful solution in order to invalidate a token you could query the DB for the last session ID for that user and compare with the received token.
In a stateless solution, as the other answers pointed out, you will probably need to persist the state somewhere. But in comparison with a Stateful Token you only need to store a counter like you suggested or a "last_login" like @The Tahaan suggested.

If you feel that using a DB is too heavy, I recommend using an in-memory solution like Redis, which besides being very fast it has the capability of easily setting a duration for the persisted data. This way the user would have to login again if:

  • User signs in in another device.
  • A certain time elapsed.
Willms answered 21/2, 2019 at 17:16 Comment(0)
C
1

What about closing the session of the user on any other device.

What about. every time user login, you saving the last login by type of device, and send a push notification to all the devices connected of the same type (supposedly one)?

In this case on a browser, You can send the push notification to the browser, just check what happens if that browser is closed at the moment?

In the case of mobile apps, you can send a push notification to the mobile app's with the instruction to close

Chasseur answered 12/6, 2019 at 3:14 Comment(0)
M
0

This is a different solution that may be a closer fit in some situations.

You still need to go to the DB, but assuming that most of your users have only one device and only some have a second device, you can use the following strategy:

During Login (New Token Request) let the client supply a device-id. Compare this with the "last_device" value of the user. If it is different it means the user has changed to a new device.

When this happens, add an epoc entry for this user in a special table.
userid: unique reference user(id) not null
epoc: timestamp

The idea is that this table could possibly be much smaller than the full user table. Only users with more than one device logged in recently will have an entry in this table. So scanning this table is efficient. After validating the token the normal way, check that the iat (Issued At) is not before the user's session epoc. If it is, then the device is not the most recent logged in device.

This solution has other uses: It allows a user to remotely log out themselves (You create a new entry for the user with the current time which effectively invalidates all existing tokens for them).

Maintain this table by deleting items that are older than the maximum lifetime of any token regularly.

Matrimonial answered 21/2, 2019 at 9:42 Comment(0)
S
0

How I think it can be done. Just create a random id (lets call this validation code) and store it in DB whenever jwt is generated. Encode this in JWT. whenever any request is made with jwt check if the validation code encoded in jwt matches in DB. If user tries to login to other device it will regenerate the validation code, expiring all other sessions.

Singletary answered 25/3, 2019 at 9:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.