Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

laravel / angularjs JWT token refresh

I am implementing JWT authentication in an angular/laravel application and I have a problem with token refresh.

Here the relevant code:

PHP: the laravel-jwt listener that 'listen' for tymon.jwt.expired event:

    /**
     * Fired when the token has expired
     * @param \Exception $e
     * @return \Illuminate\Http\JsonResponse
     */
    public function expired($e)
    {
        $token = \JWTAuth::parseToken();

        Config::package('tymon/jwt-auth', 'jwt');
        $ttl = Config::get('jwt::refresh_ttl');

        $iat = Carbon::createFromTimestamp($token->getPayload()->get('iat'));
        $now = Carbon::now();

        // if renew ttl is expired too, return 401, otherwise let
        // the application generate a new token to frontend
        if ($iat->diffInMinutes($now) >= $ttl) {
            unset($iat, $now, $ttl);
            return response_failure(
                Lang::get('errors.api.auth.expired'),
                Config::get('status.error.unauthorized')
            );
        }

        unset($iat, $now, $ttl);
    }

PHP: the 'after' filter:

/*
|--------------------------------------------------------------------------
| JWT-Auth token-refresh Filter
|--------------------------------------------------------------------------
|
| The RefreshToken filter update the response headers by returning an 
| updated authentication token.
|
*/
Route::filter('RefreshToken', function($route, $request, $response)
{
    $token = JWTAuth::parseToken();

    try {
        $token->toUser();
    } catch (TokenExpiredException $e) {
        Config::package('tymon/jwt-auth', 'jwt');
        $ttl = Config::get('jwt::refresh_ttl');

        $iat = \Carbon\Carbon::createFromTimestamp($token->getPayload()->get('iat'));
        $now = \Carbon\Carbon::now();

        if ($iat->diffInMinutes($now) < $ttl) {
            $response->headers->set('Authorization', 'Bearer ' . $token->refresh());
        }
    }
});

PHP: The authenticated routes filters:

Route::group(['before' => 'jwt-auth', 'after' => 'RefreshToken'], function () { ... });

JS: The interceptor that updates the localstorage

'use strict';

angular.module('App')

    .factory('ResponseInterceptor', ['SessionService', 'jwtHelper', '$location', '$q',
        function (SessionService, jwtHelper, $location, $q) {

            return {
                response: response
            };

            // called for http codes up to 300
            function response(response) {
                var token = response.headers('Authorization');
                if ('undefined' !== typeof token && null !== token) {
                    SessionService.setToken(token.split(' ')[1]);
                }
                return response;
            }
        }]);

This works well except for one problem (workflow):

  • token expires but is still able to be renewed
  • angular send an http request with the expired token to server
  • laravel catch the request and updates the response headers with a new token
  • laravel blacklist the previous token

The problem is that if any request is sent from angular during the "renewal" delay, all thoses requests are refused from the server because the token is invalid (blacklisted).

Am I doing things wrong ? Can someone point me in the right direction ?

What I'd like to achieve is setting the ttl of the token at about 5 minutes, and allow the user to renew the token while navigating.

like image 348
kitensei Avatar asked Nov 10 '22 17:11

kitensei


1 Answers

This was indeed a bug of the library, corrected now, read here for more informations

like image 126
kitensei Avatar answered Nov 14 '22 21:11

kitensei