Prologo
Primi Passi
Architettura
Le Basi
- Routing
- Middleware
- Protezione da CSRF
- Controller
- Richieste
- Risposte
- Views
- Blade
- Vite
- URL
- Sessioni
- Validazione
- Errori
- Logging
Approfondimenti
- Artisan
- Broadcasting
- Cache
- Collezioni
- Concorrenza
- Contesto
- Contratti
- Eventi
- File System
- Helpers
- Client HTTP
- Localizzazione
- Notifiche
- Sviluppo di Package
- Processi
- Code
- Rate-limiting
- Stringhe
- Scheduling di Task
Sicurezza
Database
Eloquent ORM
Testing
Package
Passport
- Introduzione
- Installazione
- Configurazione
- Emissione dei Token di Accesso
- Authorization Code Grant con PKCE
- Token di Password Grant
- Token di Concessione Implicita
- Client Credentials Grant Tokens
- Token di accesso personali
- Proteggere le Rotte
- Ambiti dei Token
- Consumare la tua API con JavaScript
- Eventi
- Testing
Introduzione
Laravel Passport fornisce un’implementazione completa del server OAuth2 per la tua applicazione Laravel in pochi minuti. Passport è costruito sulla base del League OAuth2 server mantenuto da Andy Millington e Simon Hamp.
Questa documentazione presuppone che tu abbia già familiarità con OAuth2. Se non conosci nulla di OAuth2, considera di familiarizzare con la generale terminologia e le funzionalità di OAuth2 prima di continuare.
Passport o Sanctum?
Prima di iniziare, potresti voler determinare se la tua applicazione è meglio servita da Laravel Passport o da Laravel Sanctum. Se la tua applicazione ha assolutamente bisogno di supportare OAuth2, allora dovresti usare Laravel Passport.
Tuttavia, se stai cercando di autenticare un’applicazione a pagina singola, un’applicazione mobile o emettere API tokens, dovresti usare Laravel Sanctum. Laravel Sanctum non supporta OAuth2; tuttavia, offre un’esperienza di sviluppo per l’autenticazione API molto più semplice.
Installazione
Puoi installare Laravel Passport tramite il comando Artisan install:api
:
php artisan install:api --passport
Questo comando pubblicherà ed eseguirà le migrazioni del database necessarie per creare le tabelle di cui la tua applicazione ha bisogno per memorizzare i client OAuth2 e i token di accesso. Il comando creerà anche le chiavi di crittografia necessarie per generare token di accesso sicuri.
Inoltre, questo comando ti chiederà se desideri utilizzare UUID come valore chiave primaria del modello Client
di Passport invece di interi auto-incrementanti.
Dopo aver eseguito il comando install:api
, aggiungi il trait Laravel\Passport\HasApiTokens
al tuo modello App\Models\User
. Questo trait fornirà alcuni metodi di aiuto al tuo modello che ti permetteranno di ispezionare il token dell’utente autenticato e gli scope:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Passport\HasApiTokens;
class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable;
}
Infine, nel file di configurazione config/auth.php
della tua applicazione, dovresti definire una guardia di autenticazione api
e impostare l’opzione driver
su passport
. Questo istruirà la tua applicazione a usare il TokenGuard
di Passport quando autentica le richieste API in arrivo:
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'passport',
'provider' => 'users',
],
],
Distribuire Passport
Quando distribuisci Passport sui server della tua applicazione per la prima volta, probabilmente dovrai eseguire il comando passport:keys
. Questo comando genera le chiavi di crittografia necessarie a Passport per creare i token di accesso. Le chiavi generate solitamente non vengono mantenute nel controllo del codice sorgente:
php artisan passport:keys
Se necessario, puoi definire il percorso da cui devono essere caricate le chiavi di Passport. Puoi usare il metodo Passport::loadKeysFrom
per fare questo. Tipicamente, questo metodo dovrebbe essere chiamato dal metodo boot
della classe App\Providers\AppServiceProvider
della tua applicazione:
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Passport::loadKeysFrom(__DIR__.'/../secrets/oauth');
}
Caricamento delle Chiavi dall’Ambiente
In alternativa, puoi pubblicare il file di configurazione di Passport usando il comando vendor:publish
di Artisan:
php artisan vendor:publish --tag=passport-config
Dopo aver pubblicato il file di configurazione, puoi caricare le chiavi di crittografia della tua applicazione definendole come variabili d’ambiente:
PASSPORT_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----
<private key here>
-----END RSA PRIVATE KEY-----"
PASSPORT_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----
<public key here>
-----END PUBLIC KEY-----"
Aggiornamento di Passport
Quando aggiorni a una nuova versione principale di Passport, è importante esaminare attentamente la guida all’aggiornamento.
Configurazione
Hashing dei Segreti dei Client
Se vuoi che i segreti dei tuoi client siano hashati quando vengono memorizzati nel database, devi chiamare il metodo Passport::hashClientSecrets
nel metodo boot
della tua classe App\Providers\AppServiceProvider
:
use Laravel\Passport\Passport;
Passport::hashClientSecrets();
Una volta abilitato, tutti i segreti dei tuoi client saranno visibili solo all’utente subito dopo la loro creazione. Poiché il valore del segreto in testo semplice non viene mai memorizzato nel database, non è possibile recuperare il valore del segreto se viene perso.
Durata dei Token
Per impostazione predefinita, Passport emette token di accesso a lunga durata che scadono dopo un anno. Se desideri configurare una durata del token più lunga o più breve, puoi utilizzare i metodi tokensExpireIn
, refreshTokensExpireIn
e personalAccessTokensExpireIn
. Questi metodi devono essere chiamati dal metodo boot
della classe App\Providers\AppServiceProvider
della tua applicazione:
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Passport::tokensExpireIn(now()->addDays(15));
Passport::refreshTokensExpireIn(now()->addDays(30));
Passport::personalAccessTokensExpireIn(now()->addMonths(6));
}
Le colonne
expires_at
nelle tabelle del database di Passport sono di sola lettura e servono solo per la visualizzazione. Quando vengono emessi i token, Passport memorizza le informazioni di scadenza all’interno dei token firmati e criptati. Se hai bisogno di invalidare un token, dovresti revocarlo.
Sovrascrivere i Modelli Predefiniti
Puoi estendere i modelli utilizzati internamente da Passport definendo il tuo modello e estendendo il corrispondente modello di Passport:
use Laravel\Passport\Client as PassportClient;
class Client extends PassportClient
{
// ...
}
Dopo aver definito il tuo modello, puoi istruire Passport a utilizzare il tuo modello personalizzato tramite la classe Laravel\Passport\Passport
. Tipicamente, dovresti informare Passport dei tuoi modelli personalizzati nel metodo boot
della classe App\Providers\AppServiceProvider
della tua applicazione:
use App\Models\Passport\AuthCode;
use App\Models\Passport\Client;
use App\Models\Passport\PersonalAccessClient;
use App\Models\Passport\RefreshToken;
use App\Models\Passport\Token;
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Passport::useTokenModel(Token::class);
Passport::useRefreshTokenModel(RefreshToken::class);
Passport::useAuthCodeModel(AuthCode::class);
Passport::useClientModel(Client::class);
Passport::usePersonalAccessClientModel(PersonalAccessClient::class);
}
Sovrascrivere le Rotte
A volte potresti voler personalizzare le rotte definite da Passport. Per fare ciò, devi prima ignorare le rotte registrate da Passport aggiungendo Passport::ignoreRoutes
al metodo register
del AppServiceProvider
della tua applicazione:
use Laravel\Passport\Passport;
/**
* Registra i servizi dell'applicazione.
*/
public function register(): void
{
Passport::ignoreRoutes();
}
Successivamente, puoi copiare le rotte definite da Passport nel file delle rotte e inserirle nel file routes/web.php
della tua applicazione, modificandole a tuo piacimento:
Route::group([
'as' => 'passport.',
'prefix' => config('passport.path', 'oauth'),
'namespace' => '\Laravel\Passport\Http\Controllers',
], function () {
// Rotte di Passport...
});
Emissione dei Token di Accesso
Usare OAuth2 tramite codici di autorizzazione è il metodo con cui la maggior parte degli sviluppatori conosce OAuth2. Quando si usano i codici di autorizzazione, un’applicazione client reindirizza un utente al tuo server dove l’utente potrà approvare o rifiutare la richiesta di emissione di un token di accesso al client.
Gestione Client
Innanzitutto, gli sviluppatori che creano applicazioni che devono interagire con l’API della tua applicazione dovranno registrare la loro applicazione con la tua creando un "client". Solitamente, questo comporta fornire il nome della loro applicazione e un URL a cui la tua applicazione può reindirizzare dopo che gli utenti hanno approvato la loro richiesta di autorizzazione.
Il comando passport:client
Il modo più semplice per creare un client è usare il comando Artisan passport:client
. Questo comando ti permette di creare i tuoi client per testare le funzionalità OAuth2. Quando esegui il comando client
, Passport ti chiederà ulteriori informazioni sul tuo client e ti fornirà un ID client e un secret:
php artisan passport:client
URL di reindirizzamento
Se vuoi consentire più URL di reindirizzamento per il tuo client, puoi elencarli separati da virgole quando ti viene chiesto l’URL dal comando passport:client
. Gli URL che contengono virgole devono essere codificati:
http://example.com/callback,http://examplefoo.com/callback
JSON API
Poiché gli utenti della tua applicazione non potranno utilizzare il comando client
, Passport fornisce una JSON API che puoi usare per creare client. Questo ti evita di dover scrivere manualmente i controller per creare, aggiornare ed eliminare i client.
Tuttavia, dovrai abbinare la JSON API di Passport con il tuo frontend per fornire un dashboard ai tuoi utenti per gestire i loro client. Di seguito, esamineremo tutti gli endpoint API per la gestione dei client. Per comodità, utilizzeremo Axios per dimostrare come effettuare richieste HTTP agli endpoint.
La JSON API è protetta dai middleware web
e auth
; pertanto, può essere chiamata solo dalla tua applicazione. Non può essere chiamata da una fonte esterna.
GET /oauth/clients
Questa rotta restituisce tutti i client dell’utente autenticato. È utile principalmente per elencare tutti i client dell’utente in modo che possano essere modificati o eliminati:
axios.get('/oauth/clients')
.then(response => {
console.log(response.data);
});
POST /oauth/clients
Questa rotta serve per creare nuovi client. Richiede due informazioni: il name
del client e un URL di redirect
. L’URL di redirect
è dove l’utente verrà reindirizzato dopo aver approvato o negato una richiesta di autorizzazione.
Quando un client viene creato, gli verranno assegnati un client ID e un client secret. Questi valori saranno usati per richiedere token di accesso dalla tua applicazione. La rotta per la creazione del client restituirà la nuova istanza del client:
const data = {
name: 'Client Name',
redirect: 'http://example.com/callback'
};
axios.post('/oauth/clients', data)
.then(response => {
console.log(response.data);
})
.catch (response => {
// List errors on response...
});
PUT /oauth/clients/{client-id}
Questa route viene utilizzata per aggiornare i client. Richiede due dati: il name
del client e un URL di redirect
. L’URL di redirect
è dove l’utente verrà reindirizzato dopo aver approvato o negato una richiesta di autorizzazione. La route restituirà l’istanza del client aggiornata:
const data = {
name: 'New Client Name',
redirect: 'http://example.com/callback'
};
axios.put('/oauth/clients/' + clientId, data)
.then(response => {
console.log(response.data);
})
.catch (response => {
// List errors on response...
});
DELETE /oauth/clients/{client-id}
Questa rotta serve per eliminare i client:
axios.delete('/oauth/clients/' + clientId)
.then(response => {
// ...
});
Richiesta di Token
Reindirizzamento per Autorizzazione
Una volta creato un client, gli sviluppatori possono utilizzare il client ID e il secret per richiedere un codice di autorizzazione e un access token dalla tua applicazione. Prima di tutto, l’applicazione che consuma deve effettuare una richiesta di reindirizzamento alla rotta /oauth/authorize
della tua applicazione nel seguente modo:
use Illuminate\Http\Request;
use Illuminate\Support\Str;
Route::get('/redirect', function (Request $request) {
$request->session()->put('state', $state = Str::random(40));
$query = http_build_query([
'client_id' => 'client-id',
'redirect_uri' => 'http://third-party-app.com/callback',
'response_type' => 'code',
'scope' => '',
'state' => $state,
// 'prompt' => '', // "none", "consent", or "login"
]);
return redirect('http://passport-app.test/oauth/authorize?'.$query);
});
Il parametro prompt
può essere utilizzato per specificare il comportamento di autenticazione dell’applicazione Passport.
Se il valore di prompt
è none
, Passport genererà sempre un errore di autenticazione se l’utente non è già autenticato nell’applicazione Passport. Se il valore è consent
, Passport mostrerà sempre la schermata di approvazione dell’autorizzazione, anche se tutti gli scope sono già stati concessi all’applicazione che consuma. Quando il valore è login
, l’applicazione Passport richiederà sempre all’utente di effettuare nuovamente il login, anche se ha già una sessione attiva.
Se non viene fornito alcun valore per prompt
, l’utente verrà richiesto di autorizzare solo se non ha già autorizzato l’accesso all’applicazione che consuma per gli scope richiesti.
Ricorda, la rotta
/oauth/authorize
è già definita da Passport. Non è necessario definirla manualmente.
Approvazione della Richiesta
Quando riceve richieste di autorizzazione, Passport risponderà automaticamente in base al valore del parametro prompt
(se presente) e potrebbe mostrare un template all’utente per permettergli di approvare o negare la richiesta di autorizzazione. Se l’utente approva la richiesta, sarà reindirizzato al redirect_uri
specificato dall’applicazione che ha effettuato la richiesta. Il redirect_uri
deve corrispondere all’URL redirect
specificato quando il client è stato creato.
Se desideri personalizzare la schermata di approvazione dell’autorizzazione, puoi pubblicare le viste di Passport utilizzando il comando Artisan vendor:publish
. Le viste pubblicate saranno posizionate nella directory resources/views/vendor/passport
:
php artisan vendor:publish --tag=passport-views
A volte potresti voler saltare il prompt di autorizzazione, ad esempio quando autorizzi un client di prima parte. Puoi farlo estendendo il modello Client
e definendo un metodo skipsAuthorization
. Se skipsAuthorization
restituisce true
, il client sarà approvato e l’utente sarà reindirizzato immediatamente al redirect_uri
, a meno che l’applicazione che consuma non abbia esplicitamente impostato il parametro prompt
durante il reindirizzamento per l’autorizzazione:
<?php
namespace App\Models\Passport;
use Laravel\Passport\Client as BaseClient;
class Client extends BaseClient
{
/**
* Determina se il client deve saltare il prompt di autorizzazione.
*/
public function skipsAuthorization(): bool
{
return $this->firstParty();
}
}
Conversione dei Codici di Autorizzazione in Token di Accesso
Se l’utente approva la richiesta di autorizzazione, verrà reindirizzato all’applicazione consumatrice. L’applicazione deve prima verificare il parametro state
confrontandolo con il valore memorizzato prima del reindirizzamento. Se il parametro state
corrisponde, allora l’applicazione deve inviare una richiesta POST
alla tua applicazione per richiedere un access token. La richiesta deve includere il codice di autorizzazione emesso dalla tua applicazione quando l’utente ha approvato la richiesta di autorizzazione:
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http;
Route::get('/callback', function (Request $request) {
$state = $request->session()->pull('state');
throw_unless(
strlen($state) > 0 && $state === $request->state,
InvalidArgumentException::class,
'Valore di state non valido.'
);
$response = Http::asForm()->post('http://passport-app.test/oauth/token', [
'grant_type' => 'authorization_code',
'client_id' => 'client-id',
'client_secret' => 'client-secret',
'redirect_uri' => 'http://third-party-app.com/callback',
'code' => $request->code,
]);
return $response->json();
});
Questa rotta /oauth/token
restituirà una risposta JSON contenente gli attributi access_token
, refresh_token
e expires_in
. L’attributo expires_in
indica il numero di secondi prima che il token di accesso scada.
Come la rotta
/oauth/authorize
, anche la rotta/oauth/token
è definita automaticamente da Passport. Non è necessario definirla manualmente.
JSON API
Passport include anche una JSON API per gestire i token di accesso autorizzati. Puoi abbinarla al tuo frontend per offrire agli utenti una dashboard per gestire i token di accesso. Per comodità, useremo Axios per dimostrare come effettuare richieste HTTP agli endpoint. La JSON API è protetta dal middleware web
e auth
; pertanto, può essere chiamata solo dalla tua applicazione.
GET /oauth/tokens
Questa rotta restituisce tutti i token di accesso autorizzati che l’utente autenticato ha creato. È utile principalmente per elencare tutti i token dell’utente in modo da poterli revocare:
axios.get('/oauth/tokens')
.then(response => {
console.log(response.data);
});
DELETE /oauth/tokens/{token-id}
Questa rotta può essere usata per revocare i token di accesso autorizzati e i relativi token di refresh:
axios.delete('/oauth/tokens/' + tokenId);
Rinfrescare i Token
Se la tua applicazione emette token di accesso a breve durata, gli utenti dovranno rinfrescare i loro token di accesso tramite il refresh token che hanno ricevuto quando è stato emesso il token di accesso:
use Illuminate\Support\Facades\Http;
$response = Http::asForm()->post('http://passport-app.test/oauth/token', [
'grant_type' => 'refresh_token',
'refresh_token' => 'the-refresh-token',
'client_id' => 'client-id',
'client_secret' => 'client-secret',
'scope' => '',
]);
return $response->json();
Questa rotta /oauth/token
restituirà una risposta JSON contenente gli attributi access_token
, refresh_token
e expires_in
. L’attributo expires_in
indica il numero di secondi prima che il token di accesso scada.
Revocazione dei Token
Puoi revocare un token utilizzando il metodo revokeAccessToken
sulla Laravel\Passport\TokenRepository
. Per revocare i token di aggiornamento di un token, usa il metodo revokeRefreshTokensByAccessTokenId
sulla Laravel\Passport\RefreshTokenRepository
. Queste classi possono essere risolte usando il service container di Laravel:
use Laravel\Passport\TokenRepository;
use Laravel\Passport\RefreshTokenRepository;
$tokenRepository = app(TokenRepository::class);
$refreshTokenRepository = app(RefreshTokenRepository::class);
// Revoca un token di accesso...
$tokenRepository->revokeAccessToken($tokenId);
// Revoca tutti i token di aggiornamento del token...
$refreshTokenRepository->revokeRefreshTokensByAccessTokenId($tokenId);
Rimozione dei Token
Quando i token sono stati revocati o sono scaduti, potresti volerli rimuovere dal database. Il comando Artisan passport:purge
incluso in Passport può fare questo per te:
# Elimina token e codici di autorizzazione revocati o scaduti...
php artisan passport:purge
# Cancella solo i token scaduti da più di 6 ore...
php artisan passport:purge --hours=6
# Pulisci solo i token revocati e i codici di autorizzazione...
php artisan passport:purge --revoked
# Pulisci solo i token e i codici di autenticazione scaduti...
php artisan passport:purge --expired
Puoi anche configurare un job pianificato nel file routes/console.php
della tua applicazione per eliminare automaticamente i tuoi token secondo una programmazione:
use Illuminate\Support\Facades\Schedule;
Schedule::command('passport:purge')->hourly();
Authorization Code Grant con PKCE
Il grant Authorization Code con "Proof Key for Code Exchange" (PKCE) è un modo sicuro per autenticare le single page applications o le applicazioni native per accedere alla tua API. Questo grant dovrebbe essere usato quando non puoi garantire che il client secret sia memorizzato in modo confidenziale o per ridurre il rischio che un attaccante intercetti il codice di autorizzazione. Una combinazione di "code verifier" e "code challenge" sostituisce il client secret quando si scambia il codice di autorizzazione con un access token.
Creare il Client
Prima che la tua applicazione possa emettere token tramite l’autorizzazione con codice e PKCE, devi creare un client abilitato per PKCE. Puoi farlo usando il comando Artisan passport:client
con l’opzione --public
:
php artisan passport:client --public
Richiedere i Token
Code Verifier e Code Challenge
Poiché questo authorization grant non fornisce un client secret, gli sviluppatori dovranno generare una combinazione di code verifier e code challenge per richiedere un token.
Il code verifier dovrebbe essere una stringa casuale di 43-128 caratteri contenente lettere, numeri e i caratteri "-"
, "."
, "_"
, "~"
, come definito nella specifica RFC 7636.
Il code challenge dovrebbe essere una stringa codificata in Base64 con caratteri sicuri per URL e nomi di file. I caratteri '='
finali devono essere rimossi e non devono essere presenti interruzioni di riga, spazi bianchi o altri caratteri aggiuntivi.
$encoded = base64_encode(hash('sha256', $code_verifier, true));
$codeChallenge = strtr(rtrim($encoded, '='), '+/', '-_');
Reindirizzamento per Autorizzazione
Una volta creato un client, puoi usare l’ID del client e il code verifier e code challenge generati per richiedere un authorization code e un access token dalla tua applicazione. Prima, l’applicazione consumatrice dovrebbe effettuare una richiesta di reindirizzamento alla rotta /oauth/authorize
della tua applicazione:
use Illuminate\Http\Request;
use Illuminate\Support\Str;
Route::get('/redirect', function (Request $request) {
$request->session()->put('state', $state = Str::random(40));
$request->session()->put(
'code_verifier', $code_verifier = Str::random(128)
);
$codeChallenge = strtr(rtrim(
base64_encode(hash('sha256', $code_verifier, true))
, '='), '+/', '-_');
$query = http_build_query([
'client_id' => 'client-id',
'redirect_uri' => 'http://third-party-app.com/callback',
'response_type' => 'code',
'scope' => '',
'state' => $state,
'code_challenge' => $codeChallenge,
'code_challenge_method' => 'S256',
// 'prompt' => '', // "none", "consent", or "login"
]);
return redirect('http://passport-app.test/oauth/authorize?'.$query);
});
Conversione dei Codici di Autorizzazione in Token di Accesso
Se l’utente approva la richiesta di autorizzazione, verrà reindirizzato all’applicazione che ha effettuato la richiesta. L’applicazione consumatrice dovrebbe verificare il parametro state
confrontandolo con il valore memorizzato prima del reindirizzamento, come nel Grant standard Authorization Code.
Se il parametro state corrisponde, l’applicazione consumatrice dovrebbe inviare una richiesta POST
alla tua applicazione per richiedere un token di accesso. La richiesta dovrebbe includere il codice di autorizzazione emesso dalla tua applicazione quando l’utente ha approvato la richiesta di autorizzazione, insieme al code verifier generato originariamente:
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http;
Route::get('/callback', function (Request $request) {
$state = $request->session()->pull('state');
$codeVerifier = $request->session()->pull('code_verifier');
throw_unless(
strlen($state) > 0 && $state === $request->state,
InvalidArgumentException::class
);
$response = Http::asForm()->post('http://passport-app.test/oauth/token', [
'grant_type' => 'authorization_code',
'client_id' => 'client-id',
'redirect_uri' => 'http://third-party-app.com/callback',
'code_verifier' => $codeVerifier,
'code' => $request->code,
]);
return $response->json();
});
Token di Password Grant
Non raccomandiamo più l’uso dei token di password grant. Invece, dovresti scegliere un tipo di grant attualmente raccomandato da OAuth2 Server.
Il password grant di OAuth2 permette ai tuoi altri client di prima parte, come un’applicazione mobile, di ottenere un access token usando un indirizzo email / nome utente e password. Questo ti consente di emettere access token in modo sicuro ai tuoi client di prima parte senza richiedere agli utenti di seguire l’intero flusso di reindirizzamento del codice di autorizzazione OAuth2.
Per abilitare il password grant, chiama il metodo enablePasswordGrant
nel metodo boot
della classe App\Providers\AppServiceProvider
della tua applicazione:
/**
* Avviare i servizi dell'applicazione.
*/
public function boot(): void
{
Passport::enablePasswordGrant();
}
Creare un Client Password Grant
Prima che la tua applicazione possa emettere token tramite il password grant, devi creare un client password grant. Puoi farlo usando il comando Artisan passport:client
con l’opzione --password
. Se hai già eseguito il comando passport:install
, non devi eseguire questo comando:
php artisan passport:client --password
Richiesta di Token
Una volta creato un client per il grant di password, puoi richiedere un access token effettuando una richiesta POST
alla route /oauth/token
con l’email e la password dell’utente. Ricorda, questa route è già registrata da Passport, quindi non è necessario definirla manualmente. Se la richiesta ha successo, riceverai un access_token
e un refresh_token
nella risposta JSON del server:
use Illuminate\Support\Facades\Http;
$response = Http::asForm()->post('http://passport-app.test/oauth/token', [
'grant_type' => 'password',
'client_id' => 'client-id',
'client_secret' => 'client-secret',
'username' => 'taylor@laravel.com',
'password' => 'my-password',
'scope' => '',
]);
return $response->json();
Ricorda, gli access token hanno una durata lunga di default. Tuttavia, puoi configurare la durata massima del tuo access token se necessario.
Richiedere Tutti gli Scope
Quando usi il grant password o il client credentials grant, potresti voler autorizzare il token per tutti gli scope supportati dalla tua applicazione. Puoi farlo richiedendo lo scope *
. Se richiedi lo scope *
, il metodo can
sull’istanza del token restituirà sempre true
. Questo scope può essere assegnato solo a un token emesso usando il grant password
o client_credentials
:
use Illuminate\Support\Facades\Http;
$response = Http::asForm()->post('http://passport-app.test/oauth/token', [
'grant_type' => 'password',
'client_id' => 'client-id',
'client_secret' => 'client-secret',
'username' => 'taylor@laravel.com',
'password' => 'my-password',
'scope' => '*',
]);
Personalizzazione del User Provider
Se la tua applicazione utilizza più di un authentication user provider, puoi specificare quale provider deve usare il client del password grant fornendo l’opzione --provider
quando crei il client tramite il comando artisan passport:client --password
. Il nome del provider deve corrispondere a uno valido definito nel file di configurazione config/auth.php
della tua applicazione. Puoi quindi proteggere la tua route usando il middleware per assicurarti che solo gli utenti dal provider specificato dalla guardia siano autorizzati.
Personalizzare il Campo Username
Quando si effettua l’autenticazione utilizzando il grant password, Passport utilizzerà l’attributo email
del tuo modello autenticabile come "username". Tuttavia, puoi personalizzare questo comportamento definendo un metodo findForPassport
nel tuo modello:
<?php
namespace App\Models;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Passport\HasApiTokens;
class User extends Authenticatable
{
use HasApiTokens, Notifiable;
/**
* Trova l'istanza utente per lo username dato.
*/
public function findForPassport(string $username): User
{
return $this->where('username', $username)->first();
}
}
Personalizzare la Validazione della Password
Quando si effettua l’autenticazione usando il grant password, Passport utilizza l’attributo password
del tuo modello per validare la password fornita. Se il tuo modello non ha un attributo password
o desideri personalizzare la logica di validazione della password, puoi definire un metodo validateForPassportPasswordGrant
nel tuo modello:
<?php
namespace App\Models;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Facades\Hash;
use Laravel\Passport\HasApiTokens;
class User extends Authenticatable
{
use HasApiTokens, Notifiable;
/**
* Valida la password dell'utente per il grant password di Passport.
*/
public function validateForPassportPasswordGrant(string $password): bool
{
return Hash::check($password, $this->password);
}
}
Token di Concessione Implicita
Non raccomandiamo più l’uso dei token di concessione implicita. Invece, dovresti scegliere un tipo di concessione attualmente raccomandato da OAuth2 Server.
La concessione implicita è simile alla concessione con codice di autorizzazione; tuttavia, il token viene restituito al client senza scambiare un codice di autorizzazione. Questa concessione è più comunemente usata per applicazioni JavaScript o mobili dove le credenziali del client non possono essere archiviate in modo sicuro. Per abilitare la concessione, chiama il metodo enableImplicitGrant
nel metodo boot
della classe App\Providers\AppServiceProvider
della tua applicazione:
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Passport::enableImplicitGrant();
}
Una volta abilitata la concessione, gli sviluppatori possono usare il loro client ID per richiedere un access token dalla tua applicazione. L’applicazione consumatrice dovrebbe effettuare una richiesta di reindirizzamento alla rotta /oauth/authorize
della tua applicazione in questo modo:
use Illuminate\Http\Request;
Route::get('/redirect', function (Request $request) {
$request->session()->put('state', $state = Str::random(40));
$query = http_build_query([
'client_id' => 'client-id',
'redirect_uri' => 'http://third-party-app.com/callback',
'response_type' => 'token',
'scope' => '',
'state' => $state,
// 'prompt' => '', // "none", "consent", or "login"
]);
return redirect('http://passport-app.test/oauth/authorize?'.$query);
});
Ricorda, la rotta
/oauth/authorize
è già definita da Passport. Non è necessario definirla manualmente.
Client Credentials Grant Tokens
Il client credentials grant è adatto per l’autenticazione machine-to-machine. Ad esempio, potresti usare questo grant in un job programmato che esegue attività di manutenzione tramite un’API.
Prima che la tua applicazione possa emettere token tramite il client credentials grant, devi creare un client per questo grant. Puoi farlo usando l’opzione --client
del comando Artisan passport:client
:
php artisan passport:client --client
Successivamente, per usare questo tipo di grant, registra un alias middleware per il middleware CheckClientCredentials
. Puoi definire gli alias dei middleware nel file bootstrap/app.php
della tua applicazione:
use Laravel\Passport\Http\Middleware\CheckClientCredentials;
->withMiddleware(function (Middleware $middleware) {
$middleware->alias([
'client' => CheckClientCredentials::class
]);
})
Poi, allega il middleware a una route:
Route::get('/orders', function (Request $request) {
...
})->middleware('client');
Per limitare l’accesso alla route a specifici scope, puoi fornire una lista separata da virgole degli scope richiesti quando alleghi il middleware client
alla route:
Route::get('/orders', function (Request $request) {
...
})->middleware('client:check-status,your-scope');
Recupero dei Token
Per recuperare un token utilizzando questo tipo di grant, effettua una richiesta all’endpoint oauth/token
:
use Illuminate\Support\Facades\Http;
$response = Http::asForm()->post('http://passport-app.test/oauth/token', [
'grant_type' => 'client_credentials',
'client_id' => 'client-id',
'client_secret' => 'client-secret',
'scope' => 'your-scope',
]);
return $response->json()['access_token'];
Token di accesso personali
A volte, i tuoi utenti potrebbero voler emettere token di accesso autonomamente senza passare attraverso il tipico flusso di reindirizzamento con codice di autorizzazione. Permettere agli utenti di emettere token attraverso l’interfaccia della tua applicazione può essere utile per far sperimentare gli utenti con la tua API o può rappresentare un approccio più semplice per emettere token di accesso in generale.
Se la tua applicazione utilizza principalmente Passport per emettere token di accesso personali, considera l’uso di Laravel Sanctum, la libreria leggera di prima parte di Laravel per emettere token di accesso API.
Creare un Client di Accesso Personale
Prima che la tua applicazione possa emettere token di accesso personale, devi creare un client di accesso personale. Puoi farlo eseguendo il comando passport:client
di Artisan con l’opzione --personal
. Se hai già eseguito il comando passport:install
, non è necessario eseguire questo comando:
php artisan passport:client --personal
Dopo aver creato il client di accesso personale, inserisci l’ID del client e il valore segreto in chiaro nel file .env
della tua applicazione:
PASSPORT_PERSONAL_ACCESS_CLIENT_ID="client-id-value"
PASSPORT_PERSONAL_ACCESS_CLIENT_SECRET="unhashed-client-secret-value"
Gestire i Token di Accesso Personali
Una volta creato un client di accesso personale, puoi emettere token per un determinato utente utilizzando il metodo createToken
sull’istanza del modello App\Models\User
. Il metodo createToken
accetta il nome del token come primo argomento e un array opzionale di scopes come secondo argomento:
use App\Models\User;
$user = User::find(1);
// Creare un token senza scopes...
$token = $user->createToken('Token Name')->accessToken;
// Creare un token con scopes...
$token = $user->createToken('My Token', ['place-orders'])->accessToken;
JSON API
Passport include anche una JSON API per gestire i personal access tokens. Puoi abbinare questo al tuo frontend per offrire agli utenti una dashboard per gestire i personal access tokens. Di seguito, esamineremo tutti gli endpoint API per la gestione dei personal access tokens. Per comodità, useremo Axios per dimostrare come effettuare richieste HTTP agli endpoint.
La JSON API è protetta dai middleware web
e auth
; quindi, può essere chiamata solo dalla tua applicazione. Non può essere chiamata da una fonte esterna.
GET /oauth/scopes
Questa rotta restituisce tutti gli scopes definiti per la tua applicazione. Puoi usare questa rotta per elencare gli scope che un utente può assegnare a un token di accesso personale:
axios.get('/oauth/scopes')
.then(response => {
console.log(response.data);
});
GET /oauth/personal-access-tokens
Questa rotta restituisce tutti i personal access tokens che l’utente autenticato ha creato. È utile principalmente per elencare tutti i token dell’utente in modo da poterli modificare o revocare:
axios.get('/oauth/personal-access-tokens')
.then(response => {
console.log(response.data);
});
POST /oauth/personal-access-tokens
Questa rotta crea nuovi token di accesso personali. Richiede due informazioni: il name
del token e gli scopes
che devono essere assegnati al token:
const data = {
name: 'Token Name',
scopes: []
};
axios.post('/oauth/personal-access-tokens', data)
.then(response => {
console.log(response.data.accessToken);
})
.catch (response => {
// Elenca gli errori nella risposta...
});
DELETE /oauth/personal-access-tokens/{token-id}
Questa rotta può essere utilizzata per revocare i token di accesso personale:
axios.delete('/oauth/personal-access-tokens/' + tokenId);
Proteggere le Rotte
Tramite Middleware
Passport include una authentication guard che validerà i token di accesso nelle richieste in arrivo. Una volta che hai configurato il guard api
per usare il driver passport
, devi solo specificare il middleware auth:api
su qualsiasi route che richiede un token di accesso valido:
Route::get('/user', function () {
// ...
})->middleware('auth:api');
Se stai usando il client credentials grant, dovresti usare il middleware
client
per proteggere le tue route invece del middlewareauth:api
.
Guardie di Autenticazione Multiple
Se la tua applicazione autentica diversi tipi di utenti che magari utilizzano modelli Eloquent completamente differenti, probabilmente dovrai definire una configurazione di guardia per ogni tipo di provider utente nella tua applicazione. Questo ti permette di proteggere le richieste destinate a specifici provider utente. Ad esempio, data la seguente configurazione delle guardie nel file di configurazione config/auth.php
:
'api' => [
'driver' => 'passport',
'provider' => 'users',
],
'api-customers' => [
'driver' => 'passport',
'provider' => 'customers',
],
La seguente rotta utilizzerà la guardia api-customers
, che usa il provider utente customers
, per autenticare le richieste in arrivo:
Route::get('/customer', function () {
// ...
})->middleware('auth:api-customers');
Per maggiori informazioni sull’uso di più provider utente con Passport, consulta la documentazione sul password grant.
Passare il Token di Accesso
Quando si chiamano rotte protette da Passport, i consumatori dell’API della tua applicazione devono specificare il loro token di accesso come token Bearer
nell’intestazione Authorization
della richiesta. Ad esempio, usando la libreria HTTP Guzzle:
use Illuminate\Support\Facades\Http;
$response = Http::withHeaders([
'Accept' => 'application/json',
'Authorization' => 'Bearer '.$accessToken,
])->get('https://passport-app.test/api/user');
return $response->json();
Ambiti dei Token
Gli ambiti permettono ai tuoi client API di richiedere un insieme specifico di permessi quando chiedono l’autorizzazione per accedere a un account. Ad esempio, se stai creando un’applicazione e-commerce, non tutti i consumatori dell’API avranno bisogno della possibilità di effettuare ordini. Invece, potresti permettere ai consumatori di richiedere solo l’autorizzazione per accedere agli stati di spedizione degli ordini. In altre parole, gli ambiti consentono agli utenti della tua applicazione di limitare le azioni che un’applicazione di terze parti può eseguire per loro conto.
Definire gli Scope
Puoi definire gli scope della tua API utilizzando il metodo Passport::tokensCan
nel metodo boot
della classe App\Providers\AppServiceProvider
della tua applicazione. Il metodo tokensCan
accetta un array di nomi degli scope e delle loro descrizioni. La descrizione dello scope può essere qualsiasi cosa tu desideri e verrà mostrata agli utenti nella schermata di approvazione dell’autorizzazione:
/**
* Avvia i servizi dell'applicazione.
*/
public function boot(): void
{
Passport::tokensCan([
'place-orders' => 'Place orders',
'check-status' => 'Check order status',
]);
}
Ambito Predefinito
Se un client non richiede ambiti specifici, puoi configurare il server Passport per aggiungere ambiti predefiniti al token utilizzando il metodo setDefaultScope
. Di solito, dovresti chiamare questo metodo nel metodo boot
della classe App\Providers\AppServiceProvider
della tua applicazione:
use Laravel\Passport\Passport;
Passport::tokensCan([
'place-orders' => 'Place orders',
'check-status' => 'Check order status',
]);
Passport::setDefaultScope([
'check-status',
'place-orders',
]);
Gli ambiti predefiniti di Passport non si applicano ai token di accesso personali generati dall’utente.
Assegnare Scopes ai Tokens
Quando si richiedono Codici di Autorizzazione
Quando si richiede un token di accesso utilizzando il grant di autorizzazione con codice, i consumatori devono specificare gli scope desiderati come parametro scope
nella query string. Il parametro scope
dovrebbe essere una lista di scope separati da spazi:
Route::get('/redirect', function () {
$query = http_build_query([
'client_id' => 'client-id',
'redirect_uri' => 'http://example.com/callback',
'response_type' => 'code',
'scope' => 'place-orders check-status',
]);
return redirect('http://passport-app.test/oauth/authorize?'.$query);
});
Quando si emettono token di accesso personale
Se stai emettendo token di accesso personale utilizzando il metodo createToken
del modello App\Models\User
, puoi passare l’array degli ambiti desiderati come secondo argomento al metodo:
$token = $user->createToken('My Token', ['place-orders'])->accessToken;
Checking Scopes
Passport include due middleware che possono essere utilizzati per verificare che una richiesta in arrivo sia autenticata con un token che ha ottenuto uno specifico scope. Per iniziare, definisci i seguenti alias di middleware nel file bootstrap/app.php
della tua applicazione:
use Laravel\Passport\Http\Middleware\CheckForAnyScope;
use Laravel\Passport\Http\Middleware\CheckScopes;
->withMiddleware(function (Middleware $middleware) {
$middleware->alias([
'scopes' => CheckScopes::class,
'scope' => CheckForAnyScope::class,
]);
})
Verifica di Tutti gli Scopes
Il middleware scopes
può essere assegnato a una route per verificare che il token di accesso della richiesta abbia tutti gli scopes elencati:
Route::get('/orders', function () {
// Il token di accesso ha sia gli scopes "check-status" che "place-orders"...
})->middleware(['auth:api', 'scopes:check-status,place-orders']);
Verifica di Qualsiasi Scope
Il middleware scope
può essere assegnato a una rotta per verificare che il token di accesso della richiesta in arrivo abbia almeno uno dei scope elencati:
Route::get('/orders', function () {
// Access token has either "check-status" or "place-orders" scope...
})->middleware(['auth:api', 'scope:check-status,place-orders']);
Verifica degli Scopes su un’istanza di Token
Una volta che una richiesta autenticata con un access token è entrata nella tua applicazione, puoi comunque verificare se il token ha uno specifico scope utilizzando il metodo tokenCan
sull’istanza autenticata App\Models\User
:
use Illuminate\Http\Request;
Route::get('/orders', function (Request $request) {
if ($request->user()->tokenCan('place-orders')) {
// ...
}
});
Metodi aggiuntivi per lo Scope
Il metodo scopeIds
restituisce un array con tutti gli ID/nomi definiti:
use Laravel\Passport\Passport;
Passport::scopeIds();
Il metodo scopes
restituisce un array con tutti gli scopes definiti come istanze di Laravel\Passport\Scope
:
Passport::scopes();
Il metodo scopesFor
restituisce un array di istanze Laravel\Passport\Scope
che corrispondono agli ID/nomi forniti:
Passport::scopesFor(['place-orders', 'check-status']);
Puoi verificare se uno scope è stato definito usando il metodo hasScope
:
Passport::hasScope('place-orders');
Consumare la tua API con JavaScript
Quando si costruisce un’API, può essere estremamente utile poter consumare la propria API dall’applicazione JavaScript. Questo approccio allo sviluppo delle API consente alla tua applicazione di utilizzare la stessa API che stai condividendo con il mondo. La stessa API può essere consumata dalla tua applicazione web, da applicazioni mobili, da applicazioni di terze parti e da qualsiasi SDK che potresti pubblicare su vari gestori di pacchetti.
Tipicamente, se vuoi consumare la tua API dalla tua applicazione JavaScript, dovresti inviare manualmente un access token all’applicazione e passarlo con ogni richiesta alla tua applicazione. Tuttavia, Passport include un middleware che può gestire questo per te. Tutto ciò che devi fare è aggiungere il middleware CreateFreshApiToken
al gruppo di middleware web
nel file bootstrap/app.php
della tua applicazione:
use Laravel\Passport\Http\Middleware\CreateFreshApiToken;
->withMiddleware(function (Middleware $middleware) {
$middleware->web(append: [
CreateFreshApiToken::class,
]);
})
Devi assicurarti che il middleware
CreateFreshApiToken
sia l’ultimo middleware elencato nella tua stack di middleware.
Questo middleware aggiungerà un cookie laravel_token
alle tue risposte in uscita. Questo cookie contiene un JWT criptato che Passport utilizzerà per autenticare le richieste API dalla tua applicazione JavaScript. Il JWT ha una durata pari al valore della tua configurazione session.lifetime
. Ora, poiché il browser invierà automaticamente il cookie con tutte le richieste successive, puoi effettuare richieste all’API della tua applicazione senza passare esplicitamente un access token:
axios.get('/api/user')
.then(response => {
console.log(response.data);
});
Personalizzare il nome del Cookie
Se necessario, puoi personalizzare il nome del cookie laravel_token
usando il metodo Passport::cookie
. Solitamente, questo metodo dovrebbe essere chiamato dal metodo boot
della classe App\Providers\AppServiceProvider
della tua applicazione:
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Passport::cookie('custom_name');
}
Protezione CSRF
Quando usi questo metodo di autenticazione, devi assicurarti che le tue richieste includano un header con un token CSRF valido. Lo scaffold JavaScript predefinito di Laravel include un’istanza di Axios, che usa automaticamente il valore criptato del cookie XSRF-TOKEN
per inviare un header X-XSRF-TOKEN
nelle richieste della stessa origine.
Se preferisci inviare l’header
X-CSRF-TOKEN
invece diX-XSRF-TOKEN
, dovrai usare il token non criptato fornito dacsrf_token()
.
Eventi
Passport genera eventi quando emette token di accesso e token di aggiornamento. Puoi ascoltare questi eventi per eliminare o revocare altri token di accesso nel tuo database:
Nome Evento |
---|
Laravel\Passport\Events\AccessTokenCreated |
Laravel\Passport\Events\RefreshTokenCreated |
Testing
Il metodo actingAs
di Passport può essere usato per specificare l’utente attualmente autenticato e i suoi ambiti. Il primo argomento del metodo actingAs
è l’istanza dell’utente e il secondo è un array di ambiti che devono essere concessi al token dell’utente:
use App\Models\User;
use Laravel\Passport\Passport;
test('servers can be created', function () {
Passport::actingAs(
User::factory()->create(),
['create-servers']
);
$response = $this->post('/api/create-server');
$response->assertStatus(201);
});
use App\Models\User;
use Laravel\Passport\Passport;
public function test_servers_can_be_created(): void
{
Passport::actingAs(
User::factory()->create(),
['create-servers']
);
$response = $this->post('/api/create-server');
$response->assertStatus(201);
}
Il metodo actingAsClient
di Passport può essere usato per specificare il client attualmente autenticato e i suoi ambiti. Il primo argomento del metodo actingAsClient
è l’istanza del client e il secondo è un array di ambiti che devono essere concessi al token del client:
use Laravel\Passport\Client;
use Laravel\Passport\Passport;
test('orders can be retrieved', function () {
Passport::actingAsClient(
Client::factory()->create(),
['check-status']
);
$response = $this->get('/api/orders');
$response->assertStatus(200);
});
use Laravel\Passport\Client;
use Laravel\Passport\Passport;
public function test_orders_can_be_retrieved(): void
{
Passport::actingAsClient(
Client::factory()->create(),
['check-status']
);
$response = $this->get('/api/orders');
$response->assertStatus(200);
}