Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

cloughax's avatar

How to Refresh JWT Token

Hi im building a api with laravel 5, using "tymon/jwt-auth" library . Currently im issuing the token after the user authenticate themselves at login. The problem is the token expires after 60 min and i was wondering how could i refresh the token after the user get new requests from the server. This is an issue because i have a lot of async calls to my api so if i refresh a token while the next async call is in progress wouldn't it invalidate those request token.

Please help because im finding it hard to find resource on this topic.

0 likes
29 replies
osteel's avatar

It should be fine as the middleware will accept requests with the token for an extra minute after it is blacklisted, precisely to avoid simultaneous async requests issues (you can see this in the code).

To refresh the token, you can use the built-in middleware (RefreshToken, as shown in the documentation here). As long as a request is sent with a token within its ttl, the token will be accepted, then blacklisted, and a new token will be generated with a new ttl and passed to the response as a Authorization header, to be used along with the next request to the API, and so on.

2 likes
osteel's avatar

What I am confused about however is the refresh_ttl bit. I understand that a token having passed its ttl (expired) but still within its refresh_ttl should still be refreshable, but I am confused about whether this should be done by the middleware (it doesn't seem to be the case) or if the TokenExpiredException should be caught so we can then check if the token is still refreshable and manually call JWTAuth::refresh and pass the new token to the response ourselves.

Any opinion on this would be greatly appreciated!

1 like
skcin7's avatar

@cloughax, I am working on the same as you at the moment: Laravel 5, using "tymon/jwt-auth" library, and I am using AngularJS on the front-end.

JWTAuth comes with a built in middleware called RefreshToken. I access this by adding the following code into the constructor of one of my Controllers: $this->middleware('jwt.refresh');. This refreshes the token when the request is made. However, I haven't quite figured out a good way to return the refreshed token to the user, or how to have AngularJS receive the refreshed token. This is what I am working on now.

AlsonicTech's avatar

@skcin7 I am refreshing the token every each request. First you must get the initial token by using parseToken() and then use refresh() to generate a new one.

$newToken = JWTAuth::parseToken()->refresh();

skcin7's avatar

@AlexRo, you would also need to implement something in your front-end in order to receive the newly generated token as well, correct? Otherwise I would think that any new requests to the server with an old token would fail validation.

AlsonicTech's avatar

@skcin7 I am using the AWT Token auth for an API. After the users logs in, it receive the token. After every request on the server, the token is refreshed (a new token is generated and sent to the user).

I think that you can find a good explanation here: http://stackoverflow.com/questions/26739167/jwt-json-web-token-automatic-prolongation-of-expiration

"A good pattern is to refresh the token before it expires.

Set the token expiration to one week and refresh the token every time the user open the web application and every one hour. If a user doesn't open the application for more than a week, they will have to login again and this is acceptable web application UX.

To refresh the token your api needs a new endpoint that receives a valid, not expired JWT and returns the same signed JWT with the new expiration field. Then the web application will store the token somewhere."

2 likes
skcin7's avatar

@AlexRo Okay, so when the server refreshes the token, is the only value that is changed inside the token itself the 'exp' value (which holds the UNIX Timestamp expiration date), and the rest of the token is the same? Is that how it works? If so, I'm assuming that this would change the JWT's verification signature, to prevent any hacks?

And also, how is the newly generated token returned back to the client? I'm using the tymon/jwt-auth library to refresh the token, and it refreshes fine, but when I look at the HTTP request, the newly generated token doesn't seem to be sent back to the client. I even am checking in the HTTP Response Headers, and the refreshed token doesn't seem to be there.

For front-end, I'm presently storing the JWT inside my application's localStorage, and the JWT is automatically sent to the server with every request as part of the 'Authorization' field of the Request Headers, so that the server can know what user (if any) is logged in. So I'm assuming I would need to have my front-end replace this JWT in localStorage with the newly generated one each time it refreshes, right?

jimmck's avatar

Well either I don't understand something. But I made a change to Middleware for Tymon.

<?php

namespace Tymon\JWTAuth\Middleware;

use Tymon\JWTAuth\Exceptions\JWTException;
use Tymon\JWTAuth\Exceptions\TokenExpiredException;

class GetUserFromToken extends BaseMiddleware
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, \Closure $next)
    {
        $expired = false;

        if (! $token = $this->auth->setRequest($request)->getToken()) {
            return $this->respond('tymon.jwt.absent', 'token_not_provided', 400);
        }

        try {
            $user = $this->auth->authenticate($token);
        } catch (TokenExpiredException $e) {
            //return $this->respond('tymon.jwt.expired', 'token_expired', $e->getStatusCode(), [$e]);
            $expired = true;
        } catch (JWTException $e) {
            return $this->respond('tymon.jwt.invalid', 'token_invalid', $e->getStatusCode(), [$e]);
        }

        if ($expired) {
            try {
                $newToken = $this->auth->setRequest($request)
                  ->parseToken()
                  ->refresh();
                $user = $this->auth->authenticate($newToken);
            } catch (TokenExpiredException $e) {
                return $this->respond('tymon.jwt.expired', 'token_expired', $e->getStatusCode(), [$e]);
            } catch (JWTException $e) {
                return $this->respond('tymon.jwt.invalid', 'token_invalid', $e->getStatusCode(), [$e]);
            }
            // send the refreshed token back to the client
            $request->headers->set('Authorization', 'Bearer ' . $newToken);
        }

        if (! $user) {
            return $this->respond('tymon.jwt.user_not_found', 'user_not_found', 404);
        }

        $this->events->fire('tymon.jwt.valid', $user);

        return $next($request);
    }
}

