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):
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.
Maybe you're using async requests? Then, you aren't sure if you're sending the most fresh token.
In this case you shouldn't use the refresh method after every request.
This was indeed a bug of the library, corrected now, read here for more informations