- Introducción
- Instalación
- Configuración
- Emisión de tokens de acceso
- Authorization Code Grant con PKCE
- Tokens con Password Grant
- Tokens con Implicit Grant
- Tokens con Client Credentials Grant
- Tokens de acceso personal
- Protegiendo rutas
- Scopes de tokens
- Consumiendo su API con JavaScript
- Eventos
- Pruebas
#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 League OAuth2 server mantenido por Andy Millington y Simon Hamp.
Esta documentación asume que ya está familiarizado con OAuth2. Si no conoce nada sobre OAuth2, considere familiarizarse primero con la terminología y las características generales de OAuth2 antes de continuar.
#¿Passport o Sanctum?
Antes de comenzar, puede que desee determinar si su aplicación se beneficiaría más usando Laravel Passport o Laravel Sanctum. Si su aplicación necesita soportar OAuth2 de forma absoluta, entonces debería usar Laravel Passport.
Sin embargo, si intenta autenticar una aplicación de una sola página, una aplicación móvil o emitir tokens API, debería usar Laravel Sanctum. Laravel Sanctum no soporta OAuth2; sin embargo, ofrece una experiencia de desarrollo de autenticación API mucho más sencilla.
#Instalación
Para comenzar, instale Passport mediante el gestor de paquetes Composer:
composer require laravel/passport
El service provider de Passport registra su propio directorio de migraciones 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 "password grant" que se usarán para generar tokens de acceso:
php artisan passport:install
Si desea usar UUIDs como valor de clave primaria del modelo Passport Client en lugar de enteros autoincrementales, instale Passport usando la opción uuids.
Después de ejecutar el comando passport:install, agregue el trait Laravel\Passport\HasApiTokens a su modelo App\Models\User. Este trait proporcionará algunos métodos auxiliares a su modelo que permiten inspeccionar el token y los scopes del usuario autenticado. Si su modelo ya usa 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 su aplicación, debe definir un guardia de autenticación api y establecer la opción driver a passport. Esto indicará a su aplicación que use el TokenGuard de Passport al autenticar solicitudes API entrantes:
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'passport',
'provider' => 'users',
],
],
#UUIDs para clientes
También puede ejecutar el comando passport:install con la opción --uuids. Esta opción le indicará a Passport que desea usar UUIDs en lugar de enteros autoincrementales como valores de clave primaria del modelo Client de Passport. Tras ejecutar el comando passport:install con la opción --uuids, se le proporcionarán instrucciones adicionales sobre cómo deshabilitar las migraciones predeterminadas de Passport:
php artisan passport:install --uuids
#Desplegando Passport
Al desplegar Passport en los servidores de su aplicación por primera vez, probablemente necesite ejecutar el comando passport:keys. Este comando genera las claves de cifrado que Passport necesita para generar tokens de acceso. Las claves generadas normalmente no se mantienen en el control de versiones:
php artisan passport:keys
Si es necesario, puede definir la ruta desde donde se deben cargar las claves de Passport. Puede usar el método Passport::loadKeysFrom para lograr esto. Normalmente, este método debe llamarse desde el método boot de la clase App\Providers\AuthServiceProvider de su aplicación:
/**
* Registrar cualquier servicio de autenticación / autorización.
*/
public function boot(): void
{
Passport::loadKeysFrom(__DIR__.'/../secrets/oauth');
}
#Cargando claves desde el entorno
Alternativamente, puede publicar el archivo de configuración de Passport usando el comando Artisan vendor:publish:
php artisan vendor:publish --tag=passport-config
Después de publicar el archivo de configuración, puede cargar las claves de cifrado de su 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 va a usar las migraciones por defecto de Passport, debe llamar al método Passport::ignoreMigrations en el método register de su clase App\Providers\AppServiceProvider. Puede exportar las migraciones por defecto usando el comando Artisan vendor:publish:
php artisan vendor:publish --tag=passport-migrations
#Actualizando Passport
Al actualizar a una nueva versión mayor de Passport, es importante que revise cuidadosamente la guía de actualización.
#Configuración
#Hashing del secreto del cliente
Si desea que los secretos de sus clientes se almacenen hasheados en la 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 habilitado, todos los secretos de sus clientes solo serán visibles para el usuario inmediatamente después de ser creados. Dado que el valor del secreto 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 expiran después de un año. Si desea configurar una duración más larga o más corta para los tokens, puede usar los métodos tokensExpireIn, refreshTokensExpireIn y personalAccessTokensExpireIn. Estos métodos deben llamarse desde el método boot de la clase App\Providers\AuthServiceProvider de su aplicación:
/**
* Registrar 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));
}
Las columnas expires_at en las tablas de base de datos de Passport son de solo lectura y solo para propósitos de visualización. Al emitir tokens, Passport almacena la información de expiración dentro de los tokens firmados y cifrados. Si necesita invalidar un token, debe revocarlo.
#Sobrescribiendo modelos por defecto
Puede extender libremente los modelos usados internamente por Passport definiendo su propio modelo y extendiendo el modelo correspondiente de Passport:
use Laravel\Passport\Client as PassportClient;
class Client extends PassportClient
{
// ...
}
Después de definir su modelo, puede indicarle a Passport que use su modelo personalizado mediante la clase Laravel\Passport\Passport. Normalmente, debe 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\RefreshToken;
use App\Models\Passport\Token;
/**
* Registrar 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);
}
#Sobrescribiendo rutas
A veces puede que desee personalizar las rutas definidas por Passport. Para lograr esto, primero debe ignorar las rutas registradas por Passport añadiendo Passport::ignoreRoutes al método register de la clase AppServiceProvider de su aplicación:
use Laravel\Passport\Passport;
/**
* Registrar cualquier servicio de la aplicación.
*/
public function register(): void
{
Passport::ignoreRoutes();
}
Luego, puede copiar las rutas definidas por Passport en su archivo de rutas 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 () {
// Rutas de Passport...
});
#Emisión de tokens de acceso
Usar OAuth2 mediante códigos de autorización es la forma en que la mayoría de los desarrolladores están familiarizados con OAuth2. Al usar códigos de autorización, una aplicación cliente redirigirá a un usuario a su servidor donde este aprobará o denegará la solicitud para emitir un token de acceso al cliente.
#Gestión de clientes
Primero, los desarrolladores que construyan aplicaciones que necesiten interactuar con la API de su aplicación deberán registrar su aplicación creando un "cliente". Normalmente, esto consiste en proporcionar el nombre de su aplicación y una URL a la que su 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 usando el comando Artisan passport:client. Este comando puede usarse para crear sus propios clientes para probar la funcionalidad OAuth2. Al ejecutar el comando client, Passport le pedirá más información sobre su cliente y le proporcionará un ID y un secreto de cliente:
php artisan passport:client
URLs de redirección
Si desea permitir múltiples URLs de redirección para su cliente, puede especificarlas usando una lista separada por comas cuando se le solicite la URL en el comando passport:client. Cualquier URL que contenga comas debe estar codificada en 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 usar para crear clientes. Esto le ahorra la molestia de tener que codificar manualmente controladores para crear, actualizar y eliminar clientes.
Sin embargo, deberá combinar la API JSON de Passport con su propio frontend para ofrecer un panel donde sus usuarios puedan gestionar sus clientes. A continuación, revisaremos todos los endpoints de la API para gestionar clientes. Para mayor comodidad, usaremos Axios para demostrar cómo hacer solicitudes HTTP a los endpoints.
La API JSON está protegida por los middleware web y auth; por lo tanto, solo puede ser llamada desde su propia aplicación. No puede ser llamada desde una fuente externa.
#GET /oauth/clients
Esta ruta devuelve todos los clientes del usuario autenticado. Esto es principalmente útil para listar todos los clientes del usuario para que pueda editarlos o eliminarlos:
axios.get('/oauth/clients')
.then(response => {
console.log(response.data);
});
#POST /oauth/clients
Esta ruta se usa para crear nuevos clientes. Requiere dos datos: el name del cliente y una URL de redirect. La URL de redirect es a donde el usuario será redirigido después de aprobar o denegar una solicitud de autorización.
Cuando se crea un cliente, se le asignará un ID y un secreto de cliente. Estos valores se usarán al solicitar tokens de acceso a su aplicación. La ruta de creación 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 => {
// Listar errores en la respuesta...
});
#PUT /oauth/clients/{client-id}
Esta ruta se usa para actualizar clientes. Requiere dos datos: el name del cliente y una URL de redirect. La URL de redirect es a donde el usuario será redirigido 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 => {
// Listar errores en la respuesta...
});
#DELETE /oauth/clients/{client-id}
Esta ruta se usa para eliminar clientes:
axios.delete('/oauth/clients/' + clientId)
.then(response => {
// ...
});
#Solicitud de tokens
#Redirigiendo para autorización
Una vez que un cliente ha sido creado, los desarrolladores pueden usar su ID y secreto 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 solicitud de redirección a la ruta /oauth/authorize de su aplicación así:
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", o "login"
]);
return redirect('http://passport-app.test/oauth/authorize?'.$query);
});
El parámetro prompt puede usarse 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 todos los scopes ya fueron concedidos previamente a la aplicación consumidora. Cuando el valor es login, la aplicación Passport siempre solicitará al usuario que vuelva a iniciar sesión, incluso si ya tiene una sesión activa.
Si no se proporciona un valor para prompt, el usuario solo será solicitado para autorización si no ha autorizado previamente el acceso a la aplicación consumidora para los scopes solicitados.
Recuerde, la ruta /oauth/authorize ya está definida por Passport. No necesita definir esta ruta manualmente.
#Aprobando la solicitud
Al recibir solicitudes de autorización, Passport responderá automáticamente según el valor del parámetro prompt (si está presente) y puede mostrar una plantilla al usuario para que apruebe o deniegue 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 redirect que se especificó cuando se creó el cliente.
Si desea personalizar la pantalla de aprobación de autorización, puede publicar las vistas de Passport usando 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 desee omitir el prompt de autorización, como cuando autoriza un cliente de primera parte. Puede lograr esto extendiendo el modelo Client y definiendo un método skipsAuthorization. Si skipsAuthorization devuelve true, el cliente será aprobado y el usuario será redirigido inmediatamente a la redirect_uri, 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
{
/**
* Determinar si el cliente debe omitir el prompt de autorización.
*/
public function skipsAuthorization(): bool
{
return $this->firstParty();
}
}
#Convirtiendo 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. La aplicación consumidora debe primero verificar el parámetro state comparándolo con el valor que se almacenó antes de la redirección. Si el parámetro state coincide, la aplicación consumidora debe enviar una solicitud POST a su aplicación para solicitar un token de acceso. La solicitud debe incluir el código de autorización que su aplicación emitió 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,
'Valor de estado inválido.'
);
$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 el número de segundos hasta que el token de acceso expire.
Al igual que la ruta /oauth/authorize, la ruta /oauth/token está definida para usted por Passport. No es necesario definir esta ruta manualmente.
#API JSON
Passport también incluye una API JSON para gestionar tokens de acceso autorizados. Puede combinar esto con su propio frontend para ofrecer a sus usuarios un panel para gestionar tokens de acceso. Para mayor comodidad, usaremos Axios para demostrar cómo hacer solicitudes HTTP a los endpoints. La API JSON está protegida por los middleware web y auth; por lo tanto, solo puede ser llamada desde su propia aplicación.
#GET /oauth/tokens
Esta ruta devuelve todos los tokens de acceso autorizados que el usuario autenticado ha creado. Esto es principalmente útil para listar todos los tokens del usuario para que pueda revocarlos:
axios.get('/oauth/tokens')
.then(response => {
console.log(response.data);
});
#DELETE /oauth/tokens/{token-id}
Esta ruta puede usarse para revocar tokens de acceso autorizados y sus tokens de refresco relacionados:
axios.delete('/oauth/tokens/' + tokenId);
#Refrescando tokens
Si su aplicación emite tokens de acceso de corta duración, los usuarios necesitarán refrescar sus tokens de acceso mediante el token de refresco 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 el número de segundos hasta que el token de acceso expire.
#Revocando tokens
Puede revocar un token usando el método revokeAccessToken en el Laravel\Passport\TokenRepository. Puede revocar los tokens de refresco de un token usando el método revokeRefreshTokensByAccessTokenId en el Laravel\Passport\RefreshTokenRepository. Estas clases pueden resolverse usando 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 refresco del token...
$refreshTokenRepository->revokeRefreshTokensByAccessTokenId($tokenId);
#Purgando tokens
Cuando los tokens han sido revocados o expirados, puede que desee purgarlos de la base de datos. El comando Artisan passport:purge incluido en Passport puede hacer esto por usted:
# Purgar tokens y códigos de autorización revocados y expirados...
php artisan passport:purge
# Purgar solo tokens expirados hace más de 6 horas...
php artisan passport:purge --hours=6
# Purgar solo tokens y códigos de autorización revocados...
php artisan passport:purge --revoked
# Purgar solo tokens y códigos de autorización expirados...
php artisan passport:purge --expired
También puede configurar un job programado en la clase App\Console\Kernel de su aplicación para podar automáticamente sus tokens según un horario:
/**
* Definir el horario de comandos de la aplicación.
*/
protected function schedule(Schedule $schedule): void
{
$schedule->command('passport:purge')->hourly();
}
#Authorization Code Grant con PKCE
El Authorization Code grant 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. Este grant debe usarse cuando no puede garantizar que el secreto del cliente se almacene de forma confidencial o para mitigar la amenaza de que el código de autorización sea interceptado por un atacante. Una combinación de un "code verifier" y un "code challenge" reemplaza el secreto del cliente al intercambiar el código de autorización por un token de acceso.
#Creando el cliente
Antes de que su aplicación pueda emitir tokens mediante el authorization code grant con PKCE, deberá crear un cliente habilitado para PKCE. Puede hacerlo usando el comando Artisan passport:client con la opción --public:
php artisan passport:client --public
#Solicitando tokens
#Code Verifier y Code Challenge
Como esta concesión de autorización no proporciona un secreto de cliente, los desarrolladores deberán generar una combinación de un code verifier y un code challenge para solicitar un token.
El code verifier debe ser una cadena aleatoria de entre 43 y 128 caracteres que contenga letras, números y los caracteres "-", ".", "_", "~", según lo definido en la especificación RFC 7636.
El code challenge debe ser una cadena codificada en Base64 con caracteres seguros para URL y nombres de archivo. Los caracteres '=' al final deben eliminarse y no debe haber saltos de línea, espacios en blanco ni otros caracteres adicionales.
$encoded = base64_encode(hash('sha256', $code_verifier, true));
$codeChallenge = strtr(rtrim($encoded, '='), '+/', '-_');
#Redirigiendo para la autorización
Una vez que se ha creado un cliente, puede usar el ID del cliente y el code verifier y code challenge generados para solicitar un código de autorización y un token de acceso desde su aplicación. Primero, la aplicación consumidora debe hacer una solicitud de redirección a la ruta /oauth/authorize de su 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", o "login"
]);
return redirect('http://passport-app.test/oauth/authorize?'.$query);
});
#Convirtiendo 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 state contra el valor que se almacenó antes de la redirección, como en el flujo estándar de Authorization Code Grant.
Si el parámetro state coincide, el consumidor debe emitir una solicitud POST a su aplicación para solicitar un token de acceso. La solicitud debe incluir el código de autorización emitido por su aplicación cuando el usuario aprobó la solicitud de autorización junto con el code verifier 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 con Password Grant
Ya no recomendamos usar tokens con password grant. En su lugar, debería elegir un tipo de concesión que actualmente recomienda OAuth2 Server.
El password grant de OAuth2 permite que otros clientes de primera parte, como una aplicación móvil, obtengan un token de acceso usando un correo electrónico / nombre de usuario y contraseña. Esto le permite emitir tokens de acceso de forma segura a sus clientes de primera parte sin requerir que los usuarios pasen por todo el flujo de redirección del código de autorización OAuth2.
#Creando un cliente para Password Grant
Antes de que su aplicación pueda emitir tokens mediante password grant, deberá crear un cliente para password grant. Puede hacerlo usando 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
#Solicitando tokens
Una vez que haya creado un cliente para password grant, puede solicitar un token de acceso haciendo una solicitud POST a la ruta /oauth/token con el correo electrónico y la contraseña del usuario. Recuerde, esta ruta ya está registrada por Passport, por lo que no es necesario definirla manualmente. Si la solicitud es exitosa, 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();
Recuerde, los tokens de acceso tienen una duración prolongada por defecto. Sin embargo, puede configurar la duración máxima del token de acceso si es necesario.
#Solicitando todos los scopes
Cuando use password grant o client credentials grant, puede que desee autorizar el token para todos los scopes soportados por su aplicación. Puede hacerlo solicitando el scope *. Si solicita el scope *, el método can en la instancia del token siempre devolverá true. Este scope solo puede asignarse a un token emitido usando los grants 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' => '*',
]);
#Personalizando el proveedor de usuarios
Si su aplicación usa más de un proveedor de usuarios de autenticación, puede especificar qué proveedor de usuarios usa el cliente de password grant proporcionando la opción --provider al crear el cliente con el comando artisan passport:client --password. El nombre del proveedor 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 usando middleware para asegurar que solo los usuarios del proveedor especificado por el guard estén autorizados.
#Personalizando el campo de nombre de usuario
Al autenticarse usando password grant, Passport usará 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();
}
}
#Personalizando la validación de la contraseña
Al autenticarse usando password grant, Passport usará el atributo password de su modelo para validar la contraseña dada. 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 el password grant de Passport.
*/
public function validateForPassportPasswordGrant(string $password): bool
{
return Hash::check($password, $this->password);
}
}
#Tokens con Implicit Grant
Ya no recomendamos usar tokens con implicit grant. En su lugar, debería elegir un tipo de concesión que actualmente recomienda OAuth2 Server.
El implicit grant es similar al authorization code grant; sin embargo, el token se devuelve al cliente sin intercambiar un código de autorización. Este grant se usa comúnmente para aplicaciones JavaScript o móviles donde las credenciales del cliente no pueden almacenarse de forma segura. Para habilitar el grant, llame al método enableImplicitGrant en el método boot de la clase App\Providers\AuthServiceProvider de su aplicación:
/**
* Registrar cualquier servicio de autenticación / autorización.
*/
public function boot(): void
{
Passport::enableImplicitGrant();
}
Una vez habilitado el grant, los desarrolladores pueden usar su ID de cliente para solicitar un token de acceso desde su aplicación. La aplicación consumidora debe hacer una solicitud de redirección a la ruta /oauth/authorize de su aplicación así:
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", o "login"
]);
return redirect('http://passport-app.test/oauth/authorize?'.$query);
});
Recuerde, la ruta /oauth/authorize ya está definida por Passport. No necesita definir esta ruta manualmente.
#Tokens con Client Credentials Grant
El client credentials grant es adecuado para autenticación máquina a máquina. Por ejemplo, podría usar este grant 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 client credentials grant, deberá crear un cliente para este grant. Puede hacerlo usando la opción --client del comando Artisan passport:client:
php artisan passport:client --client
Luego, para usar este tipo de grant, 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íficos, puede proporcionar una lista separada por comas de los scopes requeridos al adjuntar el middleware client a la ruta:
Route::get('/orders', function (Request $request) {
...
})->middleware('client:check-status,your-scope');
#Recuperando tokens
Para recuperar un token usando este tipo de grant, haga 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 para sí mismos sin pasar por el flujo típico de redirección del código de autorización. Permitir que los usuarios emitan tokens para sí mismos a través de la interfaz de su aplicación puede ser útil para que experimenten con su API o puede ser un enfoque más sencillo para emitir tokens de acceso en general.
Si su aplicación usa principalmente Passport para emitir tokens de acceso personal, considere usar Laravel Sanctum, la biblioteca ligera de primera parte de Laravel para emitir tokens de acceso API.
#Creando 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 ha ejecutado el comando passport:install, no necesita ejecutar este comando:
php artisan passport:client --personal
Después de crear su cliente de acceso personal, coloque el ID del cliente y el valor 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"
#Gestionando tokens de acceso personal
Una vez que haya creado un cliente de acceso personal, puede emitir tokens para un usuario dado usando 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 un array opcional de scopes como segundo argumento:
use App\Models\User;
$user = User::find(1);
// Creando un token sin scopes...
$token = $user->createToken('Token Name')->accessToken;
// Creando un token con scopes...
$token = $user->createToken('My Token', ['place-orders'])->accessToken;
#JSON API
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 sus tokens de acceso personal. A continuación, revisaremos todos los endpoints de la API para gestionar tokens de acceso personal. Para mayor comodidad, usaremos Axios para demostrar cómo hacer solicitudes HTTP a los endpoints.
La API JSON está protegida por los middleware web y auth; por lo tanto, solo puede ser llamada desde su propia aplicación. No puede ser llamada desde una fuente externa.
#GET /oauth/scopes
Esta ruta devuelve todos los scopes definidos para su aplicación. Puede usar esta ruta para listar los 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 principalmente útil 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 datos: el name del token y los scopes 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 => {
// Listar errores en la respuesta...
});
#DELETE /oauth/personal-access-tokens/{token-id}
Esta ruta puede usarse para revocar tokens de acceso personal:
axios.delete('/oauth/personal-access-tokens/' + tokenId);
#Protegiendo rutas
#A través de middleware
Passport incluye un authentication guard que validará los tokens de acceso en las solicitudes entrantes. Una vez que haya configurado el guard api para usar el driver passport, solo necesita especificar el middleware auth:api en cualquier ruta que requiera un token de acceso válido:
Route::get('/user', function () {
// ...
})->middleware('auth:api');
Si está usando el client credentials grant, debería usar el middleware client para proteger sus rutas en lugar del middleware auth:api.
#Múltiples authentication guards
Si su aplicación autentica diferentes tipos de usuarios que quizás usan modelos Eloquent completamente distintos, probablemente necesite definir una configuración de guard 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 guard en el archivo de configuración config/auth.php:
'api' => [
'driver' => 'passport',
'provider' => 'users',
],
'api-customers' => [
'driver' => 'passport',
'provider' => 'customers',
],
La siguiente ruta usará el guard api-customers, que usa el proveedor de usuarios customers, para autenticar las solicitudes entrantes:
Route::get('/customer', function () {
// ...
})->middleware('auth:api-customers');
Para más información sobre el uso de múltiples proveedores de usuarios con Passport, consulte la documentación de password grant.
#Pasando el 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 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();
#Scopes de tokens
Los scopes permiten que los clientes de su API soliciten un conjunto específico de permisos al solicitar autorización para acceder a una cuenta. Por ejemplo, si está construyendo una aplicación de comercio electrónico, no todos los consumidores de la API necesitarán la capacidad de realizar pedidos. En cambio, puede permitir que los consumidores solo soliciten autorización para acceder a los estados de envío de pedidos. En otras palabras, los scopes permiten que los usuarios de su aplicación limiten las acciones que una aplicación de terceros puede realizar en su nombre.
#Definiendo scopes
Puede definir los scopes de su API usando 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 un array de nombres de scopes y descripciones de scopes. La descripción del scope puede ser cualquier cosa que desee y se mostrará a los usuarios en la pantalla de aprobación de autorización:
/**
* Registrar cualquier servicio de autenticación / autorización.
*/
public function boot(): void
{
Passport::tokensCan([
'place-orders' => 'Realizar pedidos',
'check-status' => 'Verificar estado de pedidos',
]);
}
#Scope por defecto
Si un cliente no solicita scopes específicos, puede configurar su servidor Passport para adjuntar scopes por defecto al token usando el método setDefaultScope. Normalmente, debería 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' => 'Realizar pedidos',
'check-status' => 'Verificar estado de pedidos',
]);
Passport::setDefaultScope([
'check-status',
'place-orders',
]);
Los scopes por defecto de Passport no se aplican a los tokens de acceso personal que genera el usuario.
#Asignando scopes a tokens
#Al solicitar códigos de autorización
Al solicitar un token de acceso usando el authorization code grant, los consumidores deben especificar los scopes deseados como el parámetro de cadena de consulta scope. El parámetro scope debe ser una lista de scopes separados 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 emitir tokens de acceso personal
Si está emitiendo tokens de acceso personal usando el método createToken del modelo App\Models\User, puede pasar el array de scopes deseados como segundo argumento al método:
$token = $user->createToken('My Token', ['place-orders'])->accessToken;
#Verificando scopes
Passport incluye dos middleware que pueden usarse para verificar que una solicitud entrante esté autenticada con un token que tenga un scope dado. Para comenzar, agregue los siguientes middleware a la propiedad $middlewareAliases de su archivo app/Http/Kernel.php:
'scopes' => \Laravel\Passport\Http\Middleware\CheckScopes::class,
'scope' => \Laravel\Passport\Http\Middleware\CheckForAnyScope::class,
#Verificar todos los scopes
El middleware scopes puede asignarse a una ruta para verificar que el token de acceso de la solicitud entrante tenga todos los scopes listados:
Route::get('/orders', function () {
// El token de acceso tiene los scopes "check-status" y "place-orders"...
})->middleware(['auth:api', 'scopes:check-status,place-orders']);
#Verificar cualquiera de los scopes
El middleware scope puede asignarse a una ruta para verificar que el token de acceso de la solicitud entrante tenga al menos uno de los scopes listados:
Route::get('/orders', function () {
// El token de acceso tiene el scope "check-status" o "place-orders"...
})->middleware(['auth:api', 'scope:check-status,place-orders']);
#Verificando scopes en una instancia de token
Una vez que una solicitud autenticada con token ha entrado a su aplicación, aún puede verificar si el token tiene un scope dado usando 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 adicionales para scopes
El método scopeIds devolverá un array con todos los IDs / nombres definidos:
use Laravel\Passport\Passport;
Passport::scopeIds();
El método scopes devolverá un array con todos los scopes definidos como instancias de Laravel\Passport\Scope:
Passport::scopes();
El método scopesFor devolverá un array de instancias Laravel\Passport\Scope que coinciden con los IDs / nombres dados:
Passport::scopesFor(['place-orders', 'check-status']);
Puede determinar si un scope dado ha sido definido usando el método hasScope:
Passport::hasScope('place-orders');
#Consumiendo su API con JavaScript
Al construir una API, puede ser extremadamente útil poder consumir su propia API desde su aplicación JavaScript. Este enfoque de 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 su aplicación web, aplicaciones móviles, aplicaciones de terceros y cualquier SDK que publique en varios gestores de paquetes.
Normalmente, si desea consumir su API desde su aplicación JavaScript, necesitaría 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 manejar esto por usted. Todo lo que necesita hacer es agregar el middleware CreateFreshApiToken a su grupo de middleware web en el archivo app/Http/Kernel.php:
'web' => [
// Otros middleware...
\Laravel\Passport\Http\Middleware\CreateFreshApiToken::class,
],
Debe asegurarse de que el middleware CreateFreshApiToken sea el último middleware listado en su pila de middleware.
Este middleware adjuntará una cookie laravel_token a sus respuestas salientes. Esta cookie contiene un JWT cifrado que Passport usará para autenticar las solicitudes API desde su aplicación JavaScript. El JWT tiene una duración igual al valor de configuración session.lifetime. Ahora, dado que el navegador enviará automáticamente la cookie con todas las solicitudes subsecuentes, puede hacer 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);
});
#Personalizando el nombre de la cookie
Si es necesario, puede personalizar el nombre de la cookie laravel_token usando el método Passport::cookie. Normalmente, este método debe llamarse desde el método boot de la clase App\Providers\AuthServiceProvider de su aplicación:
/**
* Registrar cualquier servicio de autenticación / autorización.
*/
public function boot(): void
{
Passport::cookie('custom_name');
}
#Protección CSRF
Al usar este método de autenticación, deberá asegurarse de que se incluya un encabezado de token CSRF válido en sus solicitudes. El scaffolding JavaScript predeterminado de Laravel incluye una instancia de Axios, que usará automáticamente el valor cifrado de la cookie XSRF-TOKEN para enviar un encabezado X-XSRF-TOKEN en solicitudes del mismo origen.
Si elige enviar el encabezado X-CSRF-TOKEN en lugar de X-XSRF-TOKEN, deberá usar el token sin cifrar proporcionado por csrf_token().
#Eventos
Passport dispara eventos al emitir tokens de acceso y tokens de actualización. Puede usar estos eventos para podar o revocar otros tokens de acceso en su base de datos. Si lo desea, puede adjuntar listeners a estos eventos en la clase App\Providers\EventServiceProvider de su aplicación:
/**
* Las asignaciones de listeners 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 puede usarse para especificar el usuario actualmente autenticado así como sus scopes. El primer argumento dado al método actingAs es la instancia del usuario y el segundo es un array de scopes que deben otorgarse 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 puede usarse 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 deben otorgarse 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);
}