1. Paquetes
  2. Laravel Passport

Introducción

Laravel Passport proporciona una implementación completa de servidor OAuth2 para tu aplicación Laravel en cuestión de minutos. Passport se basa en el servidor OAuth2 de League que es mantenido por Andy Millington y Simon Hamp.

Advertencia Esta documentación asume que ya está familiarizado con OAuth2. Si no sabe nada acerca de OAuth2, considere familiarizarse con la terminología general y las características de OAuth2 antes de continuar.

¿Passport o Sanctum?

Antes de empezar, es posible que desees determinar si tu aplicación estaría mejor con Laravel Passport o Laravel Sanctum. Si tu aplicación necesita admitir absolutamente OAuth2, entonces deberías usar Laravel Passport.

Sin embargo, si estás intentando autenticar una aplicación de una sola página, una aplicación móvil o emitir tokens de API, deberías usar Laravel Sanctum. Laravel Sanctum no admite OAuth2; sin embargo, proporciona una experiencia de desarrollo de autenticación de API mucho más simple.

Instalación

Para comenzar, instala Passport a través del administrador 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 debe migrar su base de datos después de instalar el paquete. Las migraciones de Passport crearán las tablas que su aplicación necesita para almacenar clientes OAuth2 y tokens de acceso:

php artisan migrate

A continuación, debe ejecutar el comando Artisan passport:install. Este comando creará las claves de cifrado necesarias para generar tokens de acceso seguros. Además, el comando creará clientes de "acceso personal" y "concesión de contraseña" que se utilizarán para generar tokens de acceso:

php artisan passport:install

Nota Si deseas utilizar UUID como el valor de clave primaria del modelo Client de Passport en lugar de enteros autoincrementables, instala Passport utilizando la opción uuids.

Después de ejecutar el comando passport:install, agregue el rasgo Laravel\Passport\HasApiTokens a su modelo App\Models\User. Este rasgo proporcionará algunos métodos auxiliares a su modelo que le permitirán inspeccionar el token y las scopes del usuario autenticado. Si su modelo ya está utilizando el rasgo Laravel\Sanctum\HasApiTokens, puede quitar ese rasgo:

<?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 un guardia de autenticación api y establecer la opción driver en passport. Esto indicará a tu aplicación que utilice el TokenGuard de Passport al autenticar las solicitudes de API entrantes:

'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
 
'api' => [
'driver' => 'passport',
'provider' => 'users',
],
],

UUIDs del Cliente

También puedes ejecutar el comando passport:install con la opción --uuids presente. Esta opción indicará a Passport que deseas utilizar UUID en lugar de enteros autoincrementables como los valores de clave primaria del modelo Client de Passport. Después de ejecutar el comando passport:install con la opción --uuids, se te darán instrucciones adicionales sobre cómo deshabilitar las migraciones predeterminadas de Passport:

php artisan passport:install --uuids

Implementación de Passport

Cuando implementas Passport por primera vez en los servidores de tu aplicación, es probable que necesites ejecutar el comando passport:keys. Este comando genera las claves de cifrado que Passport necesita para generar tokens de acceso. Por lo general, las claves generadas no se almacenan en el control de versiones:

php artisan passport:keys

Si es necesario, puedes definir la ruta desde la cual se deben cargar las claves de Passport. Puedes utilizar el método Passport::loadKeysFrom para lograr esto. Por lo general, este método debe llamarse desde el método boot de la clase App\Providers\AuthServiceProvider de tu aplicación:

/**
* Registra cualquier servicio de autenticación/autorización.
*/
public function boot(): void
{
Passport::loadKeysFrom(__DIR__.'/../secrets/oauth');
}

Carga de Claves desde el Entorno

Alternativamente, puedes publicar el archivo de configuración de Passport utilizando el comando Artisan vendor:publish:

php artisan vendor:publish --tag=passport-config

Después de que se haya publicado el archivo de configuración, puedes cargar las claves de cifrado 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 Migraciones

Si no vas a utilizar las migraciones predeterminadas de Passport, debes llamar al método Passport::ignoreMigrations en el método register de la clase App\Providers\AppServiceProvider de tu aplicación. Puedes exportar las migraciones predeterminadas utilizando el comando Artisan vendor:publish:

php artisan vendor:publish --tag=passport-migrations

Actualización de Passport

Al actualizar a una nueva versión principal de Passport, es importante que revises cuidadosamente la guía de actualización.

Configuración