I have a test Route to refresh that PIA CSRF Token...

    Route::get('token', ['middleware' => 'jwt.auth', function () {
        //response()->myMessage(csrf_token());
        $token = JWTAuth::getToken();

        return (new Response(csrf_token()))->header('Authorization', 'Bearer ' . $token->get());

        //return new Response(csrf_token());
    }]);

 
1 like
jimmck's avatar

JWT stays fresh and clean... But Token string nevers seems to change??? Starting to play with custom claims.

osteel's avatar

@skcin7

And also, how is the newly generated token returned back to the client? I'm using the tymon/jwt-auth library to refresh the token, and it refreshes fine, but when I look at the HTTP request, the newly generated token doesn't seem to be sent back to the client. I even am checking in the HTTP Response Headers, and the refreshed token doesn't seem to be there.

It is returned as the Authorization header of the response (take a look at Tymon\JWTAuth\Middleware\RefreshToken). My bet is you can't see it in your Angular app because you also need to expose the Authorization header setting the Access-Control-Expose-Headers beforehand. You can probably do that using a CORS package.

For front-end, I'm presently storing the JWT inside my application's localStorage, and the JWT is automatically sent to the server with every request as part of the 'Authorization' field of the Request Headers, so that the server can know what user (if any) is logged in. So I'm assuming I would need to have my front-end replace this JWT in localStorage with the newly generated one each time it refreshes, right?

Absolutely. You need interceptors for both requests and responses (I assume you already did the former). The request interceptor retrieves the token from localStorage and set the Authorization header. The response interceptor checks if there's an Authorization header set and replaces the value of the token in localStorage by the new one.

I don't have much time to go into details now but let me know if you need further explanation.

3 likes
mcblum's avatar

I'm in the same spot with this but my issue is that I'm already using CORS and have added 'Authorization' as an exposed header, but it's not. My JS app can't see it. Any idea what to try next?

1 like
ayekoto's avatar

@mcblum can u show me how u handle the cors request in your laracel back end. Having issues with this for awhile now

cloughax's avatar
cloughax
OP
Best Answer
Level 1

I should have probably update the progress that I had made with issue just in case anyone is still experiencing issues with jwt and laravel. I would also like thank you guys for contributing to this discussion as It was all very helpful. While trying to build my api I found a few cool libraries that helped a lot with bootstraping my api on the server side was Dingo Api. Here is the link for their github. Read the docs it does a lot.

The following are some small snippet to demonstrate the implementation im using to refresh my jwt tokens.

Route definition

$api = app('Dingo\Api\Routing\Router');
$api->version('v1', function($api){
    $api->get('token', 'App\Http\Controllers\AuthController@token');
}); 

Controller function that actually refreshes the token

