JWT - "session" expiry

225 views
Skip to first unread message

andre.a...@tappr.io

unread,
Feb 21, 2017, 3:14:27 AM2/21/17
to Rodauth
Hello all,

I've inherited a Grape JSON / API only app with a partial, home-made authentication. Given the comprehensiveness and quality of Rodauth, I thought I would convert my app. There is one thing I have not understood.  How do I forcibly expire a user session?

Use case:
1) User logs in  (JWT only, so he gets back a jwt)
2) App stores JWT to provide on subsequent requests
3) User loses his phone (which has the logged-in client app with the stored jwt)
4) Block that session  (log in to admin panel, see list of sessions, clear the relevant session).  <- How do I do that?

As far as I could see playing around, when using jwt only, there is no "session" stored server side. For as long as the token is valid, somebody could steal the jwt and use it, even after the user logs out. On the other hand, this mechanism is there for html. Am I missing something?

In my context, I need long sessions (the user may lose connectivity for long periods), and also wanted to expire the session when the user logs out as the app is sensitive.
The way my current app was doing it was generating a jwt authorization token, however storing it server side and treating it like a session cookie (in addition to having a refresh token).

Thanks
André

Jeremy Evans

unread,
Feb 21, 2017, 11:01:11 AM2/21/17
to Rodauth
You are correct that in the JWT case, nothing is stored server side.  This is also true in the non-JWT case if you are storing sessions in cookies.

The single_session feature would handle your issue, though I'm not sure if you want the other feature it brings.  It's possible we could add a separate feature that worked like single_session in terms of forcing immediate session expiration, but allowed multiple sessions.  That would generally only be useful for JWT, since in the normal case you would just store sessions server side.

Thanks,
Jeremy

Mike Pastore

unread,
Feb 21, 2017, 6:51:35 PM2/21/17
to Rodauth
This is one of those "don't do that" scenarios that everyone does anyway. You might consider assigning each newly-issued JWT a universally unique ID, storing it in the :jti claim, and tracking some associated metadata (e.g. user-agent, IP address) in your database. Add :verify_jti to your jwt_decode_opts to check the UUID against a revocation list when it's decoded. When you need to block a session, simply add that JWT's UUID to the list. 

The oft-cited issue with this approach is that now your JWT is a glorified session ID and you've reinvented the wheel, poorly. If your revocation list is out of sync or unavailable, you have to decide whether to block or accept those JTIs. I think a decent compromise is to keep a local copy of the revocation list with each instance of your application and sync them to the master revocation list as best as possible. The potential failure case loophole here is that a JWT is revoked but stays effectively valid until the revocation list is fully replicated to all the application instances. So that's just one approach...

There are other ways to accomplish the above, such as a per-user salt to use in conjunction with the global HMAC secret, but it leads the same sorts of problems: in order to validate the JWT, you need to look something up in a database, which essentially defeats the purpose of JWTs.

andre.a...@tappr.io

unread,
Feb 21, 2017, 8:36:28 PM2/21/17
to Rodauth
I know.   And my existing app has some sort of session reinvented: a JWT that expires once in a while, and a refresh token (stored in DB), that allows you to get a new JWT. That refresh token should be one use, except the old one is mistakenly not deleted after usage :D.

In way of requirement:
- I cannot use browser sessions, as some of my clients are mobile apps.
- I want very long sessions so the user does not need to log back in
- I'd like the ability to remotely kill a session (or could compromise on deactivating an account).
- Load is not expected to be an issue, but reliability (no single point of failure) may become a requirement down the line.


What if I had
- short expiry on JWT
- when they expire, use a 'remember me' token
- client automatically logs back in using the 'remember me' token
- on logout, client calls server to remove that 'remember me' token they were assigned
- if I need to remotely close a session, I look up somehow that 'remember me' token and remove it.

I'm liking Mike's approach above, I may combine these two.

Thanks for the thoughts
André



Mike Pastore

unread,
Feb 22, 2017, 4:04:20 PM2/22/17
to Rodauth
If you can revoke any JWT by adding the JTI to a revocation list, it's not clear to me why a separate "remember me" token is necessary?

andre.a...@tappr.io

unread,
Feb 22, 2017, 7:43:45 PM2/22/17
to Rodauth
My gut feeling was against the idea of a token being valid for a very long time by default, and felt better with a white list than a black list.

Very good question. It took me a while to think it through:

Option 1) Long session, + JTI / revocation list.    With most users not bothering to logout, I have all these valid tokens stored out there. On logout, server add token to the revocation list.

Option 2) Short session (say a few hours). I need a renewal token (I thought I'd coerce the remember me feature to provide this). On logout
clients delete the remember me token,  and server also removes that token. Without logout, I have all these remember me / renewal tokens stored out there.

I originally thought 2 was much better. Now, I only see that 2 only keeps a list of currently valid tokens (the remember me / renewal ones), whereas I would need to keep  the list of all tokens distributed almost forever [I need very long session - say months]. One security benefit is that a attacker that stoles a token could use it for long (short expiry, and the remember me token would only be used once

Also, 2 allows me to use the nice re-authentication mechanism of "remember me."   You haven't authenticated in a while, I may force you to re-authenticate for a sensitive operation.

Marginal benefits to 2) over 1?

All in all, I may still be trying to re-invent sessions.

André
Reply all
Reply to author
Forward
0 new messages