Hashing del Secreto del Cliente

Si deseas que los secretos de tus clientes se cifren al almacenarse en tu base de datos, debes llamar al método Passport::hashClientSecrets en el método boot de la clase App\Providers\AuthServiceProvider de tu aplicación:

use Laravel\Passport\Passport;
 
Passport::hashClientSecrets();

Una vez habilitado, todos tus secretos de cliente solo serán visibles para el usuario inmediatamente después de ser creados. Dado que el valor del secreto del cliente en texto plano nunca se almacena en la base de datos, no es posible recuperar el valor del secreto si se pierde.

Vida Útil de los Tokens

De forma predeterminada, Passport emite tokens de acceso de larga duración que caducan después de un año. Si deseas configurar una duración de token más larga o más corta, puedes utilizar los métodos tokensExpireIn, refreshTokensExpireIn y personalAccessTokensExpireIn. Estos métodos deben llamarse desde el método boot de la clase App\Providers\AuthServiceProvider de tu aplicación:

/**
* Registra cualquier servicio de autenticación/autorización.
*/
public function boot(): void
{
Passport::tokensExpireIn(now()->addDays(15));
Passport::refreshTokensExpireIn(now()->addDays(30));
Passport::personalAccessTokensExpireIn(now()->addMonths(6));
}

Advertencia Las columnas expires_at en las tablas de base de datos de Passport son de solo lectura y solo para fines de visualización. Al emitir tokens, Passport almacena la información de vencimiento dentro de los tokens firmados y cifrados. Si necesita invalidar un token, debe revocarlo.

Anulación de Modelos Predeterminados

Eres libre de extender los modelos utilizados internamente por Passport definiendo tu propio modelo y extendiendo el modelo correspondiente de Passport:

use Laravel\Passport\Client as PassportClient;
 
class Client extends PassportClient
{
// ...
}

Después de definir tu modelo, puedes indicar a Passport que use tu modelo personalizado a través de la clase Laravel\Passport\Passport. Por lo general, debes informar a Passport sobre tus modelos personalizados en el método boot de la clase App\Providers\AuthServiceProvider de tu aplicación:

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;
 
/**
* Registra cualquier servicio de autenticación/autorización.
*/
public function boot(): void
{
Passport::useTokenModel(Token::class);
Passport::useRefreshTokenModel(RefreshToken::class);
Passport::useAuthCodeModel(AuthCode::class);
Passport::useClientModel(Client::class);
Passport::usePersonalAccessClientModel(PersonalAccessClient::class);
}

Anulación de Rutas Predeterminadas

A veces es posible que desees personalizar las rutas definidas por Passport. Para lograrlo, primero debes ignorar las rutas registradas por Passport agregando Passport::ignoreRoutes al método register de la clase AppServiceProvider de tu aplicación:

use Laravel\Passport\Passport;
 
/**
* Registra cualquier servicio de aplicación.
*/
public function register(): void
{
Passport::ignoreRoutes();
}

Luego, puedes copiar las rutas definidas por Passport en su archivo de rutas a tu archivo routes/web.php de la aplicación y modificarlas a tu gusto:

Route::group([
'as' => 'passport.',
'prefix' => config('passport.path', 'oauth'),
'namespace' => '\Laravel\Passport\Http\Controllers',
], function () {
// Rutas de Passport...
});

Emisión de Tokens de Acceso

El uso de OAuth2 a través de códigos de autorización es como 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 tu servidor, donde este aprobará o denegará la solicitud de emitir un token de acceso al cliente.

Gestión de Clientes

Primero, los desarrolladores que construyen aplicaciones que necesitan interactuar con la API de tu aplicación deberán registrar su aplicación creando un "cliente". Por lo general, 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 mediante el comando Artisan passport:client. Este comando se puede utilizar para crear tus propios clientes para probar la funcionalidad de OAuth2. Cuando ejecutas 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

Redirect URLs

Si deseas permitir múltiples URL de redirección para tu cliente, puedes especificarlas utilizando una lista delimitada por comas cuando se te pida la URL por el comando passport:client. Cualquier URL que contenga comas debe codificarse:

http://example.com/callback,http://examplefoo.com/callback

API JSON

Dado que los usuarios de tu aplicación no podrán utilizar el comando client, Passport proporciona una API JSON que puedes utilizar para crear clientes. Esto te ahorra tener que codificar manualmente controladores para crear, actualizar y eliminar clientes.

