JSON Web Token e Laravel – Parte 3 – Uso del Package

1
860

Ci siamo: dopo le procedure di installazione e configurazione possiamo usare jwt-auth per gestire in modo adeguato i nostri JSON Web Token.

Questo ultimo articolo coprirà, fondamentalmente, due momenti specifici dell’uso del package:

  • rilascio di un token al momento dell’accesso;
  • controllo di un token inviato in una richiesta da parte dell’utente;

Noterai come jwt-auth risolve, ed anche in modo piuttosto semplice, la questione con una sintassi semplice ed intuitiva, in linea con il “Laravel-pensiero”.

Generazione dei Token

Il package jwt-auth, come già detto, è molto semplice da usare ma non può comunque voltare le spalle a chi ha bisogno di più flessibilità e possibilità di personalizzazione. Per questo motivo, il package offre svariati modi di generare dei token, dai più semplici ai più precisi.

Claim Inseriti nel Token

Di default, il package inserisce nei token una serie ben definita di claim. Nello specifico, i seguenti:

  • sub Subject (Oggetto) – serve a memorizzare un identificativo specifico (ad esempio, l’id utente);
  • iat Issued At (Rilasciato il) – specifica quando il token è stato rilasciato (è un unix timestamp);
  • exp Expiry (Scade il) – indica il momento di scadenza del token (è un unix timestamp);
  • nbf Not Before (Non prima del) – indica da che momento il token viene considerato valido, e quindi può essere usato senza problemi (è un unix timestamp);
  • iss Issuer (Emittente) – indica chi è che ha emesso il token (di default è l’url della richiesta);
  • jti JWT Id – identificatore unico del tokne (sarebbe l’hash MD5 dei claim sub e iat);
  • aud Audience – indica a chi è destinato questo token (non è un campo richiesto ed obbligatorio di default);

Rilascio del Token

In Laravel 5 la procedura di rilascio del token è davvero molto semplice. Supponiamo di avere due metodi in un controller dedicato all’autenticazione: uno di questi risponde in GET e mostra un form di login classico. L’altro, POST, si occupa di elaborare la richiesta. Analizziamo, in particolare, l’eventuale struttura del metodo POST che riceve le credenziali di accesso.

use JWTAuth;
use TymonJWTAuthExceptionsJWTException;

class AuthenticateController extends Controller
{
public function authenticate(Request $request)
{
// prende dalla richiesta le credenziali di accesso
$credentials = $request->only(‘email’, ‘password’);

try {
// verifica le credenziali…
if (! $token = JWTAuth::attempt($credentials)) {

// qualcosa è andato storto nella verifica delle credenziali
return response()->json([‘error’ => ‘invalid_credentials’], 401);

}
} catch (JWTException $e) {

// qualcosa è andato storto in fase di codifica del token
return response()->json([‘error’ => ‘could_not_create_token’], 500);

}

// … se tutto va bene il token viene rilasciato!
return response()->json(compact(‘token’));
}
}

Come puoi notare, la facade JWTAuth va a sostituire quella che conosciamo già, del sistema di autenticazione.

Vengono inoltre gestiti due tipi di errori:

  • errore in fase di autenticazione, nel caso in cui le credenziali siano errate;
  • errore in fase di codifica del token (ed in tal caso il codice viene inserito in un blocco try/catch);

L’eccezione JWTException è stata creata per lo scopo.

Rilascio del Token per un Utente Specifico

Il rilascio tramite controllo delle credenziali, tuttavia, non è l’unico previsto dal package. Può capitare infatti di dover emettere un token per uno specifico utente, partendo magari da un’istanza del model User. In tal caso si può procedere così:

// prendo l’utente di cui ho bisogno…
$user = User::findOrFail($userId);

$token = JWTAuth::fromUser($user);

Se preferisci, puoi anche personalizzare i claim presenti nel token passando, come secondo parametro, un array. Sia che si parli di accesso tramite il metodo attempt che tramite fromUser.

$customClaims = [‘foo’ => ‘bar’, ‘baz’ => ‘bob’];

JWTAuth::attempt($credentials, $customClaims);
// oppure…
JWTAuth::fromUser($user, $customClaims);

Creare Token da Zero

Inoltre, puoi anche generare un token senza dover necessariamente avere a che fare con un utente ed il suo accesso. Insomma, può capitare.

Se è questo il tuo caso, il metodo JWTAuth::encode è quello che ti serve.

// tramite array definito precedentemente…
$customClaims = [‘foo’ => ‘bar’, ‘baz’ => ‘bob’];
$payload = JWTFactory::make($customClaims);
$token = JWTAuth::encode($payload);

// oppure tramite metodi concatenati…
$payload = JWTFactory::sub(123)->aud(‘foo’)->foo([‘bar’ => ‘baz’])->make();
$token = JWTAuth::encode($payload);

Come puoi notare, il metodo JWTFactory::make si occupa di preparare i claim per il payload. Il metodo JWTAuth::encode di tutto il resto.

