5 ms·
I think writing your own variation of this at one point is a sort of rite of passage for web developers. I thought I was so clever with my custom PHP framework
by iLoch 12y ago
I think writing your own variation of this at one point is a sort of rite of passage for web developers. I thought I was so clever with my custom PHP framework and sessionless backend. That said, it's done because it works well and makes sense. I'm glad to see we're finally settling on a recommended way of doing this.
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?
- necubi 12y agoThe OAuth way of doing things is to have two tokens: an auth token and a refresh token. The auth token is the one you send with your requests to authenticate them (and can be a JWT), but it has a very short expiry time (say an hour). The refresh token is typically a random string that's stored in your database and can be used in place of a username/password or other credentials to get an auth token. 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.
- alexanderblom 12y agoOne way of doing it is having an out-of-bands way of refreshing tokens in responses. So if the token is about to expire you can return an updated one on the side-channel.
- robotrobot 12y agoWe've been doing this for a while in our angular apps using response interceptors: http://engineering.talis.com/articles/elegant-api-auth-angular-js/ http://engineering.talis.com/articles/elegant-api-auth-angul...
- flaie 12y agoThis is a great write-up.
- jfroma 12y agoYes, it is a bad UX that the session expire randomly and how to handle this is a whole subject per se. 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 https://auth0.com/blog.
- vukers 12y agoI've been thinking about the same problem lately and have come up with an approach for my use-case that might work: * 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.