Sin embargo, deberás combinar la API JSON de Passport con tu propio frontend para proporcionar un panel de control para que tus usuarios gestionen sus clientes. A continuación, revisaremos todos los puntos finales de la API para gestionar clientes. Para mayor comodidad, utilizaremos Axios para demostrar cómo realizar solicitudes HTTP a los puntos finales.

La API JSON está protegida por los middleware web y auth; por lo tanto, solo se puede llamar desde tu propia aplicación. No se puede llamar desde una fuente externa.

GET /oauth/clients

Esta ruta devuelve todos los clientes para el usuario autenticado. Esto es útil principalmente para listar todos los clientes del usuario para que puedan editarlos o eliminarlos:

axios.get('/oauth/clients')
.then(response => {
console.log(response.data);
});

POST /oauth/clients

Esta ruta se utiliza para crear nuevos clientes. Requiere dos piezas de datos: el nombre del cliente y una URL de redirección. La URL de redirección es a donde se redirigirá al usuario después de aprobar o denegar una solicitud de autorización.

Cuando se crea un cliente, se le asignará un ID de cliente y un secreto de cliente. Estos valores se utilizarán al solicitar tokens de acceso desde tu aplicación. La ruta de creación de clientes devolverá la nueva instancia del cliente:

const data = {
name: 'Client Name',
redirect: 'http://example.com/callback'
};
 
axios.post('/oauth/clients', data)
.then(response => {
console.log(response.data);
})
.catch (response => {
// Lista los errores en la respuesta...
});

PUT /oauth/clients/{client-id}

Esta ruta se utiliza para actualizar clientes. Requiere dos piezas de datos: el nombre del cliente y una URL de redirección. La URL de redirección es a donde se redirigirá al usuario después de aprobar o denegar una solicitud de autorización. La ruta devolverá la instancia actualizada del cliente:

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 => {
// Lista los errores en la respuesta...
});

DELETE /oauth/clients/{client-id}

Esta ruta se utiliza para eliminar clientes:

axios.delete('/oauth/clients/' + clientId)
.then(response => {
// ...
});

Solicitud de Tokens

Redireccionamiento para la Autorización

Una vez que se haya creado un cliente, los desarrolladores pueden utilizar su ID de cliente y secreto para solicitar un código de autorización y un token de acceso desde tu aplicación. Primero, la aplicación consumidora debe realizar una solicitud de redirección a la ruta /oauth/authorize de tu aplicación de la siguiente manera:

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 se puede utilizar para especificar el comportamiento de autenticación de la aplicación Passport.

Si el valor de prompt es none, Passport siempre generará un error de autenticación si el usuario no está autenticado con la aplicación Passport. Si el valor es consentimiento, Passport siempre mostrará la pantalla de aprobación de autorización, incluso si todos los scopes ya se otorgaron previamente a la aplicación consumidora. Cuando el valor es inicio de sesión, la aplicación Passport siempre solicitará al usuario que vuelva a iniciar sesión en la aplicación, incluso si ya tienen una sesión existente.

Si no se proporciona ningún valor de prompt, al usuario se le solicitará autorización solo si previamente no ha autorizado el acceso a la aplicación consumidora para los scopes solicitados.

Nota Recuerda 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 en función del valor del parámetro prompt (si está presente) y puede mostrar una plantilla al usuario que le permita aprobar o denegar la solicitud de autorización. Si aprueban la solicitud, serán redirigidos de nuevo a la redirect_uri que especificó la aplicación consumidora. La redirect_uri debe coincidir con la URL de redirect que se especificó al crear el cliente.

Si deseas personalizar la pantalla de aprobación de autorización, puedes publicar las vistas de Passport utilizando el comando Artisan vendor:publish. Las vistas publicadas se colocarán en el directorio resources/views/vendor/passport:

php artisan vendor:publish --tag=passport-views

A veces puede que desees omitir la solicitud de autorización, como al autorizar un cliente de primera parte. Puedes lograr esto extendiendo el modelo Client y definiendo un método skipsAuthorization. Si skipsAuthorization devuelve true, se aprobará el cliente y se redirigirá al usuario de nuevo a la redirect_uri de inmediato, a menos que la aplicación consumidora haya establecido explícitamente el parámetro prompt al redirigir para la autorización:

<?php
 
namespace App\Models\Passport;
 
use Laravel\Passport\Client as BaseClient;
 
