Laravel JWT令牌是无效的刷新他们在一个认证JWT方法之后,令牌、方法、Laravel、JWT

2023-09-13 03:20:03 作者:淡陌

编辑:

在阅读有关的bug的讨论: https://github.com/tymondesigns/智威汤逊-AUTH /问题/ 83

Read the discussion about the bug at: https://github.com/tymondesigns/jwt-auth/issues/83

我原来的问题:

我与那些需要用波纹管已验证的用户智威汤逊-auth的我受保护的资源实施code:

I'm implement with jwt-auth my protected resources that require an authenticated user with bellow code:

Route::group(['middleware' => ['before' => 'jwt.auth', 'after' => 'jwt.refresh']], function() {
    // Protected routes
});

当在API授权令牌创建,并在响应Authorization头发送到调用资源客户端应用用户登录。因此,客户端应用程序时,拦截任何响应的头中的授权令牌,设置一个变量/会话/不管这个令牌值,再发送到API的一个请求。

When user 'sign in' on API an Authorization token is created, and sent on response Authorization header to client application that call the resource. So, client applications when intercept a Authorization token on header of any response, set a variable/session/whatever with this token value, to send again to API on next request.

有关登陆后,受保护的资源的第一个请求工作正常,但接下来的客户端应用程序要求使用刷新令牌的API,提供了以下错误(API安装JSON格式的所有响应):

The first request for a protected resource after 'login' works fine, but the next client application request to API with a refreshed token, gives the following error (API mount all responses in json format):

{
    "error": "token_invalid"
}

有什么可以用刷新令牌发生的呢?我刷新令牌实现(设置为中间件之后)是错的?或者是不需要手动刷新所有授权令牌来与客户端应用程序的请求?

What can be happen with refreshed tokens? My refresh token implementation (set as a after middleware) is wrong? Or isn't necessary to manually refresh all Authorization token that come with client apps requests?

更新:

我更新JWT-auth的RefreshToken中间件在这里提出 ,但 token_invalid 持续。

I update the jwt-auth RefreshToken middleware as propose here, but the token_invalid persist.

BUG:

我猜,我发现发生了什么。请注意,在刷新方法,旧令牌添加到黑名单缓存启用情况:

I guess that I found what happens. Note that in the refresh method, old token is added to blacklist cache case enabled:

// Tymon\JWTAuth\JWTManager
public function refresh(Token $token)
{
    $payload = $this->decode($token);

    if ($this->blacklistEnabled) {
        // invalidate old token
        $this->blacklist->add($payload);
    }

    // return the new token
    return $this->encode(
        $this->payloadFactory->setRefreshFlow()->make([
            'sub' => $payload['sub'],
            'iat' => $payload['iat']
        ])
    );
}

和注意,列入到黑名单的方法,关键是从旧令牌有效载荷的JTI参数:

And note that in add to blacklist method the key is the jti param from old token payload:

// Tymon\JWTAuth\Blacklist
public function add(Payload $payload)
{
    $exp = Utils::timestamp($payload['exp']);

    // there is no need to add the token to the blacklist
    // if the token has already expired
    if ($exp->isPast()) {
        return false;
    }

    // add a minute to abate potential overlap
    $minutes = $exp->diffInMinutes(Utils::now()->subMinute());

    $this->storage->add($payload['jti'], [], $minutes);

    return true;
}

因此​​,对黑名单方法被调用时,旧的令牌JTI参数是一样的,新的,所以新的令牌是黑名单:

Thus, when has on blacklist method is called, the old token jti param is the same that the new, so the new token is in blacklist:

// Tymon\JWTAuth\Blacklist
public function has(Payload $payload)
{
    return $this->storage->has($payload['jti']);
}

如果您不需要只是设置为false jwt.php配置文件黑名单功能。但是,如果它暴露了一些安全漏洞,我不能说。

If you don't need the blacklist functionality just set to false on jwt.php configuration file. But I can't say if it expose to some security vulnerability.

