18

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 ;)

5
  • How would a user's session become invalid before the token expires, assuming a short (approx. 5 minutes) expiration time?
    – Jack
    Commented Dec 17, 2016 at 22:48
  • Because of a business rule, a different part of the system might invalidate the session. Commented Dec 17, 2016 at 22:49
  • 1
    JWTs are for proof of identity, as in "this request is proved to be from user X". If your business rule is something like "user X is no longer allowed to interact with resource Y", that's something that should be checked separately from the JWT.
    – Jack
    Commented Dec 17, 2016 at 22:51
  • @Jack exactly! that's my point precisely, and the reason why I have to use an additional, stateful layer for saving session information. Plain JWT, nice as it might be, is just not cut for the work. Commented Dec 17, 2016 at 23:02
  • 1
    You may be interested in my answer then :)
    – Jack
    Commented Dec 17, 2016 at 23:58

3 Answers 3

22

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.

1
  • 3
    This is the guidance I was looking for, and you brought a very relevant insight to the discussion - that this is a case of authentication vs. authorization, and each should be dealt differently. Thanks! Commented Dec 19, 2016 at 16:35
17

Your API session is a thing which should not exist in a RESTful world at all. RESTful operations are supposed to be stateless, session contains state and thus has no place in a RESTful world.

The JWT should be your only way to determine whether a user is still eligible to access an endpoint or not. A session should play absolutely no role in it. If it does, you do not have a RESTful API.

When you eliminate the session altogether, which if you are aiming for a RESTful API you should do, and only use the JWT as a authenticating factor, a user is either authorized to use your endpoint or not - in which case the 401 Unauthorized response code is appropriate - and should call the renew endpoint with grant_type=refresh_token or whatever identification of renewal you are using.

Update:

From the comment it seems like the validation flow of the JWT you are currently using is not correct. The validation is supposed to look like this:

  Client        RESTful API      JWT Issuer
     |              |                |
     |----- 1. ---->|                | 
     |              |------ 2. ----->|-----
     |              |                | 3. |
     |              |<----- 4. ------|<----
-----|<---- 5. -----|                |
| 6. |              |                |
---->|----- 7. ---->|                |
     |              |------ 8. ----->|-----
     |              |                | 9. |
     |              |<----- 10. -----|<----
     |              |                |
     |              |------          |
     |              | 11. |          |
     |<---- 12. ----|<-----          |
     |              |                |
     .              .                .

1. Ask RESTful API for a JWT using login endpoint.
2. Ask Issuer to create a new JWT.
3. Create JWT.
4. Return JWT to the RESTful API.
5. Return JWT to Client.
6. Store JWT to append it to all future API requests.
7. Ask for data from API providing JWT as authorization.
8. Send JWT to Issuer for verification.
9. Issuer verifies JWT.
10. Issuer returns 200 OK, verification successful.
11. Retrieve and format data for Client.
12. Return data to Client.

The server, RESTful API, has to check the validity of the token that is being sent as the Authorization. That is not the responsibility of the Client. It seems like you are currently not doing this. Implement the verification of the JWT this way and you do not need sessions at all.

14
  • Thanks for your answer. Agreed, using a session would not be a 100% RESTful approach, but as I mentioned above, I need to deny access to some users before the token expires. Commented Dec 17, 2016 at 17:47
  • 2
    @ÓscarLópez Then simply invalidate the tokens the users are using. They will no longer be able to access the API using the provided token (which now will have been invalidated) and you do not need a session.
    – Andy
    Commented Dec 17, 2016 at 17:53
  • 1
    The tokens are stored on the client, how can I invalidate them there? I would have to keep track of which ones are valid... and that's where state rears its ugly head. Sometimes it's unavoidable. Commented Dec 17, 2016 at 18:04
  • A malicious client could keep sending a previously valid token for as long as its expiration time allows it, but I need to kick him out of the system immediately - hence setting a short renewal time is also not an option. Commented Dec 17, 2016 at 18:16
  • 4
    @Laiv I made the sequence diagram in notepad.
    – Andy
    Commented Dec 17, 2016 at 21:23
1

So, I'll confess that it doesn't make much sense to me to be worried about which approach is most RESTful when you're already breaking REST conventions with the session, but I understand satisfying your business requirements.

From a REST standpoint, the client either is authenticated, or they are not. The architecture does not care much why (that is injecting needless state), so to answer your main question, I would not have a renew endpoint at all. A logged in client will just always send their JWT and the server always validates it and either accepts by sending the appropriate Success code based on the action 200, 201, etc) or rejects with a 401 or 403 as appropriate.

Now, the JWT is going to be associated with an account of some kind. That account might be locked or throttled or whatever, and so the token itself may be valid but the action still be rejected elsewhere. If the case is that the user account is locked because of business rules, then it is still a 401 or 403 depending on how much info you want to give the client (different businesses have different opinions on that).

Finally, if you are asserting that the account might be unlocked and valid but the JWT just needs to be revoked, then I would STILL stick with the 401 or 403 and maintain something like a Certificate Revocation List of invalid JWTs that you can put one in, so long as it cleans itself up when the JWT would have expired (most databases have a way to do that or you can have events in app code).

1
  • jwt are meant to be stateless. the moment you question its content and validate it against a db, it is no longer stateless thus becomes redundant as there are better solutions for statefull sessions
    – Stavm
    Commented Feb 17, 2019 at 11:01

Not the answer you're looking for? Browse other questions tagged or ask your own question.