Understanding the tymon/jwt-auth refresh token mechanism. When & why JWT_TTL, JWT_REFRESH_TTL.

Syed Sirajul Islam Anik
4 min readOct 5, 2020

--

Image from: https://bryanavery.co.uk/wp-content/uploads/2020/05/JWT.png

If you’re familiar with jwt and you want to implement jwt with Laravel/Lumen, you may know a few packages. But after knowing about the tymon/jwt-auth I didn’t look for any other packages. For the last two production applications, I have been using this package. I use this package because of its extensibility. Moreover, it does all sorts of validation out of the box.

I had to go through the codebase several times to get the most out of it. I had implemented the following using the package.

  • Had implemented a cache on the subject.
  • Had implemented passwordless user authentication.
  • Had implemented a storage-less authentication system for a gateway.

After deploying the application in the production, I get to find an error in the log. It was shooting Tymon\JWTAuth\Exceptions\TokenExpiredException. I had to spend a good portion of time to check what is going on actually. After a thorough investigation, I found that it’s actually causing an error due to the TTL values. The user’s token had a validity of 2 months but the refresh token had a validity of 14 days. 😐

There are two types of JWT_*TTL.

  • JWT_TTL — which defines the expiry for the token. It’s being added to the token. After the expiry, the token will no longer work.
  • JWT_REFRESH_TTL — which denotes the amount of time slot the token has within which it should be refreshed.

How the authentication works and the TTL has an effect?

Upon setting up the jwt-auth successfully, when calling the auth()’s check() or user() methods, it checks in the following order.

So, what is wrong here?

So, when you’re trying to make a request with an invalid non-jwt token or not token with that, it’ll be blocked in the 3rd–5th step of the list. And when your token is a valid JWT token, then in the final steps, when checking one by one, then

If any of these timestamps are not found as expected or unavailable, then the token becomes invalid. So, when requesting with JWT token, these values must be available and should be exactly as said above.

Where is the JWT_REFRESH_TTL?

According to their documentation, the auth()->refresh() should be behind the auth middleware. That means whenever you need to refresh a token, you should pass a valid JWT token which will be parsed and should have valid iat nbf and exp values. And, if you call the auth()->refresh() method, it’ll set refresh flow and tries to generate a new token. Like before, it now tries to validate the payload with validateRefresh method instead of validatePayload. Each claim implements the method. So, when refreshing a token, the iat uses the refresh ttl value. So, when refreshing a token, your token generation time (iat) + refresh_ttl (in seconds) should be in the future. If it’s in the past, it’ll then throw TokenExpiredException. But if it’s in the future, you’ll get a new token.

BUT THE CATCH!!!

Whenever generating a new token, calling the *->refresh() then the newly issued token still has the previous iat value. So, at a certain point, your endpoint to refresh a token will raise a TokenExpiredException. Even if you set your JWT_TTL lower than the JWT_REFRESH_TTL, and as your token’s iat remains constant, the iat + refresh_ttl value will always be in the past after a certain period of time.

So, what to do then?

Whenever you need to refresh a token, generate a new token for the user like

$token = auth()->fromUser(auth()->user());

And, if you want to invalid the current token, then you can use

auth()->invalidate(); // auth()->invalidate(true);

So, you’ll get a new token with a new iat and you’ll invalidate the currently available token with the invalidate method before the exp value. And return the newly generated token as a response.

That’s all. Happy coding. ❤

--

--

Syed Sirajul Islam Anik

software engineer with "Senior" tag | procrastinator | programmer | !polyglot | What else 🙄 — Open to Remote