//AuthController
public function token(){
    $token = JWTAuth::getToken();
    if(!$token){
        throw new BadRequestHtttpException('Token not provided');
    }
    try{
        $token = JWTAuth::refresh($token);
    }catch(TokenInvalidException $e){
        throw new AccessDeniedHttpException('The token is invalid');
    }
    return $this->response->withArray(['token'=>$token]);
}

For my client im using angularjs, so to automatically refreshes itself with the following code. Im using the angular-jwt library

angular.module('myapp',['angular-jwt','angular-storage'])
.config(['jwtInterceptorProvider', function(jwtInterceptorProvider){
    jwtInterceptorProvider.tokenGetter = function(jwtHelper, $http,store) {
        var jwt = store.get('jwt');
        if(jwt){
            if(jwtHelper.isTokenExpired(jwt)){              
                return $http({
                    url : 'api/token',
                    skipAuthorization : true,
                    method: 'GET',
                    headers : { Authorization : 'Bearer '+ jwt},
                }).then(function(response){
                    store.set('jwt',response.data.token);
                    return response.data.token;
                },function(response){
                    store.remove('jwt');                    
                });
            }else{
                return jwt; 
            }
        }               
    }
    $httpProvider.interceptors.push('jwtInterceptor');
}]);

Im also using restangular to communicate with my backend. The app is not completed as there are a few things I might change about the the whole jwt auth process. For example I want to track the if a user is logged in and invalidate tokens if the user should reauthenticate themself. But for the time being this is what im using. I spent too much time on this part of the application and I am now currently working on the core parts of my application.

6 likes
mcblum's avatar

@ayekoto I actually figured out the initial problem which was that I, stupidly, had the headers exposed correctly but didn't realize that in my Angular interceptor I was trying to access the header like this:

var new_key         = success.headers.Authorization;

Instead of

var new_key         = success.headers('Authorization');

Once I made the change it works but currently I'm having the issue where the very second the token is refreshed, it's invalid, so my async requests are failing. Using Tymons/JWT-Auth like pretty much everyone else :)

shanecp's avatar

@cloughax After spending a couple of days on this, finally your post helped me to solve the refresh token issue. I've noticed there's still a little issue when returning the promise back to tokenGetter() (in Angular).

If the refresh_ttl is expired on server, it'll return an error response. What happens now is the original request will still be called without a new refresh token. Which will obviously fail because there isn't a new token. I think it would be better if the original request can be terminated (and force the user to login again), if that promise fails.

cloughax's avatar

Ok guys i ran into some issues with blacklisting of tokens with the previous implementation i had posted, so just in case there are persons out there that might need some help here is what I did to solve the problem, but first let me explain the issue.