class Client extends BaseClient
{
/**
* Determina si el cliente debe omitir el mensaje de autorización.
*/
public function skipsAuthorization(): bool
{
return $this->firstParty();
}
}

Conversión de Códigos de Autorización a Tokens de Acceso

Si el usuario aprueba la solicitud de autorización, se redirigirán de nuevo a la aplicación consumidora. El consumidor debería verificar primero el parámetro state contra el valor que se almacenó antes de la redirección. Si el parámetro state coincide, el consumidor debería enviar una solicitud POST a tu aplicación para solicitar un token de acceso. La solicitud debe incluir el código de autorización que emitió tu aplicación cuando el usuario aprobó la solicitud 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,
'Invalid state value.'
);
 
$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();
});

Esta ruta /oauth/token devolverá una respuesta JSON que contiene los atributos access_token, refresh_token y expires_in. El atributo expires_in contiene la cantidad de segundos hasta que caduque el token de acceso.

Nota Al igual que la ruta /oauth/authorize, la ruta /oauth/token ya está 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. Puedes combinar esto con tu propio frontend para ofrecer a tus usuarios un panel para gestionar los tokens de acceso. Para mayor comodidad, utilizaremos Axios para demostrar cómo realizar solicitudes HTTP a los puntos finales. La API JSON está protegida por los middleware web y auth; por lo tanto, solo se puede llamar 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 puedan revocarlos:

axios.get('/oauth/tokens')
.then(response => {
console.log(response.data);
});

DELETE /oauth/tokens/{token-id}

Esta ruta se puede utilizar para revocar tokens de acceso autorizados y sus tokens de actualización relacionados:

axios.delete('/oauth/tokens/' + tokenId);

Renovación de Tokens

Si tu aplicación emite tokens de acceso de corta duración, los usuarios deberán actualizar sus tokens de acceso mediante el 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();

Esta ruta /oauth/token devolverá una respuesta JSON que contiene los atributos access_token, refresh_token y expires_in. El atributo expires_in contiene la cantidad de segundos hasta que caduque el token de acceso.

Revocación de Tokens

Puedes revocar un token utilizando el método revokeAccessToken en el Laravel\Passport\TokenRepository. Puedes revocar los tokens de actualización de un token utilizando el método revokeRefreshTokensByAccessTokenId en el Laravel\Passport\RefreshTokenRepository. Estas clases se pueden resolver utilizando el contenedor de servicios de Laravel:

use Laravel\Passport\TokenRepository;
use Laravel\Passport\RefreshTokenRepository;
 
$tokenRepository = app(TokenRepository::class);
$refreshTokenRepository = app(RefreshTokenRepository::class);
 
// Revocar un token de acceso...
$tokenRepository->revokeAccessToken($tokenId);
 
// Revocar todos los tokens de actualización del token...
$refreshTokenRepository->revokeRefreshTokensByAccessTokenId($tokenId);

Purga de Tokens

Cuando los tokens se han revocado o han caducado, es posible que desees eliminarlos de la base de datos. El comando Artisan passport:purge incluido en Passport puede hacer esto por ti:

# Purge revoked and expired tokens and auth codes...
php artisan passport:purge
 
# Only purge tokens expired for more than 6 hours...
php artisan passport:purge --hours=6
 
# 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 puedes configurar un trabajo programado en la clase App\Console\Kernel de la aplicación para purgar automáticamente tus tokens según un horario:

/**
* Define el horario de comandos de la aplicación.
*/
protected function schedule(Schedule $schedule): void
{
$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 tu API. Esta concesión debe utilizarse cuando no puedes garantizar que el secreto del cliente se almacenará de manera confidencial o para mitigar la amenaza de que el código de autorización sea interceptado por un atacante. Una combinación de un "verificador de código" y un "reto de código" reemplaza el secreto del cliente al intercambiar el código de autorización por un token de acceso.

Creación del Cliente

Antes de que tu aplicación pueda emitir tokens mediante la concesión de código de autorización con PKCE, deberás crear un cliente habilitado para PKCE. Puedes hacer esto utilizando 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

Dado que esta concesión de autorización no proporciona un secreto de cliente, los desarrolladores deberán generar una combinación de un verificador de código y un reto de código para solicitar un token.

El verificador de código debería ser una cadena aleatoria de entre 43 y 128 caracteres que contenga letras, números y los caracteres \"-\", \".\", \"_\", \"~\", según se define en la especificación RFC 7636.

El reto de código debería ser una cadena codificada en Base64 con caracteres seguros para URL y nombres de archivos. Se deben eliminar los caracteres '=' finales y no deben haber saltos de línea, espacios en blanco ni otros caracteres adicionales.

$encoded = base64_encode(hash('sha256', $code_verifier, true));
 
$codeChallenge = strtr(rtrim($encoded, '='), '+/', '-_');

Redireccionamiento para la Autorización

Una vez que se ha creado un cliente, puedes usar el ID de cliente y el verificador y reto de código generados para solicitar un código de autorización y un token de acceso de tu aplicación. Primero, la aplicación consumidora debería realizar una solicitud 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 a Tokens de Acceso

Si el usuario aprueba la solicitud de autorización, se redirigirán de nuevo a la aplicación consumidora. El consumidor debería verificar el parámetro state contra el valor que se almacenó antes de la redirección, como en la concesión estándar de código de autorización.

Si el parámetro state coincide, el consumidor debería enviar una solicitud POST a tu aplicación para solicitar un token de acceso. La solicitud debe incluir el código de autorización que emitió tu 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();
});

