JWT and one-time tokens?
Asked Answered
L

4

10

I'm in the process of rolling my own JWT token auth, however, I would really like it to be a one time-token – so once it's used, the server generates a new token and the client will have to use that token during the next request/call.

However, it has come to my understanding that JWT is supposed to be 'stateless' – but with the approach of a one time token, I guess I would need to somehow store the valid tokens, since the token will be refreshed once it's used. Or is there any way to avoid storing a value on the server, and still be able to create one-time tokens?

The two main reasons for why I don't want to store any value is first of all scalability (sure, I could have cache-server inbetween to store the values, but it would be nice if that wasn't required), secondly, JWT is supposed to be stateless from my understanding, which it wouldn't be if I need to store a value on the server to be able to validate the token.

Any ideas?

Lambaste answered 1/5, 2017 at 13:9 Comment(7)
Why would you need it to be one time only? What happens if a token is used a second time?Waki
Im thinking that if we only generate one token to be used for all endpoints.. and lets say we have an endpoint for deleting records in a database.. then a man in the middle or simply someone who manage to get hold on that token ONCE would be able to create multiple delete-requests and cause alot of damage.. however.. if the token only can be used once, then the damage would at least limited.. I might be misstaken though..Lambaste
Solutions exist but they depends on your business.Waki
Your justufication is not right. Even if you invalidate after the first use, that man-in-the-middle could catch another and another token and so onWaki
True.. but using a single token to deal with all authentication would make it alot easier for a man in the middle i guess.. lets say we mamage to get hold in a token.. the token wont expire until 30min after its creation.. that would leave our api endpoints close to "un-guarded" during 30min.. however.. if we invalidate the token once used and provides a new token.. then maybe (just maybe) our actual user might already have consumed that token and it would be useless for our man in the middle.. sure, this isnt "safe".. but its still one additional level of security.. i guess?Lambaste
I don't agree. You over-complicate things and you gain almost nothing. However, if you still want those one-time-tokens see my answer.Waki
Ok, yes.. thanks alot for your feedback :)!Lambaste
H
26

Use the user's current password's hash for signing the JWT token, in this way all tokens generated before a successful password change would get invalidated the next time. I got the idea from here https://www.jbspeakr.cc/howto-single-use-jwt/.

Halbert answered 12/11, 2018 at 15:29 Comment(3)
Here's a question - so you sign all your endpoints with a private key, key you sign them with a password change key - how do you auth the other endpoints?Curmudgeon
Personally I wouldn't change the singing key, i'd issue a shorter token.Curmudgeon
Whilst not meant personally that article IS NOT the way, it's A way, but I wouldn't recommend it.Curmudgeon
W
5

Solutions exist, of course.

As with any distributed system (you mentioned scalability) you have to choose between availability and consistence.

  1. You choose availability. In this case you could maintain a list of already-used tokens that you replicate in a eventually consistent manner between all the endpoints. For example when a token is used the respective endpoint send that token to the other endpoints in the backgound. There is however a (short) time frame when that token can be used a second time by another endpoint until that endpoint is updated.

  2. You choose consistency (you won't allow a token to be used multiple times whatsoever). In this case you use a central database with already-used tokens and you check that database everytime you need to perform an action. Scalability? You could use sharding on the token and have n databases, each one being responsible for a tokens subset.

It depends on your business what solution fits best.

Waki answered 1/5, 2017 at 15:30 Comment(1)
Yes, the idea for now is to store the tokens in a Redis cache. However Im not sure of how much performance overhead that would add.. so if it were possible to avoid using a db, then that would be sweet.. however, I guess that could be tricky..or impossible :/ Im might also be over-complicating this since the performance hit might not even be noticable.. but thought that all possible solutions should be investigated before I decide..Lambaste
C
2

Not really no, a JWT token is valid if it hasn't expired and the signature is correct, commonly people will keep a DB of blacklisted tokens which are usually ones where people have logged out etc.

The only sensible way I can think of is give them a short expiry time and maintain a list of tokens that have already been used, you'd then periodically remove the ones that subsequently expire from the DB.

There are actually some DB's that have a TTL on records (dynamoDB, mongodb) so you'd just put the tokens in and set a TTL for when the token expires.

Update 2022 Just to be clear JWT tokens AREN'T stateless they have claims that, as long as they're signed by the right private key - give you a stateful piece of data that can be reissued by your API to reflect the current state of the user.

You'd just need to handle token re-issue on the consumer.

Curmudgeon answered 1/5, 2017 at 13:15 Comment(3)
Ah ok, yeah I read some about blacklisting the already used tokens.. that could be one way of doing it.. however.. and I know this is close to impossible.. but would it be possible to use "one time"-tokens without the need of a db (any kind..).. to treat the token as "stateless"?Lambaste
I personally can't think of another way, why are trying to avoid a DB? You could simply use redis/TTL the DB wouldn't get that big if you set a short expiry time on tokens?Curmudgeon
You need to have some sort of state of the transaction, i.e. something like token ABC was used. If you want to keep it "lighter" you might use a nonce in the jwt and store that state against that nonce in the db, so you don't have to store the whole token. In any case, you should use either a ttl or a batch job to delete the obsolete data from the db if you're concerned with the db size.Liverish
M
0

Like others have mentioned, it depends on your business case. Password resets links can be like mentioned on https://www.jbspeakr.cc/howto-single-use-jwt/.

If you have the Single-Use & Single-Auth scenario, where you might want to invalidate any previously used and unused token, you can store a single nonce and update it on every new token request and also when its used.

Momentous answered 30/1, 2019 at 13:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.