Here are my two cents:
- Web Apps: If you set an expiration of 1 week, do not use the token for 1 week. Use it for a day and then use the token to get a new token. This way every time your user uses the application, he gets one extra week logged in. This is not different than the normal concept of session and cookies.
If the user does not use your application for a week, next time he go to your app, he will have to login again and this is fine and widely accepted.
- For Mobile/Native Apps: you can use the same explained above, but that's not how most of the mobile applications work these days, I can open facebook after a month without using it and I'm sure I will not need to login again. One way you can do this is to use a refresh token that never expires to fetch a JWT that does expires. The problem with a token that never expires is that it never expires, what happen if your phone is stolen?. In my opinion, there must be a clear and easy interface where the user can revoke these tokens but looking at random alphanumeric characters wont help, so the best practice will be to show something like "Revoke access on John's IPad" this means that the refresh token must be requested for an specific device name.
Certain events can trigger things, if you lost your wallet (at least in Argentina), first thing you do is to call the bank to disable your credit cards. If you forget your laptop in a friend's house and you don't want then to read your Facebook you will probably change your password. Changing the password is usually a good event to
1. Revoke all refresh tokens (for native apps)
2. Anchor the "iss" date for JWTs, this means that tokens issued before the last password change are not longer valid.
Because changing the password can have other purposes, you can also put a prominent button in your UI: "Close session on all browsers and devices", this will delete all refresh token and store the timestamp to check against the JWTs.
We might write a blog post soon about this on Auth0's blog: https://auth0.com/blog.
This way you can store (in an app, or a browser) the less sensitive refresh token (which might expire after a week or so) instead of the highly sensitive permanent auth credentials.
* Set expiration to a low value (~15 minutes)
* Every generated JWT also gets added to an "issuedTokens" collection for each user
During JWT validation, if the expiration has passed, an "Expired" response would be returned from the server (ex. 401 w/ "Expired" in body). When the client receives this status, it should initiate a refresh process which trades an expired token for a new one.
The refresh endpoint on the server should take an expired token and perform the following:
1. Validate token (except expiration)
2. Retrieve user ID and check if token is in its issuedTokens collection
3. Issue a new JWT
4. Remove the expired token from the collection and add the new one
Upon failure of any of these steps, an Unauthorized error should be sent to client which then requires logging in again.
To prevent a never-ending build-up of issued tokens, we can set a TTL on the tokens in the issuedTokens collection. Set the TTL value to the amount of time that a login should be active for before requiring logging in again.
This approach doesn't hit the database unless you keep trying to refresh an expired token. In which case you can make use of a cached blacklist of failed tokens. This can reside next to the application itself if treated as a cache layer.
This is definitely just a work-in-progress solution that I'm about to test out. Let me know your thoughts on it.
I've recently incorporated JWTs into my own application. One of the questions I'd like to ask other HNers is how you deal with token expiration. I don't want to reissue a token if I don't have to, but I don't want to create permanent tokens either. An obvious solution is to embed an expiry in the token and check against that, however I don't think that'll lead to a very good UX if tokens "randomly" expire. I've come up with two solutions: 1.) Each user has a session seed token that is used as the secret and thus a user can log out of all sessions by requesting a secret change. This works because any previous JWTs are now invalid because they cannot be validated with the new secret. 2.) I could ping the server periodically (or decode the JWT on the client side) and check when the token expires. Shortly before the token expires, I could request a new one. This doesn't solve the problem of tokens expiring when the user is offline though. Anyone have any better solutions?