在阅读有关的bug的讨论: https://github.com/tymondesigns/智威汤逊-AUTH /问题/ 83

Read the discussion about the bug at: https://github.com/tymondesigns/jwt-auth/issues/83

推荐答案

当我得到这个问题,我发现让我的项目的工作是从每个新的请求年长的令牌生成数据的新令牌的解决方案。

When I get this issue, the solution that I found to get my project working was to generate a new token with data from older token on each new request.

我的解决方案,这对我的作品,是坏的,丑陋的,如果你有很多异步请求,并能产生更多的问题,你的API(或业务的核心)的服务器是缓慢的。

My solution, that works for me, is bad, ugly, and can generate more issues if you have many async requests and your API(or business core) server is slow.

有关现在的工作,但我会调查更多的这个问题,原因之后0.5.3版本的问题仍然存在。

For now is working, but I will investigate more this issue, cause after 0.5.3 version the issue continues.

例如:

1请求(GET /登录):

Request 1 (GET /login):

Some guest data on token

2请求(POST /登录响应):

Request 2 (POST /login response):

User data merged with guest data on old token generating a new token

程序code例子(你可以做的更好=)),你可以在routes.php文件出的路线运行这个,我说是丑陋哈哈:

Procedural code example(you can do better =) ), you can run this on routes.php out of routes, I say that is ugly haha:

// ----------------------------------------------------------------
// AUTH TOKEN WORK
// ----------------------------------------------------------------
$authToken = null;
$getAuthToken = function() use ($authToken, $Response) {
    if($authToken === null) {
         $authToken = JWTAuth::parseToken();
    }
    return $authToken;
};

$getLoggedUser = function() use ($getAuthToken) {
    return $getAuthToken()->authenticate();
};

$getAuthPayload = function() use ($getAuthToken) {
    try {
        return $getAuthToken()->getPayload();
    } catch (Exception $e) {
        return [];
    }
};

$mountAuthPayload = function($customPayload) use ($getLoggedUser, $getAuthPayload) {
    $currentPayload = [];
    try {
        $currentAuthPayload = $getAuthPayload();
        if(count($currentAuthPayload)) {
            $currentPayload = $currentAuthPayload->toArray();
        }
        try {
            if($user = $getLoggedUser()) {
                $currentPayload['user'] = $user;
            }
            $currentPayload['isGuest'] = false;
        } catch (Exception $e) {
            // is guest
        }
    } catch(Exception $e) {
        // Impossible to parse token
    }

    foreach ($customPayload as $key => $value) {
        $currentPayload[$key] = $value;
    }

    return $currentPayload;
};

// ----------------------------------------------------------------
// AUTH TOKEN PAYLOAD
// ----------------------------------------------------------------
try {
    $getLoggedUser();
    $payload = ['isGuest' => false];
} catch (Exception $e) {
    $payload = ['isGuest' => true];
}

try {
    $payload = $mountAuthPayload($payload);
} catch (Exception $e) {
    // Make nothing cause token is invalid, expired, etc., or not exists.
    // Like a guest session. Create a token without user data.
}

有些航线(简单的例子,以节省用户的移动设备):

Some route(simple example to save user mobile device):

Route::group(['middleware' => ['before' => 'jwt.auth', 'after' => 'jwt.refresh']], function () use ($getLoggedUser, $mountAuthPayload) {
    Route::post('/session/device', function () use ($Response, $getLoggedUser, $mountAuthPayload) {
        $Response = new \Illuminate\Http\Response();
        $user = $getLoggedUser();

        // code to save on database the user device from current "session"...

        $payload = app('tymon.jwt.payload.factory')->make($mountAuthPayload(['device' => $user->device->last()->toArray()]));
        $token = JWTAuth::encode($payload);
        $Response->header('Authorization', 'Bearer ' . $token);

        $responseContent = ['setted' => 'true'];

        $Response->setContent($responseContent);
        return $Response;
    });
});