Tokens de Concesión de Contraseña

Advertencia Ya no recomendamos el uso de tokens de concesión de contraseña. En su lugar, debería elegir un tipo de concesión que actualmente recomienda OAuth2 Server.

La concesión de contraseña OAuth2 permite que otros clientes internos, como una aplicación móvil, obtengan un token de acceso utilizando una dirección de correo electrónico/nombre de usuario y una contraseña. Esto le permite emitir tokens de acceso de manera segura a sus clientes internos sin requerir que sus usuarios atraviesen todo el flujo de redirección del 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 mediante la concesión de contraseña, deberá crear un cliente de concesión de contraseña. Puede hacerlo utilizando el comando Artisan passport:client con la opción --password. Si ya ejecutó el comando passport:install, no es necesario que ejecute 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, esta ruta ya está registrada por Passport, así 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' => '[email protected]',
'password' => 'my-password',
'scope' => '',
]);
 
return $response->json();

Nota Recuerda que, por defecto, los tokens de acceso tienen una larga duración. Sin embargo, puedes configurar la duración máxima de los tokens de acceso si es necesario.

Solicitud de Todos los Alcances

Cuando use la concesión de contraseña o la concesión de credenciales de cliente, es posible que desee autorizar el token para todas las scopes admitidas por su aplicación. Puede hacer esto solicitando la scope *. Si solicita la scope *, el método can en la instancia del token siempre devolverá true. Esta scope solo puede asignarse a un token que se emitió mediante la concesión de 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' => '[email protected]',
'password' => 'my-password',
'scope' => '*',
]);

Personalización del Proveedor de Usuarios

Si su aplicación utiliza más de un proveedor de usuarios de autenticación, puede especificar qué proveedor de usuarios 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 proporcionado debe coincidir con un proveedor válido definido en el archivo de configuración config/auth.php de su aplicación. Luego puede proteger su ruta mediante middleware para asegurarse de que solo los usuarios del proveedor especificado en el guardia estén autorizados.

Personalización del Campo de Nombre de Usuario

Al autenticarse mediante la concesión de contraseña, Passport utilizará el atributo email de su modelo autenticable como el "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;
 
/**
* Encuentra la instancia de usuario para el nombre de usuario dado.
*/
public function findForPassport(string $username): User
{
return $this->where('username', $username)->first();
}
}

Personalización de la Validación de Contraseñas

Al autenticarse mediante la concesión de contraseña, Passport utilizará el atributo password de su modelo para validar la contraseña proporcionada. Si su modelo no tiene un atributo 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;
 
/**
* Valida la contraseña del usuario para la concesión de contraseña de Passport.
*/
public function validateForPassportPasswordGrant(string $password): bool
{
return Hash::check($password, $this->password);
}
}

Tokens de Concesión Implícita

Advertencia Ya no recomendamos el uso de tokens de concesión implícita. En su lugar, debería elegir un tipo de concesión que actualmente recomienda 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 aplicaciones JavaScript o móviles donde las credenciales del cliente no se pueden almacenar de manera 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:

/**
* Registra cualquier servicio de autenticación/autorización.
*/
public function boot(): void
{
Passport::enableImplicitGrant();
}

Una vez que se haya habilitado la concesión, los desarrolladores pueden utilizar su ID de cliente para solicitar un token de acceso de su aplicación. La aplicación consumidora debe realizar una solicitud de redirección a la ruta /oauth/authorize de su aplicación de la siguiente manera:

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 Recuerda que la ruta /oauth/authorize ya está definida por Passport. No es necesario definir manualmente esta ruta.

