2015-04-17 15 views
7

EDIT:tokeny laravel JWT są nieważne po odświeżyć je w podejściu uwierzytelniania JWT

zapoznać się z dyskusją na temat błędów w: https://github.com/tymondesigns/jwt-auth/issues/83

MY oryginalne pytanie:

jestem Implementacja z jwt-auth moich chronionych zasobów wymagających uwierzytelnionego użytkownika z kodem mieszanym:

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

Gdy użytkownik "loguje się" na interfejsie API, tworzony jest token autoryzacji i wysyłany w odpowiedzi na nagłówek autoryzacji do aplikacji klienckiej, która wywołuje zasób. Tak więc, aplikacje klienckie przechwytujące token autoryzacji w nagłówku dowolnej odpowiedzi, ustaw zmienną/sesję/cokolwiek z tą wartością tokena, aby ponownie wysłać do API przy następnym żądaniu.

Pierwszy wniosek do chronionego zasobu po „login” działa dobrze, ale następnego żądania aplikacji klienta API z odświeżonym tokena, daje następujący błąd (API zamontować wszystkie odpowiedzi w formacie JSON):

{ 
    "error": "token_invalid" 
} 

Co może się stać z odświeżonymi tokenami? Moja implementacja tokena odświeżania (ustawione jako pośrednie oprogramowanie pośrednie) jest niepoprawna? Czy też nie jest konieczne ręczne odświeżanie wszystkich tokenów autoryzacji, które pochodzą z żądaniami aplikacji klienckich?

UPDATE:

zaktualizować JWT-auth RefreshToken middleware jak zaproponować here, ale token_invalid utrzymywać.

BUG:

myślę, że znalazłem to, co się dzieje. Zauważ, że w sposobie odświeżania starych żeton zostanie dodany do listy zablokowanych przypadek cache włączona:

// 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'] 
     ]) 
    ); 
} 

i zauważ, że w dodatku do czarnej metodę kluczem jest param WIT ze starych tokena ładowności:

// 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; 
} 

tak więc, gdy jest na metodzie czarnej listy nazywa, stary żeton WIT param jest tym samym, że nowy, więc nowy żeton jest w czarnej liście:

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

Jeśli nie potrzebują funkcji czarnej listy po prostu ustaw na false na pliku konfiguracyjnym jwt.php. Ale nie mogę powiedzieć, czy naraża się na pewną lukę w zabezpieczeniach.

zapoznać się z dyskusją na temat błędów w: https://github.com/tymondesigns/jwt-auth/issues/83

+0

Czy Twój problem został rozwiązany w nowej wersji 0.5.3? Jak wspomniano przez autora, został on rozwiązany w najnowszej wersji. Ale wciąż mam ten sam problem jak twój. – davidcoder

+0

@davidcoder po znalezieniu tego problemu, piszę wrapper do jwt-auth, aby go rozwiązać, ponieważ potrzebowałem szybkiego rozwiązania. Zasadniczo zastępuję oprogramowanie pośrednie RefreshToken w pakiecie App \ Http \ Middleware. Ale przetestuję nową wersję, aby zweryfikować, co się dzieje. – Maykonn

+0

Mogę powiedzieć, że problem nadal trwa. Dziękuję bardzo za ten wgląd. Wyłączenie czarnej listy zadziałało –

Odpowiedz

3

Kiedy się ten problem, rozwiązanie, które znalazłem, aby mój projekt roboczy, aby wygenerować nowy token z danymi ze starszej tokena na każdy nowy wniosek.

Moje rozwiązanie, które działa dla mnie, jest złe, brzydkie i może generować więcej problemów, jeśli masz wiele żądań asynchronicznych, a serwer API (lub rdzeń biznesowy) działa wolno.

Na razie działa, ale zbadam więcej tego problemu, ponieważ po wersji 0.5.3 problem będzie nadal występował.

Np

Zapytanie 1 (GET/login):

Some guest data on token 

Zapytanie 2 (odpowiedź POST/login):

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

proceduralne przykład kodu (można to zrobić lepiej =)), możesz uruchomić to na routes.php z tras, mówię, że jest brzydko 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. 
} 

Niektóre trasy (prosty przykład, aby zapisać urządzenia mobilnego użytkownika):

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; 
    }); 
});