Handling expiry/“remember me” functionality with J

2020-05-13 10:01发布

Conceptually, I really like JWT as it is in line with the statelessness of REST etc (no state saved server-side, all relevant data is contained in the token).

What I am unsure about: how would you handle token expiry when not connected (ie, a "remember me" functionality)?

There's an emerging coverage of JWT on the web, but I couldn't find anyone that answered the expiry question yet.

Clarification: I am not asking how to handle a token soon-to-expire, but what to do when a token has already expired (user closed website/app for a while). The simplest solution that comes to my mind is caching the user's credentials, which is rather insecure.

6条回答
倾城 Initia
2楼-- · 2020-05-13 10:05

I can think of one way, but it is not really defined the standard.

What about adding another kind of expiration date with different lifespan to the claims? With two claims, we can treat the shorter one of it as the resource access expiration date, and the longer one as the refresh expiration date, e.g.

{
    "iat": /* current time */,
    "bbf": /* current time + 1 hour -- expired means no resource access */
    "exp": /* current time + 1 week -- expired means cannot refresh */
}

(Note: I use bbf for the shorter expiration date. No specific reason, just because it has 3 characters in length.)

So with "remember me" checked, when the user reconnects, he can use the same token to request for a new one, but not to access the resource. With this, all relevant data is contained within the token -- no extra token required.

And lastly, when "remember me" not checked, just use the same lifespan for bbf and exp.

查看更多
乱世女痞
3楼-- · 2020-05-13 10:14

You need to persist the JWT on the client so that it’s available across page loads, the most secure strategy is an HTTPS-only cookie. This will send the JWT to your server on every request and the server can check the validity of the token and reject it if it's expired. How you handle the expiration is dependent on the type of web app you have.

For a single-page application (e.g. Angular.js apps) you would want to structure the application so that it makes an initial request of the server before it bootstraps the rest of the application. If the server sees that the JWT in this request is expired it would issue a 401 response. You application would respond to this response by rendering a login form. Otherwise it would continue with the assumption that the JWT is valid and can be used to access the required resources. If, at any time, the app sees a 401 it should bring the user back to the login form.

For traditional web apps that render their pages on the server: for any request that has an expired JWT (as read from the cookie) the server should issue a 302 redirect to a login form.

查看更多
4楼-- · 2020-05-13 10:18

There isn't a black and white answer to the given problem of how to implement the remember-me functionality theoretical wise. Lots of theoretical arguments are given on why something should not be done, while at the same time there is no clear answer given to the problem as for how it should practically be done.

Remember-me implicitly comes with the problem that you need a longer time window of token expiration, there is just no way around this. The most secure way is to have the user login regularly given brief expiry; nobody practically likes this though, so a tradeoff is made where theoretical secure perfection is weighed against practical measures.

The way this tradeoff works is by patching the imperfections that come with having a long expiry for a token. But make no mistake about it, you will need a jwt / cookie with long expiry (whether you use two tokens, implement some secondary refreshing mechanism or whatever, at the end you run into the same problem)!
Based on articles I've read on how others did it, this is how it's done.

The way I will implement this is by offering a 3-month expiry on a jwt stored in an httpOnly / secure cookie when the user checks remember-me.
On logout just clear the cookie.
Obviously protect using https / CSRF measures on top.

If you don't agree with it, stop fussing and offer an alternative solution - which there clearly isn't from the infinite number of discussions I've read on it.
If there were a straightforward solution to the problem, there probably wouldn't be so many discussions about it in the first place.

查看更多
等我变得足够好
5楼-- · 2020-05-13 10:23

I am not so sure if I follow but I will write what I think.

Imagine the token as a hotel card, you pay in advance for 5 days (remember me set to expire on 5 days). I can enter the building, garage, room, etc. within those 5 days, after those 5 days, it won't work anymore.

What to do when token has already expired? Nothing at all.

Imagine I pay those 5 days and meh, I had an urgency and I go back home (with the card on the pocket). The hotel doesn't care at all, when the 5 days pass, the card is just an useless piece of plastic and if you try to use it on the hotel, it will do nothing.

So back to web development. If you offer a remember me service, you can put an expiry date to let's say 7 days. As long as the user has the token, he can access the service without any problem. If he loses the token, he needs to login again. If he uses the token and it have expired, he will need to login again too.

If he login, he gets a token for 7 days, if he doesn't use it anymore and after 20 days he comes again, he would need to login again, the server will just decline your petitions until you do so.

What I would do if you use something like angular on the frontend is to check the token validation on startup so you can have a nice user experience.

What I don't understand about your question is de caching thing though.

查看更多
Luminary・发光体
6楼-- · 2020-05-13 10:25

I think what you are asking is how to invalidate a JWT server side for long expiry tokens (e.g. "remember me" functionality)?

I ran into this issue myself recently and ended up using a unique user secret to invalidate the token, when the user attempts to validate a token that was produced with an old secret it will fail. The username can be found in the decoded JWT pre verification.

You could probably even use the users password salt for this, that way any current JWT's would be invalidated when a user changes their password (assuming you also change the salt at the same time), this may be problematic though as the password hash and JWT's would become tightly coupled

查看更多
地球回转人心会变
7楼-- · 2020-05-13 10:26

In addition to @Jesus answer, you can think about implementing a refresh token system: https://auth0.com/blog/refresh-tokens-what-are-they-and-when-to-use-them/

In the hotel-example, your hotel-card (access-token) would be invalid after time X, but at the reception you can use your passport (refresh-token) to get a new hotel card again.

You could store the refresh token in the database with additional data about the device the user is using, allowing him to disable the device in case it gets stolen.

Example:

  1. first correct client login: Create a refresh token which is valid forever (until it gets deleted or invalidated)
  2. store refresh token in database
  3. return access token (JWT) with expiration time to client ( this token gets not stored in database)
  4. for the next request, the client sends the access token

  5. Now Check if the access token is expired:

    5.1 Access Token not expired, all okay

    5.2 Access Token expired, check if there is a refresh token in database

    5.2.1 Refresh Token is in database, return new Access Token

    5.2.2 No Refresh Token in database, return 401 / logout, User has to login again

Hope this helps.

查看更多
登录 后发表回答