Tokens de 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 usar esta concesión en un trabajo programado que realiza tareas de mantenimiento a través de una API.

Antes de que su aplicación pueda emitir tokens mediante la concesión de credenciales de cliente, deberá crear un cliente de concesión de credenciales de cliente. Puede hacerlo utilizando la opción --client del comando passport:client Artisan:

php artisan passport:client --client

A continuación, para utilizar este tipo de concesión, puede agregar el middleware CheckClientCredentials a la propiedad $middlewareAliases del archivo app/Http/Kernel.php de su aplicación:

use Laravel\Passport\Http\Middleware\CheckClientCredentials;
 
protected $middlewareAliases = [
'client' => CheckClientCredentials::class,
];

Luego, adjunte el middleware a una ruta:

Route::get('/orders', function (Request $request) {
...
})->middleware('client');

Para restringir el acceso a la ruta a scopes específicas, puede proporcionar una lista de scopes requeridas, separadas por comas, 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 mediante este tipo de concesión, realice una solicitud al punto final 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, es posible que sus usuarios deseen emitir tokens por sí mismos sin tener que pasar por todo el flujo de redirección del código de autorización. Permitir a los usuarios emitir tokens por sí mismos a través de la interfaz de usuario de su aplicación puede ser útil para permitir que los usuarios experimenten con su API o puede servir como un enfoque más sencillo para emitir tokens de acceso en general.

Nota Si tu aplicación utiliza principalmente Passport para emitir tokens de acceso personal, considera usar Laravel Sanctum, la biblioteca de primera clase de Laravel para emitir 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, deberá crear un cliente de acceso personal. Puede hacerlo ejecutando el comando Artisan passport:client con la opción --personal. Si ya ejecutó el comando passport:install, no es necesario que ejecute este comando:

php artisan passport:client --personal

Después de crear su cliente de acceso personal, coloque el ID de cliente y el valor de secreto en texto plano en el archivo .env de su 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 específico utilizando el método createToken en la instancia del modelo App\Models\User. El método createToken acepta el nombre del token como su primer argumento y un array opcional de scopes como su segundo argumento:

use App\Models\User;
 
$user = User::find(1);
 
// Creación de un token sin alcances...
$token = $user->createToken('Token Name')->accessToken;
 
// Creación de un token con alcances...
$token = $user->createToken('My Token', ['place-orders'])->accessToken;

API JSON

Passport también incluye una API JSON para gestionar tokens de acceso personal. Puede combinar esto con su propio frontend para ofrecer a sus usuarios un panel para gestionar tokens de acceso personal. A continuación, revisaremos todos los puntos finales de la API para gestionar tokens de acceso personal. Para mayor comodidad, usaremos Axios para demostrar cómo hacer solicitudes HTTP a los puntos finales.

La API JSON está protegida por los middlewares web y auth, por lo tanto, solo puede ser llamada desde su propia aplicación. No se puede llamar desde una fuente externa.

GET /oauth/scopes

Esta ruta devuelve todas las scopes definidas para su aplicación. Puede utilizar esta ruta para listar las scopes 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 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 piezas de datos: el name del token y las scopes que se deben asignar al token:

const data = {
name: 'Token Name',
scopes: []
};
 
axios.post('/oauth/personal-access-tokens', data)
.then(response => {
console.log(response.data.accessToken);
})
.catch (response => {
// Lista los errores en la respuesta...
});

DELETE /oauth/personal-access-tokens/{token-id}

Esta ruta se puede utilizar para revocar tokens de acceso personal:

axios.delete('/oauth/personal-access-tokens/' + tokenId);

Protección de Rutas

A través de Middleware

Passport incluye un guardia de autenticación que validará los tokens de acceso en las solicitudes entrantes. Una vez que haya configurado el guardia api para usar el controlador passport, solo necesita especificar el middleware auth:api en las rutas que requieran un token de acceso válido:

Route::get('/user', function () {
// ...
})->middleware('auth:api');

Advertencia Si está utilizando el concesión de credenciales de cliente, debería usar el middleware client para proteger sus rutas en lugar del middleware auth:api.

Múltiples Guardias de Autenticación