Nota: ne ho già parlato in precedenza ma mi ripeto. Se vuoi usare JWTFactory ricorda di aggiungere agli alias la facade, come descritto nella procedura di installazione.

A questo punto, il token generato e ritornato può essere salvato nell’applicazione client che la richiede.

Autenticazione e Riconoscimento

Siamo quasi alla fine: non rimane altro, infatti, che scoprire come implementare nella nostra applicazione un controllo adeguato del token per “proteggere” le risorse che dobbiamo tenere al sicuro. O comunque, in generale, capire che strumenti il package offre per rimanere protetti.

Come Passare il Token al Server

Il primo step è capire come passare un token in modo adeguato, in maniera tale che jwt-auth lo riconosca e possa lavorarci. Siamo infatti nella seconda fase, in cui il client passa il token precedentemente salvato al server, per ottenere un accesso.

Si può passare un token in due modi.

Il primo è nell’header della richiesta, così:

Authorization: Bearer {qui_va_il_token}

… niente di più: devi solo impostare un header “Authorization” a cui alleghi il token.

Se non ti piace lavorare con gli header puoi sempre usare il corpo della richiesta, aggiungendo un elemento token alla query string:

http://api.miosito.dev/endpoint?token={qui_va_il_token}

Il Metodo parseToken

Se hai passato in uno dei due modi appena visti il tuo token al server, a questo punto è davvero tutto in discesa.

Innanzitutto, hai a disposizione il metodo parseToken di JWTAuth per recuperarlo.

$myToken = JWTAuth::parseToken();

Si tratta di un metodo più “grezzo”, nel senso che ti permette di lavorare con il token ed esaminarlo adeguatamente. In alternativa puoi anche usare JWTAuth::getToken.

Ad ogni modo, non useremo spesso questi due metodi direttamente. Vediamo adesso come “recuperare” i dati dell’utente loggato partendo dal token.

Il metodo authenticate

Il metodo authenticate ci permette di verificare in modo più veloce se un utente è effettivamente entrato nel sistema, oppure no. Niente operazioni complesse e verifiche sul token: pensa a tutto il package.

Ecco un esempio di codice di verifica, da mettere ad esempio in un metodo di un controller:

public function getAuthenticatedUser()
{
try {

if (! $user = JWTAuth::parseToken()->authenticate()) {

// errore in caso di utete non trovato
return response()->json([‘user_not_found’], 404);
}

} catch (TymonJWTAuthExceptionsTokenExpiredException $e) {

// errore in caso di token scaduto
return response()->json([‘token_expired’], $e->getStatusCode());

} catch (TymonJWTAuthExceptionsTokenInvalidException $e) {

// errore in caso di token non valido
return response()->json([‘token_invalid’], $e->getStatusCode());

} catch (TymonJWTAuthExceptionsJWTException $e) {

// errore in caso di token non presente
return response()->json([‘token_absent’], $e->getStatusCode());

}

// il token è valido e l’utente esiste: ne vengono ritornati i dettagli
return response()->json(compact(‘user’));
}

Saltano all’occhio subito un paio di cose:

  • il metodo authenticate ritorna l’istanza dell’utente, pronta ad essere usata (un’istanza del model User, ad essere precisi);
  • il package JWTAuth mette a disposizioni svariate tipologie d’eccezione per contemplare ogni possibile caso;

Ad ogni modo, gli strumenti messi a disposizione dal package non finiscono qua… sarebbe piuttosto noioso in effetti dover fare un simile controllo per ogni metodo, vero?

Middleware!

Usando Laravel 5.*, lo sviluppatore ha a sua disposizione due middleware relativi ai token ed al loro controllo.

Registriamoli, innanzitutto, aggiungendoli all’array $routeMiddleware in app/Http/Kernel.php.

protected $routeMiddleware = [

‘jwt.auth’ => ‘TymonJWTAuthMiddlewareGetUserFromToken’,
‘jwt.refresh’ => ‘TymonJWTAuthMiddlewareRefreshToken’,
];

Il primo, GetUserFromToken, si occupa di controllare l’effettiva presenza di un token nella richiesta. Se il token non c’è, provvede a mostrare una risposta in output adeguata. In questo modo lo sviluppatore non deve preoccuparsi d’altro.

Il primo RefreshToken, aumenta il livello di sicurezza dell’applicazione. Ad ogni richiesta, infatti, genera un nuovo token a partire dal precedente e lo ritorna come parte della risposta. In questo modo le possibilità di un attacco andato a buon fine (nonostante i token siano già abbastanza sicuri) è ancora più difficile.

Sarai tu a scegliere che approccio usare.

Conclusioni

Siamo giunti alla fine: adesso dovresti avere un’idea più chiara riguardo il mondo JWT, il suo funzionamento e l’insieme di possibilità offerte dal package jwt-auth.

Per qualsiasi domanda e curiosità lascia pure un commento! Risponderò appena possibile.