Force Expiring of JWTs with Refresh Tokens
A concept to stop the bully who somehow stole your JWT.
My first post, RESTful API Design Tips from Experience, brought about many threads of discussion, some of which were focused upon JWT Authorisation and Terminating Sessions.
I reference this particular thread from Reddit:
[..] one downside to [JWTs] is that you can’t revoke [them] before their expiry without making the authentication flow stateful in some way. If a user account is compromised, the attacker can’t be forcefully logged out. You just have to wait for the token to expire.
Why not include a simple reference to the issuing refresh token in the JWT payload for additional validation?
One way around this could be to include the refresh token’s id
of sorts (potentially called rid
) in the JWT payload so the server can check not only the expiry and signature of the JWT itself, but also do a lookup from the payload against the issuing refresh token to check its validity (existence/expiry) before continuing the request.
- Check for the presence of a token in the request’s headers.
- Check that token is a valid JWT, correctly signed and not expired.
- Check the user exists from the
uid
property of the payload. - Check the issuing refresh token still exists from the
rid
property. - If all these checks pass, the token is valid; the server’s response now has context of the requesting user, and all JWTs can be invalidated if the refresh token is deleted via an request similar to
/logout
.
Where would it do this lookup from? [..] the authentication flow won’t be stateless anymore, which was the main benefit of JWTs right?
The lookup would be against the database, as that is where the refresh tokens are stored, however this brings about what is meant by potentially losing the main benefit of JWTs.
Adding a “required” reference to a value stored in the database effectively functions like a cookie, but then again, refresh tokens indirectly function the same way to retrieve a token. However this reference is persisted in the database so the whole process doesn’t rely on a temporal session that may be lost if something happens to the server process itself.
I suppose this is a detail that may need to be overlooked if one hopes to authenticate via JWTs and have a surefire way of terminating a refresh token “session” and invalidating all issued JWTs that were created with that refresh token, instead of waiting for them to expire.
It’s up to you as to what you’d prefer to focus on.
With that said, if someone were to maliciously hijack your JWT somehow, it would be in their best interest to work quickly before the JWT expires…