Si su aplicación autentica diferentes tipos de usuarios que tal vez usan modelos Eloquent completamente diferentes, es probable que necesite definir una configuración de guardia para cada tipo de proveedor de usuarios en su aplicación. Esto le permite proteger solicitudes destinadas a proveedores de usuarios específicos. Por ejemplo, dada la siguiente configuración de guardia en el archivo de configuración config/auth.php:

'api' => [
'driver' => 'passport',
'provider' => 'users',
],
 
'api-customers' => [
'driver' => 'passport',
'provider' => 'customers',
],

La siguiente ruta utilizará el guardia api-customers, que utiliza el proveedor de usuarios customers, para autenticar las solicitudes entrantes:

Route::get('/customer', function () {
// ...
})->middleware('auth:api-customers');

Nota Para obtener más información sobre el uso de varios proveedores de usuarios con Passport, consulta la documentación del grant de contraseña.

Paso del Token de Acceso

Al llamar a rutas protegidas por Passport, los consumidores de la API de su aplicación deben especificar su token de acceso como un token Bearer en el encabezado Authorization de su solicitud. Por ejemplo, al usar la biblioteca Guzzle HTTP:

use Illuminate\Support\Facades\Http;
 
$response = Http::withHeaders([
'Accept' => 'application/json',
'Authorization' => 'Bearer '.$accessToken,
])->get('https://passport-app.test/api/user');
 
return $response->json();

Alcances de Token

Las scopes permiten a los clientes de tu API solicitar un conjunto específico de permisos al solicitar autorización para acceder a una cuenta. Por ejemplo, si estás construyendo una aplicación de comercio electrónico, no todos los consumidores de la API necesitarán la capacidad de realizar pedidos. En cambio, puedes permitir a los consumidores solicitar autorización para acceder solo a los estados de envío de pedidos. En otras palabras, las scopes permiten a los usuarios de tu aplicación limitar las acciones que una aplicación de terceros puede realizar en su nombre.

Definición de Alcances

Puedes definir las scopes de tu API utilizando el método Passport::tokensCan en el método boot de la clase App\Providers\AuthServiceProvider de tu aplicación. El método tokensCan acepta un array de nombres de scope y descripciones de scope. La descripción del scope puede ser cualquier cosa que desees y se mostrará a los usuarios en la pantalla de aprobación de autorización:

/**
* Registra cualquier servicio de autenticación/autorización.
*/
public function boot(): void
{
Passport::tokensCan([
'place-orders' => 'Place orders',
'check-status' => 'Check order status',
]);
}

Alcance Predeterminado

Si un cliente no solicita scopes específicos, puedes configurar tu servidor Passport para adjuntar scope(s) predeterminados al token mediante el método setDefaultScope. Por lo general, deberías llamar a este método desde el método boot de la clase App\Providers\AuthServiceProvider de tu aplicación:

use Laravel\Passport\Passport;
 
Passport::tokensCan([
'place-orders' => 'Place orders',
'check-status' => 'Check order status',
]);
 
Passport::setDefaultScope([
'check-status',
'place-orders',
]);

Nota Las scopes predeterminadas de Passport no se aplican a los tokens de acceso personal generados por el usuario.

Asignación de Alcances a Tokens

Cuando Solicitas Códigos de Autorización

Al solicitar un token de acceso mediante el grant de código de autorización, los consumidores deben especificar sus scopes deseados como el parámetro de cadena de consulta scope. El parámetro scope debe ser una lista delimitada por espacios de scopes:

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

Cuando Emites Tokens de Acceso Personal

Si estás emitiendo tokens de acceso personal utilizando el método createToken del modelo App\Models\User, puedes pasar el array de scopes deseados como segundo argumento al método:

$token = $user->createToken('My Token', ['place-orders'])->accessToken;

Verificación de Alcances

Passport incluye dos middlewares que se pueden usar para verificar que una solicitud entrante esté autenticada con un token que haya sido otorgado con un determinado scope. Para empezar, agrega los siguientes middlewares al array $middlewareAliases en el archivo app/Http/Kernel.php de tu aplicación:

'scopes' => \Laravel\Passport\Http\Middleware\CheckScopes::class,
'scope' => \Laravel\Passport\Http\Middleware\CheckForAnyScope::class,

Verificar Todos los Alcances

El middleware scopes se puede asignar a una ruta para verificar que el token de acceso de la solicitud entrante tenga todos los scopes enumerados:

Route::get('/orders', function () {
// El token de acceso tiene los alcances "check-status" y "place-orders"...
})->middleware(['auth:api', 'scopes:check-status,place-orders']);

