Handling Token Renewal and Session Expiration in RESTful APIs

authenticationdesignhttprestweb services

I'm building a RESTful API that uses JWT tokens for user authentication (issued by a login endpoint and sent in all headers afterwards), and the tokens need to be refreshed after a fixed amount of time (invoking a renew endpoint, which returns a renewed token).

It's possible that an user's API session becomes invalid before the token expires, hence all of my endpoints start by checking that: 1) the token is still valid and 2) the user's session is still valid. There is no way to directly invalidate the token, because the clients store it locally.

Therefore all my endpoints have to signal my clients of two possible conditions: 1) that it's time to renew the token or 2) that the session has become invalid, and they are no longer allowed to access the system. I can think of two alternatives for my endpoints to signal their clients when one of the two conditions occurs (assume that the clients can be adapted to either option):

  1. Return an http 401 code (unauthorized) if the session has become invalid or return a 412 code (precondition failed) when the token has expired and it's time to call the renew endpoint, which will return a 200 (ok) code.
  2. Return 401 for signaling that either the session is invalid or the token has expired. In this case the client will immediately call the renew endpoint, if it returns 200 then the token is refreshed, but if renew also returns 401 then it means that the client is out of the system.

Which of the two above alternatives would you recommend? Which one would be more standard, simpler to understand, and/or more RESTful? Or would you recommend a different approach altogether? Do you see any obvious problems or security risks with either option? Extra points if your answer includes external references that support your opinion.

UPDATE

Guys, please focus on the real question – which of the two http code alternatives for signaling a renewal/session invalidation is the best? Don't mind the fact that my system uses JWT and server-side sessions, that's a peculiarity of my API for very specific business rules, and not the part I'm seeking help for 😉

Best Answer

This sounds like a case of authentication versus authorization.

JWTs are cryptographically signed claims about the originator of a request. A JWT might contain claims like "This request is for user X" and "User X has an administrator roles". Obtaining and providing this proof through passwords, signatures, and TLS is the domain of authentication - proving you are who you say you are.

What those claims mean to your server - what specific users and roles are allowed to do - is the problem of authorization. The difference between the two can be described with two scenarios. Suppose Bob wants to enter the restricted storage section of his company's warehouse, but first he must deal with a guard named Jim.

Scenario A - Authentication

  • Bob: "Hello Jim, I'd like to enter restricted storage."
  • Jim: "Have you got your badge?"
  • Bob: "Nope, forgot it."
  • Jim: "Sorry pal, no entry without a badge."

Scenario B - Authorization

  • Bob: "Hello Jim, I'd like to enter restricted storage. Here's my badge."
  • Jim: "Hey Bob, you need level 2 clearance to enter here. Sorry."

JWT expiration times are an authentication device used to prevent others from stealing them. If all your JWTs have five minute expiration times, it's not nearly as big a deal if they're stolen because they'll quickly become useless. However, the "session expiration" rule you discuss sounds like an authorization problem. Some change in state means that user X is no longer allowed to do something they used to be able to do. For instance, user Bob might have been fired - it doesn't matter that his badge says he's Bob anymore, because simply being Bob no longer gives him any authority with the company.

These two cases have distinct HTTP response codes: 401 Unauthorized and 403 Forbidden. The unfortunately named 401 code is for authentication issues such as missing, expired, or revoked credentials. 403 is for authorization, where the server knows exactly who you are but you're just not allowed to do the thing you're attempting to do. In the case of a user's account being deleted, attempting to do something with a JWT at an endpoint would result in a 403 Forbidden response. However, if the JWT is expired, the correct result would be 401 Unauthorized.

A common JWT pattern is to have "long lived" and "short lived" tokens. Long lived tokens are stored on the client like short lived tokens, but they're limited in scope and only used with your authorization system to obtain short lived tokens. Long lived tokens, as the name implies, have very long expiration periods - you can use them to request new tokens for days or weeks on end. Short lived tokens are the tokens you're describing, used with very short expiration times to interact with your system. Long lived tokens are useful to implement Remember Me functionality, so you don't need to supply your password every five minutes to get a new short lived token.

The "session invalidation" problem you're describing sounds similar to attempting to invalidate a long-lived JWT, as short lived ones are rarely stored server-side while long-lived ones are tracked in case they need to be revoked. In such a system, attempting to acquire credentials with a revoked long-lived token would result in 401 Unauthorized, because the user might technically be able to acquire credentials but the token they're using isn't suitable for the task. Then when the user attempts to acquire a new long lived token using their username and password, the system could respond with 403 Forbidden if they're kicked out of the system.

Related Topic