Laravel Passport
- Introducción
- Instalación
- Configuración
- Emisión de tokens de acceso
- Concesión de códigos de autorización con PKCE
- Concesión de tokens con contraseña
- Concesión implícita de tokens
- Concesión de credenciales de cliente
- Tokens de acceso personal
- Protección de rutas
- Ámbitos de token
- Consumo de la API con JavaScript
- Eventos
- Probando
Introducción
Laravel Passport proporciona una implementación completa de servidor OAuth2 para su aplicación Laravel en cuestión de minutos. Passport está construido sobre el servidor OAuth2 de League, mantenido por Andy Millington y Simon Hamp.
Advertencia
Esta documentación asume que ya estás familiarizado con OAuth2. Si no es así, considera aprender primero sobre la terminología general y las características de OAuth2 antes de continuar.
¿Passport o Sanctum?
Antes de empezar, es recomendable determinar si tu aplicacación realmente necesita Laravel Passport o, si por el contrario, es mejor usar Laravel Sanctum. Si es absolutamente necesario que tu aplicación soporte OAuth2, entonces deberías utilizar Laravel Passport.
Sin embargo, si usted está tratando de autenticar una aplicación de una sola página, aplicación móvil, o emitir tokens de API, debe utilizar Laravel Sanctum. Laravel Sanctum no soporta OAuth2; sin embargo, proporciona una experiencia de desarrollo de autenticación de API mucho más simple.
Instalación
Para empezar, instale Passport a través del gestor de paquetes Composer:
composer require laravel/passport
El proveedor de servicios de Passport registra su propio directorio de migración de base de datos, por lo que deberías migrar tu base de datos después de instalar el paquete. Las migraciones de Passport crearán las tablas que tu aplicación necesita para almacenar clientes OAuth2 y tokens de acceso:
php artisan migrate
Luego, debes ejecutar el comando passport:install
Artisan. Este comando creará las claves de encriptación necesarias para generar tokens de acceso seguros. Además, el comando creará clientes de "acceso personal" y "concesión de contraseña" que serán utilizados para generar tokens de acceso:
php artisan passport:install
Nota
Si desea utilizar UUID como valor de clave principal del modeloClient
Passport en lugar de números enteros autoincrementados, instale Passport utilizando la opciónuuids
.
Después de ejecutar el comando passport:install
, añada el trait Laravel\Passport\HasApiTokens
a su modelo App\Models\User
. Este trait proporcionará algunos métodos de ayuda a su modelo que le permitirán inspeccionar el token y los scopes del usuario autenticado. Si su modelo ya utiliza el trait Laravel\Sanctum\HasApiTokens
, puede eliminar ese trait:
<?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;}
Finalmente, en el archivo de configuración config/auth.php
de tu aplicación, debes definir una guarda de autenticación api
y establecer la opción driver
a passport
. Esto le indicará a tu aplicación que use el TokenGuard
de Passport cuando autentique las solicitudes API entrantes:
'guards' => [ 'web' => [ 'driver' => 'session', 'provider' => 'users', ], 'api' => [ 'driver' => 'passport', 'provider' => 'users', ],],
UUID de cliente
También puede ejecutar el comando passport:install
con la opción --uuids
presente. Esta opción indicará a Passport que desea utilizar UUID en lugar de números enteros autoincrementados como valores de clave primaria del modelo Client
de Passport. Después de ejecutar el comando passport:install
con la opción --uuids
, recibirá instrucciones adicionales para desactivar las migraciones predeterminadas de Passport:
php artisan passport:install --uuids
Despliegue de Passport
Al desplegar Passport en los servidores de su aplicación por primera vez, es probable que necesite ejecutar el comando passport:keys
. Este comando genera las claves de cifrado que Passport necesita para generar los tokens de acceso. Normalmente, las claves generadas no se guardan en el control de código fuente:
php artisan passport:keys
Si es necesario, puede definir la ruta desde donde se cargarán las claves de Passport. Para ello puede utilizar el método Passport::loadKeysFrom
. Típicamente, este método debería ser llamado desde el método boot
de la clase App\Providers\AuthServiceProvider
de su aplicación:
/** * Register any authentication / authorization services. * * @return void */public function boot(){ $this->registerPolicies(); Passport::loadKeysFrom(__DIR__.'/../secrets/oauth');}
Carga de claves del entorno
De manera alternativa, puede publicar el archivo de configuración de Passport utilizando el comando vendor:publish
Artisan:
php artisan vendor:publish --tag=passport-config
Una vez publicado el fichero de configuración, puedes cargar las claves de encriptación de tu aplicación definiéndolas como variables de entorno:
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-----"
Personalización de la migración
Si no va a utilizar las migraciones por defecto de Passport, debe llamar al método Passport::ignoreMigrations
en el método register
de su clase AppProviders\AppServiceProvider
. Puede exportar las migraciones por defecto utilizando el comando vendor:publish
de Artisan:
php artisan vendor:publish --tag=passport-migrations
Actualización de Passport
Cuando actualice a una nueva versión de Passport, es importante que revise cuidadosamente la guía de actualización.
Configuración
Cifrado de los Secrets
de Cliente
Si desea que las claves secretas de su cliente sean cifrados (mediante hash) cuando se almacenen en su base de datos, debe llamar al método Passport::hashClientSecrets
en el método boot
de su clase App\Providers\AuthServiceProvider
:
use Laravel\Passport\Passport; Passport::hashClientSecrets();
Una vez habilitadas, todas tus claves secretas de cliente sólo podrán ser visualizados por el usuario inmediatamente después de ser creados. Dado que el valor del secreto de cliente en texto plano nunca se almacena en la base de datos, no es posible recuperar el valor del secreto si se pierde.
Duración de los tokens
Por defecto, Passport emite tokens de acceso de larga duración que caducan al cabo de un año. Si desea configurar una duración de token más larga o más corta, puede utilizar los métodos tokensExpireIn
, refreshTokensExpireIn
y personalAccessTokensExpireIn
. Estos métodos deben ser llamados desde el método boot
de la clase App\Providers\AuthServiceProvider
de su aplicación:
/** * Register any authentication / authorization services. * * @return void */public function boot(){ $this->registerPolicies(); Passport::tokensExpireIn(now()->addDays(15)); Passport::refreshTokensExpireIn(now()->addDays(30)); Passport::personalAccessTokensExpireIn(now()->addMonths(6));}
Advertencia
Las columnasexpires_at
de las tablas de la base de datos de Passport son de sólo lectura y de visualización. Al emitir tokens, Passport almacena la información de caducidad dentro de los tokens firmados y cifrados. Si necesita invalidar un token, deberá revocarlo.
Sobreescribiendo modelos predeterminados
Puede ampliar los modelos utilizados internamente por Passport definiendo su propio modelo y ampliando el modelo correspondiente de Passport:
use Laravel\Passport\Client as PassportClient; class Client extends PassportClient{ // ...}
Tras definir su modelo, puede indicar a Passport que utilice su modelo personalizado a través de la clase Laravel\Passport\Passport
. Normalmente, debería informar a Passport sobre sus modelos personalizados en el método boot
de la clase App\Providers\AuthServiceProvider
de su aplicación:
use App\Models\Passport\AuthCode;use App\Models\Passport\Client;use App\Models\Passport\PersonalAccessClient;use App\Models\Passport\Token; /** * Register any authentication / authorization services. * * @return void */public function boot(){ $this->registerPolicies(); Passport::useTokenModel(Token::class); Passport::useClientModel(Client::class); Passport::useAuthCodeModel(AuthCode::class); Passport::usePersonalAccessClientModel(PersonalAccessClient::class);}
Sobreescribiendo rutas
A veces puede que desee personalizar las rutas definidas por Passport. Para lograr esto, primero necesitas ignorar las rutas registradas por Passport agregando Passport::ignoreRoutes
al método register
del AppServiceProvider
de tu aplicación:
use Laravel\Passport\Passport; /** * Register any application services. * * @return void */public function register(){ Passport::ignoreRoutes();}
A continuación, puede copiar las rutas definidas por Passport en su archivo routes al archivo routes/web.php
de su aplicación y modificarlas a su gusto:
Route::group([ 'as' => 'passport.', 'prefix' => config('passport.path', 'oauth'), 'namespace' => 'Laravel\Passport\Http\Controllers',], function () { // Passport routes...});
Emisión de tokens de acceso
El uso de OAuth2 a través de códigos de autorización es la forma en que la mayoría de los desarrolladores están familiarizados con OAuth2. Al utilizar códigos de autorización, una aplicación cliente redirigirá a un usuario a su servidor, donde aprobará o denegará la solicitud para emitir un token de acceso al cliente.
Gestión de clientes
En primer lugar, los desarrolladores que creen aplicaciones que necesiten interactuar con la API de tu aplicación tendrán que registrar su aplicación con la tuya creando un "cliente". Normalmente, esto consiste en proporcionar el nombre de su aplicación y una URL a la que tu aplicación pueda redirigir después de que los usuarios aprueben su solicitud de autorización.
El comando passport:client
La forma más sencilla de crear un cliente es utilizando el comando passport:client
de Artisan. Este comando puede ser utilizado para crear tus propios clientes para probar tu funcionalidad OAuth2. Cuando ejecutes el comando client
, Passport te pedirá más información sobre tu cliente y te proporcionará un ID de cliente y un secreto:
php artisan passport:client
Redirección de URL
Si desea permitir varias URL de redirección para su cliente, puede especificarlas utilizando una lista delimitada por comas cuando el comando passport:client
le pida la URL. Cualquier URL que contenga comas debe codificarse como URL:
http://example.com/callback,http://examplefoo.com/callback
API JSON
Dado que los usuarios de su aplicación no podrán utilizar el comando client
, Passport proporciona una API JSON que puede utilizar para crear clientes. Esto le ahorra la molestia de tener que codificar manualmente los controladores para crear, actualizar y eliminar clientes.
Sin embargo, necesitará emparejar la API JSON de Passport con su propio frontend para proporcionar un panel de control para que sus usuarios gestionen sus clientes. A continuación, revisaremos todos los puntos finales de la API para gestionar clientes. Por comodidad, utilizaremos Axios para mostrar cómo realizar solicitudes HTTP a los endpoints.
La API JSON está protegida por el middleware web
y auth
; por lo tanto, sólo puede invocarse desde su propia aplicación. No se puede llamar desde una fuente externa.
GET /oauth/clients
Esta ruta devuelve todos los clientes del usuario autenticado. Esto es útil principalmente para listar todos los clientes del usuario para que, por ejemplo, puedan ser editados o eliminados:
axios.get('/oauth/clients') .then(response => { console.log(response.data); });
POST /oauth/clients
Esta ruta se utiliza para crear nuevos clientes. Requiere dos datos: el name
del cliente y una URL de redirect
. La URL de redirect
es donde el usuario será redirigido después de aprobar o denegar una solicitud de autorización.
Cuando se crea un cliente, se le asigna un ID de cliente y una clave secreta. Estos valores se utilizarán cuando solicite tokens de acceso a su aplicación. La ruta de creación de cliente devolverá la nueva instancia de cliente:
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}
Esta ruta se utiliza para actualizar clientes. Requiere dos datos: el name
del cliente y una URL de redirect
. La URL de redirect
es a la que se redirigirá al usuario tras aprobar o denegar una solicitud de autorización. La ruta devolverá la instancia de cliente actualizada:
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}
Esta ruta se utiliza para eliminar clientes:
axios.delete('/oauth/clients/' + clientId) .then(response => { // });
Solicitud de tokens
Redireccionamiento para autorización
Una vez un cliente ha sido creado, los desarrolladores pueden utilizar su ID de cliente y su clave secreta para solicitar un código de autorización y un token de acceso a su aplicación. En primer lugar, la aplicación consumidora debe realizar una solicitud de redirección a la ruta /oauth/authorize
de su aplicación, como se indica a continuación:
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);});
El parámetro prompt
puede utilizarse para especificar el comportamiento de autenticación de la aplicación Passport.
Si el valor de prompt
es none
, Passport siempre lanzará un error de autenticación si el usuario no está ya autenticado con la aplicación Passport. Si el valor es consent
, Passport siempre mostrará la pantalla de aprobación de autorización, incluso si previamente todos los ámbitos ya han concedidos a la aplicación consumidora. Si el valor es login
, la aplicación Passport siempre pedirá al usuario que vuelva a iniciar sesión en la aplicación, incluso si ya tiene una sesión existente.
Si no se indica ningún valor para prompt
, sólo se solicitará autorización al usuario si éste no ha autorizado previamente el acceso a la aplicación de consumo para los ámbitos solicitados.
Nota
Recuerde que la ruta/oauth/authorize
ya está definida por Passport. No es necesario definir manualmente esta ruta.
Aprobación de la solicitud
Al recibir solicitudes de autorización, Passport responderá automáticamente basándose en el valor del parámetro prompt
(si está presente) y puede mostrar una plantilla al usuario permitiéndole aprobar o denegar la solicitud de autorización. Si aprueban la solicitud, serán redirigidos de vuelta a la redirect_uri
especificada por la aplicación consumidora. La redirect_uri
debe coincidir con la URL de redirección
que se especificó cuando se creó el cliente.
Si desea personalizar la pantalla de aprobación de autorizaciones, puede publicar las vistas de Passport utilizando el comando vendor:publish
Artisan. Las vistas publicadas se colocarán en el directorio resources/views/vendor/passport
:
php artisan vendor:publish --tag=passport-views
A veces es posible que desee omitir la solicitud de autorización, como cuando se autoriza a un cliente de origen. Puedes conseguirlo extendiendo el modelo Client
y definiendo un método skipsAuthorization
. Si skipsAuthorization
devuelve true
, el cliente será aprobado y el usuario será redirigido de vuelta a la redirect_uri
inmediatamente, a menos que la aplicación consumidora haya establecido explícitamente el parámetro prompt
al redirigir para autorización:
<?php namespace App\Models\Passport; use Laravel\Passport\Client as BaseClient; class Client extends BaseClient{ /** * Determine if the client should skip the authorization prompt. * * @return bool */ public function skipsAuthorization() { return $this->firstParty(); }}
Conversión de códigos de autorización en tokens de acceso
Si el usuario aprueba la solicitud de autorización, será redirigido de vuelta a la aplicación consumidora. El consumidor debe comprobar primero el parámetro de state
comparándolo con el valor almacenado antes de la redirección. Si el parámetro de estado coincide, el consumidor debe enviar una petición POST
a su aplicación para solicitar un token de acceso. La petición debe incluir el código de autorización que fue emitido por tu aplicación cuando el usuario aprobó la petición de autorización:
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 ); $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();});
La ruta /oauth/token
devolverá una respuesta JSON que contiene los atributos access_token
, refresh_token
y expires_in
. El atributo expires_in
contiene el número de segundos que faltan para que caduque el token de acceso.
Nota
Al igual que la ruta/oauth/authorize
, la ruta/oauth/token
es definida por Passport. No es necesario definir manualmente esta ruta.
API JSON
Passport también incluye una API JSON para gestionar tokens de acceso autorizados. Puede emparejar esto con su propio frontend para ofrecer a sus usuarios un panel de control para gestionar los tokens de acceso. Por comodidad, utilizaremos Axios para mostrar cómo realizar solicitudes HTTP a los puntos finales. La API JSON está protegida por el middleware web
y auth
; por lo tanto, sólo puede ser llamada desde tu propia aplicación.
GET /oauth/tokens
Esta ruta devuelve todos los tokens de acceso autorizados que el usuario autenticado ha creado. Esto es útil principalmente para listar todos los tokens del usuario para que, por ejemplo, pueda revocarlos:
axios.get('/oauth/tokens') .then(response => { console.log(response.data); });
DELETE /oauth/tokens/{token-id}
Esta ruta se puede utilizar para revocar los tokens de acceso autorizados y sus tokens de actualización relacionados:
axios.delete('/oauth/tokens/' + tokenId);
Actualización de tokens
Si su aplicación emite tokens de acceso de corta duración, los usuarios tendrán que actualizar sus tokens de acceso a través del token de actualización que se les proporcionó cuando se emitió el token de acceso:
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();
La ruta /oauth/token
devolverá una respuesta JSON con los atributos access_token
, refresh_token
y expires_in
. El atributo expires_in
contiene el número de segundos que faltan para que caduque el token de acceso.
Revocación de tokens
Puedes revocar un token usando el método revokeAccessToken
de Laravel\Passport\TokenRepository
. Puedes revocar los tokens de refresco de un token usando el método revokeRefreshTokensByAccessTokenId
de Laravel\Passport\RefreshTokenRepository
. Estas clases pueden resolverse utilizando el contenedor de servicios de Laravel:
use Laravel\Passport\TokenRepository;use Laravel\Passport\RefreshTokenRepository; $tokenRepository = app(TokenRepository::class);$refreshTokenRepository = app(RefreshTokenRepository::class); // Revoke an access token...$tokenRepository->revokeAccessToken($tokenId); // Revoke all of the token's refresh tokens...$refreshTokenRepository->revokeRefreshTokensByAccessTokenId($tokenId);
Borrar tokens
Cuando los tokens han sido revocados o han caducado, es posible que desee borrarlos de la base de datos. El comando passport:purge
Artisan incluido en Passport puede hacerlo por ti:
# Purge revoked and expired tokens and auth codes...php artisan passport:purge # Only purge revoked tokens and auth codes...php artisan passport:purge --revoked # Only purge expired tokens and auth codes...php artisan passport:purge --expired
También puede configurar un trabajo programado en la clase App\Console\Kernel
de su aplicación para borrar automáticamente sus tokens:
/** * Define the application's command schedule. * * @param \Illuminate\Console\Scheduling\Schedule $schedule * @return void */protected function schedule(Schedule $schedule){ $schedule->command('passport:purge')->hourly();}
Concesión de códigos de autorización con PKCE
La concesión de Código de Autorización con "Proof Key for Code Exchange" (PKCE) es una forma segura de autenticar aplicaciones de una sola página o aplicaciones nativas para acceder a su API. Esta concesión debe utilizarse cuando no pueda garantizar que la clave secreta del cliente se almacenará de forma confidencial o para mitigar la amenaza de que un atacante intercepte el código de autorización. Una combinación de un "verificador de código" y un "desafío de código" sustituye a la clave secreta del cliente cuando se intercambia el código de autorización por un token de acceso.
Creación del cliente
Antes de que tu aplicación pueda emitir tokens a través de la concesión de código de autorización con PKCE, necesitarás crear un cliente habilitado para PKCE. Usted puede hacer esto usando el comando Artisan passport:client
con la opción --public
:
php artisan passport:client --public
Solicitud de tokens
Verificador de Código y Desafío de Código
Como esta concesión de autorización no proporciona una clave secreta de cliente, los desarrolladores tendrán que generar una combinación de verificador de código y desafío de código para solicitar un token.
El verificador de código debe ser una cadena aleatoria de entre 43 y 128 caracteres que contenga letras, números y caracteres "-"
, "."
, "_"
, "~"
, tal y como se define en la especificación RFC 7636.
El desafío de código debe ser una cadena codificada en Base64 con URL y caracteres seguros de nombre de archivo. Los caracteres '='
finales deben ser eliminados y no debe haber saltos de línea, espacios en blanco u otros caracteres adicionales.
$encoded = base64_encode(hash('sha256', $code_verifier, true)); $codeChallenge = strtr(rtrim($encoded, '='), '+/', '-_');
Redireccionamiento para autorización
Una vez creado un cliente, puede utilizar el ID del cliente, el verificador de código y el desafío de código generados para solicitar un código de autorización y un token de acceso a su aplicación. Primero, la aplicación consumidora debe hacer una petición de redirección a la ruta /oauth/authorize
de tu aplicación:
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);});
Conversión de códigos de autorización en tokens de acceso
Si el usuario aprueba la solicitud de autorización, será redirigido de vuelta a la aplicación consumidora. El consumidor debe verificar el parámetro de state
contra el valor que fue almacenado antes de la redirección, como en la concesión estándar del código de autorización.
Si el parámetro state
coincide, el consumidor debe emitir una petición POST
a su aplicación para solicitar un token de acceso. La solicitud debe incluir el código de autorización que fue emitido por su aplicación cuando el usuario aprobó la solicitud de autorización junto con el verificador de código generado originalmente:
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();});
Concesión de tokens con contraseña
Advertencia
Ya no recomendamos el uso de tokens de concesión de contraseña. En su lugar, deberías elegir un tipo de concesión que esté actualmente recomendado por OAuth2 Server.
La concesión de contraseña OAuth2 permite a sus otros clientes "first-party", como una aplicación móvil, obtener un token de acceso utilizando una dirección de correo electrónico / nombre de usuario y contraseña. Esto le permite emitir tokens de acceso de forma segura a sus clientes "first-party" sin necesidad de que sus usuarios pasen por todo el flujo de redirección de código de autorización OAuth2.
Creación de un cliente de concesión de contraseña
Antes de que su aplicación pueda emitir tokens a través de la concesión de contraseña, necesitará crear un cliente de concesión de contraseña. Puedes hacerlo utilizando el comando Artisan passport:client
con la opción --password
. Si ya ha ejecutado el comando passport:install
, no necesita ejecutar este comando:
php artisan passport:client --password
Solicitud de tokens
Una vez que haya creado un cliente de concesión de contraseña, puede solicitar un token de acceso emitiendo una solicitud POST
a la ruta /oauth/token
con la dirección de correo electrónico y la contraseña del usuario. Recuerde que esta ruta ya está registrada por Passport, por lo que no es necesario definirla manualmente. Si la solicitud tiene éxito, recibirá un access_token
y un refresh_token
en la respuesta JSON del servidor:
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();
Nota
Recuerda, los tokens de acceso son de larga duración por defecto. Sin embargo, eres libre de configurar la duración máxima de tu token de acceso si es necesario.
Solicitud de todos los ámbitos
Cuando utilice la concesión de contraseña o la concesión de credenciales de cliente, es posible que desee autorizar el token para todos los ámbitos admitidos por su aplicación. Puede hacerlo solicitando el ámbito *
. Si solicita el ámbito *
, el método can
de la instancia del token siempre devolverá true
. Este ámbito sólo puede asignarse a un token que se emita utilizando la concesión 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' => '*',]);
Personalización del Proveedor de Usuario
Si su aplicación utiliza más de un proveedor de usuario de autenticación, puede especificar qué proveedor de usuario utiliza el cliente de concesión de contraseña proporcionando una opción --provider
al crear el cliente mediante el comando artisan passport:client --password
. El nombre del proveedor dado debe coincidir con un proveedor válido definido en el archivo de configuración config/auth.php
de su aplicación. A continuación, puede proteger su ruta utilizando middleware para asegurarse de que sólo los usuarios del proveedor especificado en la guarda están autorizados.
Personalización del campo de nombre de usuario
Cuando se autentique utilizando la concesión de contraseña, Passport utilizará el atributo de email
de su modelo autenticable como "nombre de usuario". Sin embargo, puede personalizar este comportamiento definiendo un método findForPassport
en su modelo:
<?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; /** * Find the user instance for the given username. * * @param string $username * @return \App\Models\User */ public function findForPassport($username) { return $this->where('username', $username)->first(); }}
Personalización de la validación de contraseña
Cuando se autentique utilizando la concesión de contraseña, Passport utilizará el atributo password
de su modelo para validar la contraseña dada. Si su modelo no tiene un atributo de password
o desea personalizar la lógica de validación de la contraseña, puede definir un método validateForPassportPasswordGrant
en su modelo:
<?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; /** * Validate the password of the user for the Passport password grant. * * @param string $password * @return bool */ public function validateForPassportPasswordGrant($password) { return Hash::check($password, $this->password); }}
Tokens de Concesión Implícitos
Advertencia
Ya no recomendamos el uso de tokens de concesión implícitos. En su lugar, deberías elegir un tipo de grant que actualmente es recomendado por OAuth2 Server.
La concesión implícita es similar a la concesión de código de autorización; sin embargo, el token se devuelve al cliente sin intercambiar un código de autorización. Esta concesión se utiliza más comúnmente para JavaScript o aplicaciones móviles donde las credenciales del cliente no se pueden almacenar de forma segura. Para habilitar la concesión, llame al método enableImplicitGrant
en el método boot
de la clase App\Providers\AuthServiceProvider
de su aplicación:
/** * Register any authentication / authorization services. * * @return void */public function boot(){ $this->registerPolicies(); Passport::enableImplicitGrant();}
Una vez habilitada la concesión, los desarrolladores pueden utilizar su ID de cliente para solicitar un token de acceso a su aplicación. La aplicación consumidora debería hacer una petición de redirección a la ruta /oauth/authorize
de tu aplicación de esta forma:
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);});
Nota
Recuerde que la ruta/oauth/authorize
ya está definida por Passport. No es necesario definir manualmente esta ruta.
Concesión de credenciales de cliente
La concesión de credenciales de cliente es adecuada para la autenticación de máquina a máquina. Por ejemplo, puede utilizar esta concesión en un trabajo programado que realice tareas de mantenimiento a través de una API.
Antes de que tu aplicación pueda emitir tokens a través de la concesión de credenciales de cliente, necesitarás crear un cliente de concesión de credenciales de cliente. Puedes hacer esto usando la opción --client
del comando passport:client
de Artisan:
php artisan passport:client --client
Luego, para usar este tipo de concesión, necesitas agregar el middleware CheckClientCredentials
a la propiedad $routeMiddleware
de tu archivo app/Http/Kernel.php
:
use Laravel\Passport\Http\Middleware\CheckClientCredentials; protected $routeMiddleware = [ 'client' => CheckClientCredentials::class,];
A continuación, adjunte el middleware a una ruta:
Route::get('/orders', function (Request $request) { ...})->middleware('client');
Para restringir el acceso a la ruta a ámbitos específicos, puede proporcionar una lista delimitada por comas de los ámbitos requeridos al adjuntar el middleware client
a la ruta:
Route::get('/orders', function (Request $request) { ...})->middleware('client:check-status,your-scope');
Recuperación de tokens
Para recuperar un token utilizando este tipo de concesión, realice una solicitud al 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'];
Tokens de acceso personal
A veces, sus usuarios pueden querer emitir tokens de acceso a sí mismos sin pasar por el típico flujo de redirección de código de autorización. Permitir a los usuarios emitir tokens a través de la interfaz de usuario de su aplicación puede ser útil para permitir a los usuarios experimentar con su API o puede servir como un enfoque más simple para la emisión de tokens de acceso en general.
Nota
Si tu aplicación utiliza principalmente Passport para emitir tokens de acceso personal, considera el uso de Laravel Sanctum, la librería ligera de Laravel para la emisión de tokens de acceso a la API.
Creación de un cliente de acceso personal
Antes de que su aplicación pueda emitir tokens de acceso personal, necesitará crear un cliente de acceso personal. Puedes hacer esto ejecutando el comando passport:client
de Artisan con la opción --personal
. Si ya ha ejecutado el comando passport:install
, no necesita ejecutar este comando:
php artisan passport:client --personal
Después de crear tu cliente de acceso personal, coloca el ID del cliente y el valor del secreto en texto plano en el archivo .env
de tu aplicación:
PASSPORT_PERSONAL_ACCESS_CLIENT_ID="client-id-value"PASSPORT_PERSONAL_ACCESS_CLIENT_SECRET="unhashed-client-secret-value"
Gestión de tokens de acceso personal
Una vez que haya creado un cliente de acceso personal, puede emitir tokens para un usuario determinado utilizando el método createToken
en la instancia del modelo App\Models\User
. El método createToken
acepta el nombre del token como primer argumento y una array opcional de ámbitos como segundo argumento:
use App\Models\User; $user = User::find(1); // Creating a token without scopes...$token = $user->createToken('Token Name')->accessToken; // Creating a token with scopes...$token = $user->createToken('My Token', ['place-orders'])->accessToken;
API JSON
Passport también incluye una API JSON para gestionar tokens de acceso personales. Puede emparejarlo con su propio frontend para ofrecer a sus usuarios un panel de control para gestionar los tokens de acceso personales. A continuación, revisaremos todas las rutas de la API para gestionar los tokens de acceso personal. Para mayor comodidad, utilizaremos Axios para mostrar cómo realizar solicitudes HTTP a los endpoints.
La API JSON está protegida por el middleware web
y auth
; por lo tanto, sólo puede ser invocada desde su propia aplicación. No se puede llamar desde una fuente externa.
GET /oauth/scopes
Esta ruta devuelve todos los ámbitos definidos para su aplicación. Puede utilizar esta ruta para listar los ámbitos que un usuario puede asignar a un token de acceso personal:
axios.get('/oauth/scopes') .then(response => { console.log(response.data); });
GET /oauth/personal-access-tokens
Esta ruta devuelve todos los tokens de acceso personal que el usuario autenticado ha creado. Esto es útil principalmente para listar todos los tokens del usuario para que, por ejemplo, pueda editarlos o revocarlos:
axios.get('/oauth/personal-access-tokens') .then(response => { console.log(response.data); });
POST /oauth/personal-access-tokens
Esta ruta crea nuevos tokens de acceso personal. Requiere dos datos: el name
del token y los scopes (ámbitos)
que deben asignarse al token:
const data = { name: 'Token Name', scopes: []}; axios.post('/oauth/personal-access-tokens', data) .then(response => { console.log(response.data.accessToken); }) .catch (response => { // List errors on response... });
DELETE /oauth/personal-access-tokens/{token-id}
Esta ruta puede utilizarse para revocar tokens de acceso personal:
axios.delete('/oauth/personal-access-tokens/' + tokenId);
Protección de rutas
Mediante middleware
Passport incluye una guarda de autenticación que validará los tokens de acceso en las peticiones entrantes. Una vez configurado la guarda api
para utilizar el controlador passport
, sólo es necesario especificar el middleware auth:api
en cualquier ruta que requiera un token de acceso válido:
Route::get('/user', function () { //})->middleware('auth:api');
Advertencia
Si estás utilizando la concesión de credenciales de cliente, debes utilizar el middleware decliente
para proteger tus rutas en lugar del middlewareauth:api
.
Guardas de autenticación múltiple
Si tu aplicación autentica diferentes tipos de usuarios que quizás utilicen modelos de Eloquent completamente diferentes, es probable que necesites definir una configuración de guarda para cada tipo de proveedor de usuario en tu aplicación. Esto le permite proteger peticiones destinadas a proveedores de usuario específicos. Por ejemplo, dada la siguiente configuración de guarda, el archivo de configuración config/auth.php
::
'api' => [ 'driver' => 'passport', 'provider' => 'users',], 'api-customers' => [ 'driver' => 'passport', 'provider' => 'customers',],
La siguiente ruta utilizará la guarda api-customers
, que utiliza el proveedor de usuario customers
, para autenticar las peticiones entrantes:
Route::get('/customer', function () { //})->middleware('auth:api-customers');
Nota
Para más información sobre el uso de proveedores de usuarios múltiples con Passport, consulte la documentación sobre concesión de contraseñas.
Paso del token de acceso
Al llamar a rutas protegidas por Passport, los consumidores de la API de tu aplicación deben especificar su token de acceso como token Bearer
en la cabecera Authorization
de su petición. Por ejemplo, al utilizar la biblioteca 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();
Ámbitos de token
Los ámbitos permiten a sus clientes API solicitar un conjunto específico de permisos cuando solicitan autorización para acceder a una cuenta. Por ejemplo, si está creando una aplicación de comercio electrónico, no todos los consumidores de la API necesitarán la capacidad de realizar pedidos. En su lugar, puede permitir que los consumidores sólo soliciten autorización para acceder a los estados de envío de los pedidos. En otras palabras, los ámbitos permiten a los usuarios de su aplicación limitar las acciones que una aplicación de terceros puede realizar en su nombre.
Definición de ámbitos
Puede definir los ámbitos de su API utilizando el método Passport::tokensCan
en el método boot
de la clase App\Providers\AuthServiceProvider
de su aplicación. El método tokensCan
acepta una array de nombres de ámbito y descripciones de ámbito. La descripción del ámbito puede ser cualquier cosa que desee y se mostrará a los usuarios en la pantalla de aprobación de autorización:
/** * Register any authentication / authorization services. * * @return void */public function boot(){ $this->registerPolicies(); Passport::tokensCan([ 'place-orders' => 'Place orders', 'check-status' => 'Check order status', ]);}
Ámbito predeterminado
Si un cliente no solicita ningún ámbito específico, puede configurar el servidor Passport para que adjunte ámbitos predeterminados al token mediante el método setDefaultScope
. Normalmente, debe llamar a este método desde el método boot
de la clase App\Providers\AuthServiceProvider
de su aplicación:
use Laravel\Passport\Passport; Passport::tokensCan([ 'place-orders' => 'Place orders', 'check-status' => 'Check order status',]); Passport::setDefaultScope([ 'check-status', 'place-orders',]);
Nota Los ámbitos predeterminados de Passport no se aplican a los tokens de acceso personales generados por el usuario.
Asignación de ámbitos a tokens
Al solicitar códigos de autorización
Al solicitar un token de acceso utilizando la concesión de código de autorización, los consumidores deben especificar los ámbitos deseados como parámetro de la cadena de consulta scope
. El parámetro de scope
debe ser una lista de ámbitos delimitada por espacios:
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);});
Al expedir tokens de acceso personal
Si está emitiendo tokens de acceso personal utilizando el método createToken
del modelo App\Models\User
, puede pasar el array de ámbitos deseados como segundo argumento al método:
$token = $user->createToken('My Token', ['place-orders'])->accessToken;
Comprobación de ámbitos
Passport incluye dos middleware que pueden utilizarse para verificar que una solicitud entrante está autenticada con un token al que se ha concedido un ámbito determinado. Para empezar, añada el siguiente middleware a la propiedad $routeMiddleware
de su archivo app/Http/Kernel.php
:
'scopes' => \Laravel\Passport\Http\Middleware\CheckScopes::class,'scope' => \Laravel\Passport\Http\Middleware\CheckForAnyScope::class,
Comprobar todos los ámbitos
El middleware scopes
puede ser asignado a una ruta para verificar que el token de acceso de la petición entrante tiene todos los ámbitos listados:
Route::get('/orders', function () { // Access token has both "check-status" and "place-orders" scopes...})->middleware(['auth:api', 'scopes:check-status,place-orders']);
Comprobar todos los ámbitos
El middleware scopes
puede asignarse a una ruta para verificar que el token de acceso de la solicitud entrante tiene al menos uno de los ámbitos enumerados:
Route::get('/orders', function () { // Access token has either "check-status" or "place-orders" scope...})->middleware(['auth:api', 'scope:check-status,place-orders']);
Comprobación de ámbitos en una instancia de token
Una vez que una solicitud autenticada con un token de acceso ha entrado en su aplicación, puede comprobar si el token tiene un ámbito determinado utilizando el método tokenCan
en la instancia autenticada de App\Models\User
:
use Illuminate\Http\Request; Route::get('/orders', function (Request $request) { if ($request->user()->tokenCan('place-orders')) { // }});
Métodos de ámbito adicionales
El método scopeIds
devolverá un array de todos los IDs / nombres definidos:
use Laravel\Passport\Passport; Passport::scopeIds();
El método scopes
devolverá un array de todos los scopes definidos como instancias de Laravel\Passport\Scope
:
Passport::scopes();
El método scopesFor
devolverá un array de instancias de Laravel\Passport\Scope
que coincidan con los IDs / nombres dados:
Passport::scopesFor(['place-orders', 'check-status']);
Puede determinar si un ámbito dado ha sido definido utilizando el método hasScope
:
Passport::hasScope('place-orders');
Uso de la API con JavaScript
Cuando se construye una API, puede ser extremadamente útil poder consumir su propia API desde su aplicación JavaScript. Este enfoque del desarrollo de API permite que su propia aplicación consuma la misma API que está compartiendo con el mundo. La misma API puede ser consumida por tu aplicación web, aplicaciones móviles, aplicaciones de terceros y cualquier SDK que puedas publicar en varios gestores de paquetes.
Normalmente, si desea consumir su API desde su aplicación JavaScript, tendría que enviar manualmente un token de acceso a la aplicación y pasarlo con cada solicitud a su aplicación. Sin embargo, Passport incluye un middleware que puede gestionar esto por usted. Todo lo que tiene que hacer es añadir el middleware CreateFreshApiToken
a su grupo middleware web
en su archivo app/Http/Kernel.php
:
'web' => [ // Other middleware... \Laravel\Passport\Http\Middleware\CreateFreshApiToken::class,],
Advertencia
Debes asegurarte de que el middlewareCreateFreshApiToken
es el último middleware listado en tu pila de middleware.
Este middleware adjuntará una cookie laravel_token
a tus respuestas salientes. Esta cookie contiene un JWT cifrado que Passport utilizará para autenticar las solicitudes de API de su aplicación JavaScript. El JWT tiene un tiempo de vida igual a tu valor de configuración session.lifetime
. Ahora, dado que el navegador enviará automáticamente la cookie con todas las solicitudes posteriores, puede realizar solicitudes a la API de su aplicación sin pasar explícitamente un token de acceso:
axios.get('/api/user') .then(response => { console.log(response.data); });
Personalizar el nombre de la cookie
Si es necesario, puede personalizar el nombre de la cookie laravel_token
utilizando el método Passport::cookie
. Normalmente, este método debe ser llamado desde el método de boot
de la clase AppProviders\AuthServiceProvider
de su aplicación:
/** * Register any authentication / authorization services. * * @return void */public function boot(){ $this->registerPolicies(); Passport::cookie('custom_name');}
Protección CSRF
Cuando se utiliza este método de autenticación, usted tendrá que asegurarse de que un encabezado token CSRF válido se incluye en sus peticiones. El andamiaje JavaScript predeterminado de Laravel incluye una instancia de Axios, que utilizará automáticamente el valor cifrado de la cookie XSRF-TOKEN
para enviar una cabecera X-XSRF-TOKEN
en las peticiones del mismo origen.
Nota
Si decide enviar la cabeceraX-CSRF-TOKEN
en lugar deX-XSRF-TOKEN
, tendrá que utilizar el token sin cifrar proporcionado porcsrf_token()
.
Eventos
Passport genera eventos cuando emite tokens de acceso y tokens de actualización. Puede utilizar estos eventos para borrar o revocar otros tokens de acceso en su base de datos. Si lo desea, puede adjuntar oyentes a estos eventos en la clase AppProviders\EventServiceProvider
de su aplicación:
/** * The event listener mappings for the application. * * @var array */protected $listen = [ 'Laravel\Passport\Events\AccessTokenCreated' => [ 'App\Listeners\RevokeOldTokens', ], 'Laravel\Passport\Events\RefreshTokenCreated' => [ 'App\Listeners\PruneOldTokens', ],];
Testing
El método actingAs
de Passport puede utilizarse para especificar el usuario autenticado actualmente, así como sus ámbitos. El primer argumento dado al método actingAs
es la instancia del usuario y el segundo es una array de ámbitos que deberían concederse al token del usuario:
use App\Models\User;use Laravel\Passport\Passport; public function test_servers_can_be_created(){ Passport::actingAs( User::factory()->create(), ['create-servers'] ); $response = $this->post('/api/create-server'); $response->assertStatus(201);}
El método actingAsClient
de Passport puede utilizarse para especificar el cliente autenticado actualmente, así como sus ámbitos. El primer argumento dado al método actingAsClient
es la instancia del cliente y el segundo es un array de ámbitos que deben concederse al token del cliente:
use Laravel\Passport\Client;use Laravel\Passport\Passport; public function test_orders_can_be_retrieved(){ Passport::actingAsClient( Client::factory()->create(), ['check-status'] ); $response = $this->get('/api/orders'); $response->assertStatus(200);}