Verificar Cualquier Alcance

El middleware scope se puede asignar a una ruta para verificar que el token de acceso de la solicitud entrante tenga al menos uno de los scopes enumerados:

Route::get('/orders', function () {
// El token de acceso tiene el alcance "check-status" o "place-orders"...
})->middleware(['auth:api', 'scope:check-status,place-orders']);

Verificación de Alcances en una Instancia de Token

Una vez que una solicitud autenticada con token de acceso ha ingresado a tu aplicación, aún puedes verificar si el token tiene un determinado scope 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 Alcance 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 todas las scopes definidas como instancias de Laravel\Passport\Scope:

Passport::scopes();

El método scopesFor devolverá un array de instancias de Laravel\Passport\Scope que coinciden con los IDs / nombres dados:

Passport::scopesFor(['place-orders', 'check-status']);

Puedes determinar si se ha definido un determinado scope utilizando el método hasScope:

Passport::hasScope('place-orders');

Consumir tu API con JavaScript

Cuando construyes una API, puede ser extremadamente útil poder consumir tu propia API desde tu aplicación de JavaScript. Este enfoque para el desarrollo de API permite que tu propia aplicación consuma la misma API que estás 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 deseas consumir tu API desde tu aplicación de JavaScript, necesitarías enviar manualmente un token de acceso a la aplicación y pasarlo con cada solicitud a tu aplicación. Sin embargo, Passport incluye un middleware que puede encargarse de esto por ti. Todo lo que necesitas hacer es agregar el middleware CreateFreshApiToken a tu grupo de middleware web en el archivo app/Http/Kernel.php:

'web' => [
// Otro middleware...
\Laravel\Passport\Http\Middleware\CreateFreshApiToken::class,
],

Advertencia Asegúrese de que el middleware CreateFreshApiToken sea el último middleware enumerado en su pila de middlewares.

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 desde tu aplicación de JavaScript. El JWT tiene una vigencia igual al valor de configuración de session.lifetime. Ahora, dado que el navegador enviará automáticamente la cookie con todas las solicitudes subsiguientes, puedes realizar solicitudes a la API de tu aplicación sin pasar explícitamente un token de acceso:

axios.get('/api/user')
.then(response => {
console.log(response.data);
});

Personalización del Nombre de la Cookie

Si es necesario, puedes personalizar el nombre de la cookie laravel_token utilizando el método Passport::cookie. Por lo general, este método debe llamarse desde el método boot de la clase App\Providers\AuthServiceProvider de tu aplicación:

/**
* Registra cualquier servicio de autenticación/autorización.
*/
public function boot(): void
{
Passport::cookie('custom_name');
}

Protección CSRF

Al utilizar este método de autenticación, deberás asegurarte de incluir un encabezado de token CSRF válido en tus solicitudes. El andamiaje predeterminado de JavaScript de Laravel incluye una instancia de Axios, que utilizará automáticamente el valor de la cookie cifrada XSRF-TOKEN para enviar un encabezado X-XSRF-TOKEN en las solicitudes del mismo origen.

Nota Si eliges enviar el encabezado X-CSRF-TOKEN en lugar de X-XSRF-TOKEN, deberás utilizar el token no cifrado proporcionado por csrf_token().

Eventos

Passport emite eventos al emitir tokens de acceso y tokens de actualización. Puedes utilizar estos eventos para limpiar o revocar otros tokens de acceso en tu base de datos. Si lo deseas, puedes adjuntar listeners a estos eventos en la clase App\Providers\EventServiceProvider de tu aplicación:

/**
* Los mapeos de escuchadores de eventos para la aplicación.
*
* @var array
*/
protected $listen = [
'Laravel\Passport\Events\AccessTokenCreated' => [
'App\Listeners\RevokeOldTokens',
],
 
'Laravel\Passport\Events\RefreshTokenCreated' => [
'App\Listeners\PruneOldTokens',
],
];

Pruebas

El método actingAs de Passport se puede usar para especificar el usuario actualmente autenticado y sus scopes. El primer argumento dado al método actingAs es la instancia del usuario y el segundo es un array de scopes que se deben otorgar al token del usuario:

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

El método actingAsClient de Passport se puede utilizar para especificar el cliente actualmente autenticado, así como sus scopes. El primer argumento dado al método actingAsClient es la instancia del cliente y el segundo es un array de scopes que se deben otorgar al token del cliente:

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