Blacklisting is a feature that allows you to invalidate tokens. So in my case when I refreshed the tokens with the jwt.refresh middleware the previous token was invalidated. This highlighted a concept about tymon/jwt-auth framework I didn't fully grasp. The token was actually refreshed through the middleware so I didn't need to try and refresh the token again inside the controller (unless there might be some other feature I want to implement otherwise from refreshing the token). Also the response header for that request (in my case auth/token would contain the new token. All you need to do is splice it out if you want the token.

This now brings me to another issue with my previous implementation. In angular when the token expires several asyn calls to refresh the token route would be made for each request that failed. This would try to refresh the same expired token more than once. With blacklisting turned on this would fail. The first token that was refreshed would be valid and all the other asyn calls would fail. What I did was if any of the other asyn calls failed while trying to refresh a token check again if the token in localstorage really expired, then kick the person out of the system by otherwise return the token.

For those who are like me and want to see code here you go:

// Angular jwt interceptor. This handles how  my tokens are refreshed
jwtInterceptorProvider.tokenGetter = function(jwtHelper, $http,store) {
        var jwt = store.get('jwt');
        if(jwt){
            if(jwtHelper.isTokenExpired(jwt)){              
                return $http({
                    url : 'api/token',
                    skipAuthorization : true,
                    method: 'GET',
                    headers : { Authorization : 'Bearer '+ jwt},
                }).then(function(response){
                    store.set('jwt',response.headers('Authorization').split(" ")[1]);
                    return store.get('jwt');
                },function(response){
                    jwt = store.get('jwt');
                    if(!jwtHelper.isTokenExpired(jwt)){ 
                        return store.get('jwt');
                    }
                });
            }else{
                return jwt; 
            }
        }               
    }
//My AuthController 
public function __construct(){
       $this->middleware('jwt.refresh',['only'=>['token']]);
    }
public function token(){
        
    }

I hope this helps someone. I know how frustrating it can be to understand this especially when the resource that are out there dont go into details with code and examples to explain the issue properly.

cloughax's avatar

If you want more fine grain control by refreshing the yourself you could also remove the jwt.refresh middleware and refresh the token yourself. I am opting for this approach as it more cleaner to parse on the angular side and it express what it is doing more clearly.

public function refresh(){
        $current_token  = JWTAuth::getToken();
        $token          = JWTAuth::refresh($current_token);

        return $this->response->array(compact('token'));
    }
3 likes
r0bin51's avatar

I have a similar issue. I'm using my laravel api using jwt token for my mobile app.

everytime i make a request, I refresh the token: Route::group(['middleware' => ['jwt.auth', 'jwt.refresh']], function() {

but after 60 min the token expire and i cannot extend it, or refresh. I read there should be another token ("refresh token") stored in the client, but reading your post I think you found a solution without using it... or am I wrong?

Can you please explain me quicly how to proceed?

thanks a lot!

baobazamazing's avatar

I use this technique " $current_token = JWTAuth::getToken(); $token = JWTAuth::refresh($current_token);

    return $this->response->array(compact('token'));

", but i have error "ErrorException in PayloadFactory.php line 194: Array to string conversion "

rsteinmann's avatar

@cloughax Thanks for your explanations on this. It really helped me out. JWT is a not so easy to get concept and now I finally have it integrated in my backend and my frontend (angular). Your last approach is what I've also done as concept and it works fine.

francis.rod01's avatar

With upgrading from laravel to 5.3, there was an @tymondesigns jwt-auth update, so a call to JWTAuth :: getToken () does not exist as a static method. I would like to know how to solve this.

francis.rod01's avatar

I think that setRequest()->parseToken()->refresh() not change raw token.

$newToken = $this->auth->setRequest($request)
                  ->parseToken()
                  ->refresh();

The new token is the same that old token.

gandra's avatar

What is the meaning to refresh token on each request? Why, in such case, you do not simply increase TTL fo token? I do not get benefit of refreshing token on each request versus increasing TTL?

coustas's avatar

Hello in which file you did the change angular.module ? app.js or config.router???

thiyagu's avatar

Any implemented document for Lumen + JWT + Access token and refresh token process for user authendication.

YaduRvt's avatar

Please find my middle ware . i use this middle ware for all the api requests. Its working.

public function handle($request, Closure $next)
    {
        $success_status = 200;
        try{
            $user = JWTAuth::toUser($request->header('Authorization'));
        }catch (JWTException $e) {
            if($e instanceof \Tymon\JWTAuth\Exceptions\TokenExpiredException || $e instanceof \Tymon\JWTAuth\Exceptions\TokenBlacklistedException) {
                try {
                    $new_token  = JWTAuth::refresh($request->header('Authorization'));
                    $status     = 200;
                    // set new token in to the request header
                    $request->headers->set('Authorization',$new_token);
                    $response          = $next($request);
                    $original          = $response->getOriginalContent();
                    $original['token'] = $new_token;
                    // set response - token as a common parameter
                    $response->setContent(json_encode($original));
                    return $response;
                } catch (TokenExpiredException $e) {
                    $status     = 401;
                    $message    = 'Please Login Again. Your Session Timed Out';
                    return response()->json(compact('status','message'),$success_status);
                }
                catch (JWTException $e) {
                    $status     = 401;
                    $message    = 'Please Login Again. Refresh token time expired';
                    return response()->json(compact('status','message'),$success_status);
                }          
            }else if ($e instanceof \Tymon\JWTAuth\Exceptions\TokenInvalidException) {
                $status     = 401;
                $message    = 'This token is invalid. Please Login';
                return response()->json(compact('status','message'),$success_status);
            }else{
                $status     = 404;
                $message    = 'Token is required';
                return response()->json(compact('status','message'),$success_status);
                //return response()->json(['error'=>'Token is required']);
            }
        }
       return $next($request);
    }

Please or to participate in this conversation.