- Introducción
- Instalación
- Configuración
- Autenticación con Tokens API
- Autenticación SPA
- Autenticación en Aplicaciones Móviles
- Pruebas
#Introducción
Laravel Sanctum ofrece un sistema de autenticación ligero para SPAs (aplicaciones de una sola página), aplicaciones móviles y APIs simples basadas en tokens. Sanctum permite que cada usuario de su aplicación genere múltiples tokens API para su cuenta. Estos tokens pueden tener asignadas habilidades / scopes que especifican qué acciones pueden realizar.
#Cómo Funciona
Laravel Sanctum existe para resolver dos problemas distintos. Discutamos cada uno antes de profundizar en la librería.
#Tokens API
Primero, Sanctum es un paquete simple que puede usar para emitir tokens API a sus usuarios sin la complejidad de OAuth. Esta característica está inspirada en GitHub y otras aplicaciones que emiten "personal access tokens". Por ejemplo, imagine que la sección de "configuración de cuenta" de su aplicación tiene una pantalla donde un usuario puede generar un token API para su cuenta. Puede usar Sanctum para generar y administrar esos tokens. Estos tokens típicamente tienen un tiempo de expiración muy largo (años), pero pueden ser revocados manualmente por el usuario en cualquier momento.
Laravel Sanctum ofrece esta funcionalidad almacenando los tokens API de los usuarios en una única tabla de base de datos y autenticando las solicitudes HTTP entrantes mediante el encabezado Authorization, que debe contener un token API válido.
#Autenticación SPA
En segundo lugar, Sanctum existe para ofrecer una forma sencilla de autenticar aplicaciones de una sola página (SPAs) que necesitan comunicarse con una API impulsada por Laravel. Estas SPAs pueden estar en el mismo repositorio que su aplicación Laravel o ser un repositorio completamente separado, como una SPA creada con Vue CLI o una aplicación Next.js.
Para esta característica, Sanctum no utiliza tokens de ningún tipo. En cambio, Sanctum usa los servicios de autenticación basados en cookies y sesiones incorporados en Laravel. Normalmente, Sanctum utiliza el guardia de autenticación web de Laravel para lograr esto. Esto proporciona los beneficios de protección CSRF, autenticación por sesión, además de proteger contra la filtración de credenciales de autenticación vía XSS.
Sanctum solo intentará autenticar usando cookies cuando la solicitud entrante provenga de su propio frontend SPA. Cuando Sanctum examina una solicitud HTTP entrante, primero buscará una cookie de autenticación y, si no está presente, entonces examinará el encabezado Authorization en busca de un token API válido.
Está perfectamente bien usar Sanctum solo para autenticación con tokens API o solo para autenticación SPA. El hecho de usar Sanctum no significa que deba usar ambas funcionalidades que ofrece.
#Instalación
Las versiones más recientes de Laravel ya incluyen Laravel Sanctum. Sin embargo, si el archivo composer.json de su aplicación no incluye laravel/sanctum, puede seguir las instrucciones de instalación a continuación.
Puede instalar Laravel Sanctum mediante el gestor de paquetes Composer:
composer require laravel/sanctum
Luego, debe publicar los archivos de configuración y migración de Sanctum usando el comando Artisan vendor:publish. El archivo de configuración sanctum se colocará en el directorio config de su aplicación:
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
Finalmente, debe ejecutar las migraciones de base de datos. Sanctum creará una tabla en la base de datos para almacenar los tokens API:
php artisan migrate
Luego, si planea usar Sanctum para autenticar una SPA, debe agregar el middleware de Sanctum al grupo de middleware api dentro del archivo app/Http/Kernel.php de su aplicación:
'api' => [
\Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
\Illuminate\Routing\Middleware\ThrottleRequests::class.':api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
#Personalización de Migraciones
Si no va a usar las migraciones predeterminadas de Sanctum, debe llamar al método Sanctum::ignoreMigrations en el método register de su clase App\Providers\AppServiceProvider. Puede exportar las migraciones predeterminadas ejecutando el siguiente comando: php artisan vendor:publish --tag=sanctum-migrations
#Configuración
#Sobrescribir Modelos Predeterminados
Aunque no es comúnmente necesario, puede extender libremente el modelo PersonalAccessToken que Sanctum usa internamente:
use Laravel\Sanctum\PersonalAccessToken as SanctumPersonalAccessToken;
class PersonalAccessToken extends SanctumPersonalAccessToken
{
// ...
}
Luego, puede indicarle a Sanctum que use su modelo personalizado mediante el método usePersonalAccessTokenModel que Sanctum proporciona. Normalmente, debe llamar a este método en el método boot de uno de los proveedores de servicios de su aplicación:
use App\Models\Sanctum\PersonalAccessToken;
use Laravel\Sanctum\Sanctum;
/**
* Inicializar cualquier servicio de la aplicación.
*/
public function boot(): void
{
Sanctum::usePersonalAccessTokenModel(PersonalAccessToken::class);
}
#Autenticación con Tokens API
No debe usar tokens API para autenticar su propia SPA de primera parte. En su lugar, use las funciones de autenticación SPA integradas en Sanctum.
#Emisión de Tokens API
Sanctum le permite emitir tokens API / tokens de acceso personal que pueden usarse para autenticar solicitudes API a su aplicación. Al hacer solicitudes usando tokens API, el token debe incluirse en el encabezado Authorization como un token Bearer.
Para comenzar a emitir tokens para usuarios, su modelo User debe usar el trait Laravel\Sanctum\HasApiTokens:
use Laravel\Sanctum\HasApiTokens;
class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable;
}
Para emitir un token, puede usar el método createToken. El método createToken devuelve una instancia de Laravel\Sanctum\NewAccessToken. Los tokens de API se almacenan en forma de hash usando SHA-256 antes de guardarse en su base de datos, pero puede acceder al valor en texto plano del token usando la propiedad plainTextToken de la instancia NewAccessToken. Debe mostrar este valor al usuario inmediatamente después de crear el token:
use Illuminate\Http\Request;
Route::post('/tokens/create', function (Request $request) {
$token = $request->user()->createToken($request->token_name);
return ['token' => $token->plainTextToken];
});
Puede acceder a todos los tokens del usuario usando la relación Eloquent tokens proporcionada por el trait HasApiTokens:
foreach ($user->tokens as $token) {
// ...
}
#Habilidades de los Tokens
Sanctum le permite asignar "habilidades" a los tokens. Las habilidades cumplen una función similar a los "scopes" de OAuth. Puede pasar un arreglo de habilidades como segundo argumento al método createToken:
return $user->createToken('token-name', ['server:update'])->plainTextToken;
Al manejar una solicitud entrante autenticada por Sanctum, puede determinar si el token tiene una habilidad dada usando el método tokenCan:
if ($user->tokenCan('server:update')) {
// ...
}
#Middleware para Habilidades de Tokens
Sanctum también incluye dos middleware que pueden usarse para verificar que una solicitud entrante esté autenticada con un token que tenga una habilidad específica. Para comenzar, agregue los siguientes middleware a la propiedad $middlewareAliases en el archivo app/Http/Kernel.php de su aplicación:
'abilities' => \Laravel\Sanctum\Http\Middleware\CheckAbilities::class,
'ability' => \Laravel\Sanctum\Http\Middleware\CheckForAnyAbility::class,
El middleware abilities puede asignarse a una ruta para verificar que el token de la solicitud entrante tenga todas las habilidades listadas:
Route::get('/orders', function () {
// El token tiene las habilidades "check-status" y "place-orders"...
})->middleware(['auth:sanctum', 'abilities:check-status,place-orders']);
El middleware ability puede asignarse a una ruta para verificar que el token de la solicitud entrante tenga al menos una de las habilidades listadas:
Route::get('/orders', function () {
// El token tiene la habilidad "check-status" o "place-orders"...
})->middleware(['auth:sanctum', 'ability:check-status,place-orders']);
#Solicitudes Iniciadas desde la UI de Primera Parte
Para mayor comodidad, el método tokenCan siempre devolverá true si la solicitud autenticada entrante proviene de su SPA de primera parte y está usando la autenticación SPA integrada en Sanctum.
Sin embargo, esto no significa necesariamente que su aplicación deba permitir que el usuario realice la acción. Normalmente, las políticas de autorización de su aplicación determinarán si el token tiene permiso para realizar las habilidades y también verificarán que la instancia del usuario tenga permitido realizar la acción.
Por ejemplo, si imaginamos una aplicación que administra servidores, esto podría significar verificar que el token esté autorizado para actualizar servidores y que el servidor pertenezca al usuario:
return $request->user()->id === $server->user_id &&
$request->user()->tokenCan('server:update')
Al principio, permitir que el método tokenCan se llame y siempre devuelva true para solicitudes iniciadas desde la UI de primera parte puede parecer extraño; sin embargo, es conveniente poder asumir siempre que un token API está disponible y puede inspeccionarse mediante el método tokenCan. Con este enfoque, siempre puede llamar a tokenCan dentro de las políticas de autorización de su aplicación sin preocuparse si la solicitud fue iniciada desde la UI de su aplicación o por un consumidor tercero de su API.
#Protección de Rutas
Para proteger rutas y que todas las solicitudes entrantes deban estar autenticadas, debe adjuntar el guardia de autenticación sanctum a sus rutas protegidas dentro de los archivos routes/web.php y routes/api.php. Este guardia asegurará que las solicitudes entrantes estén autenticadas ya sea como solicitudes con sesión basada en cookies o que contengan un encabezado de token API válido si la solicitud proviene de un tercero.
Puede preguntarse por qué sugerimos autenticar las rutas dentro del archivo routes/web.php de su aplicación usando el guardia sanctum. Recuerde que Sanctum primero intentará autenticar las solicitudes entrantes usando la cookie de autenticación de sesión típica de Laravel. Si esa cookie no está presente, Sanctum intentará autenticar la solicitud usando un token en el encabezado Authorization. Además, autenticar todas las solicitudes con Sanctum garantiza que siempre pueda llamar al método tokenCan en la instancia del usuario autenticado actualmente:
use Illuminate\Http\Request;
Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
return $request->user();
});
#Revocación de Tokens
Puede "revocar" tokens eliminándolos de su base de datos usando la relación tokens que proporciona el trait Laravel\Sanctum\HasApiTokens:
// Revocar todos los tokens...
$user->tokens()->delete();
// Revocar el token que se usó para autenticar la solicitud actual...
$request->user()->currentAccessToken()->delete();
// Revocar un token específico...
$user->tokens()->where('id', $tokenId)->delete();
#Expiración de Tokens
Por defecto, los tokens de Sanctum nunca expiran y solo pueden invalidarse revocando el token. Sin embargo, si desea configurar un tiempo de expiración para los tokens API de su aplicación, puede hacerlo mediante la opción de configuración expiration definida en el archivo de configuración sanctum de su aplicación. Esta opción define el número de minutos hasta que un token emitido se considere expirado:
'expiration' => 525600,
Si desea especificar el tiempo de expiración de cada token de forma independiente, puede hacerlo proporcionando el tiempo de expiración como tercer argumento al método createToken:
return $user->createToken(
'token-name', ['*'], now()->addWeek()
)->plainTextToken;
Si ha configurado un tiempo de expiración para los tokens de su aplicación, también puede desear programar una tarea para podar los tokens expirados de su aplicación. Afortunadamente, Sanctum incluye un comando Artisan sanctum:prune-expired que puede usar para lograr esto. Por ejemplo, puede configurar una tarea programada para eliminar todos los registros de tokens expirados que hayan expirado al menos 24 horas:
$schedule->command('sanctum:prune-expired --hours=24')->daily();
#Autenticación SPA
Sanctum también existe para proporcionar un método sencillo de autenticar aplicaciones de una sola página (SPAs) que necesitan comunicarse con una API impulsada por Laravel. Estas SPAs pueden estar en el mismo repositorio que su aplicación Laravel o ser un repositorio completamente separado.
Para esta característica, Sanctum no usa tokens de ningún tipo. En cambio, Sanctum usa los servicios de autenticación basados en cookies y sesiones incorporados en Laravel. Este enfoque de autenticación proporciona los beneficios de protección CSRF, autenticación por sesión, además de proteger contra la filtración de credenciales de autenticación vía XSS.
Para autenticar, su SPA y API deben compartir el mismo dominio de nivel superior. Sin embargo, pueden estar en subdominios diferentes. Además, debe asegurarse de enviar el encabezado Accept: application/json y el encabezado Referer o Origin con su solicitud.
#Configuración
#Configuración de sus Dominios de Primera Parte
Primero, debe configurar desde qué dominios su SPA realizará solicitudes. Puede configurar estos dominios usando la opción stateful en el archivo de configuración sanctum. Esta configuración determina qué dominios mantendrán la autenticación "stateful" usando cookies de sesión de Laravel al hacer solicitudes a su API.
Si accede a su aplicación mediante una URL que incluye un puerto (127.0.0.1:8000), debe asegurarse de incluir el número de puerto con el dominio.
#Middleware de Sanctum
Luego, debe agregar el middleware de Sanctum al grupo de middleware api dentro del archivo app/Http/Kernel.php. Este middleware es responsable de asegurar que las solicitudes entrantes desde su SPA puedan autenticarse usando cookies de sesión de Laravel, mientras permite que solicitudes de terceros o aplicaciones móviles se autentiquen usando tokens API:
'api' => [
\Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
\Illuminate\Routing\Middleware\ThrottleRequests::class.':api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
#CORS y Cookies
Si tiene problemas para autenticarse con su aplicación desde una SPA que se ejecuta en un subdominio separado, probablemente haya configurado incorrectamente sus ajustes de CORS (Cross-Origin Resource Sharing) o de cookies de sesión.
Debe asegurarse de que la configuración CORS de su aplicación devuelva el encabezado Access-Control-Allow-Credentials con el valor True. Esto puede lograrse configurando la opción supports_credentials en el archivo config/cors.php de su aplicación a true.
Además, debe habilitar las opciones withCredentials y withXSRFToken en la instancia global axios de su aplicación. Normalmente, esto se realiza en el archivo resources/js/bootstrap.js. Si no usa Axios para hacer solicitudes HTTP desde su frontend, debe realizar la configuración equivalente en su propio cliente HTTP:
axios.defaults.withCredentials = true;
axios.defaults.withXSRFToken = true;
Finalmente, debe asegurarse de que la configuración del dominio de la cookie de sesión de su aplicación soporte cualquier subdominio de su dominio raíz. Puede lograr esto prefijando el dominio con un . en el archivo de configuración config/session.php de su aplicación:
'domain' => '.domain.com',
#Autenticación
#Protección CSRF
Para autenticar su SPA, la página de "login" de su SPA debe primero hacer una solicitud al endpoint /sanctum/csrf-cookie para inicializar la protección CSRF para la aplicación:
axios.get('/sanctum/csrf-cookie').then(response => {
// Iniciar sesión...
});
Durante esta solicitud, Laravel establecerá una cookie XSRF-TOKEN que contiene el token CSRF actual. Este token debe enviarse luego en un encabezado X-XSRF-TOKEN en solicitudes posteriores, lo que algunas librerías HTTP como Axios y Angular HttpClient hacen automáticamente. Si su librería HTTP JavaScript no lo hace, deberá establecer manualmente el encabezado X-XSRF-TOKEN con el valor de la cookie XSRF-TOKEN que establece esta ruta.
#Inicio de Sesión
Una vez inicializada la protección CSRF, debe hacer una solicitud POST a la ruta /login de su aplicación Laravel. Esta ruta /login puede ser implementada manualmente o usando un paquete de autenticación sin interfaz como Laravel Fortify.
Si la solicitud de inicio de sesión es exitosa, estará autenticado y las solicitudes posteriores a las rutas de su aplicación se autenticarán automáticamente mediante la cookie de sesión que Laravel emitió a su cliente. Además, dado que su aplicación ya hizo una solicitud a la ruta /sanctum/csrf-cookie, las solicitudes posteriores deberían recibir protección CSRF automáticamente siempre que su cliente HTTP JavaScript envíe el valor de la cookie XSRF-TOKEN en el encabezado X-XSRF-TOKEN.
Por supuesto, si la sesión de su usuario expira por inactividad, las solicitudes posteriores a la aplicación Laravel pueden recibir respuestas HTTP 401 o 419. En ese caso, debe redirigir al usuario a la página de inicio de sesión de su SPA.
Usted puede escribir su propio /login endpoint; sin embargo, debe asegurarse de que autentique al usuario utilizando los servicios de autenticación basados en sesión estándar que proporciona Laravel: servicios de autenticación basados en sesión que Laravel proporciona. Normalmente, esto significa usar el guard de autenticación web.
#Protección de Rutas
Para proteger rutas y que todas las solicitudes entrantes deban estar autenticadas, debe adjuntar el guardia de autenticación sanctum a sus rutas API dentro del archivo routes/api.php. Este guardia asegurará que las solicitudes entrantes estén autenticadas ya sea como solicitudes con sesión stateful desde su SPA o que contengan un encabezado de token API válido si la solicitud proviene de un tercero:
use Illuminate\Http\Request;
Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
return $request->user();
});
#Autorización de Canales Privados de Broadcast
Si su SPA necesita autenticarse con canales privados / de presencia de broadcast, debe colocar la llamada al método Broadcast::routes dentro de su archivo routes/api.php:
Broadcast::routes(['middleware' => ['auth:sanctum']]);
Luego, para que las solicitudes de autorización de Pusher tengan éxito, deberá proporcionar un authorizer personalizado de Pusher al inicializar Laravel Echo. Esto permite que su aplicación configure Pusher para usar la instancia axios que está configurada correctamente para solicitudes cross-domain:
window.Echo = new Echo({
broadcaster: "pusher",
cluster: import.meta.env.VITE_PUSHER_APP_CLUSTER,
encrypted: true,
key: import.meta.env.VITE_PUSHER_APP_KEY,
authorizer: (channel, options) => {
return {
authorize: (socketId, callback) => {
axios.post('/api/broadcasting/auth', {
socket_id: socketId,
channel_name: channel.name
})
.then(response => {
callback(false, response.data);
})
.catch(error => {
callback(true, error);
});
}
};
},
})
#Autenticación en Aplicaciones Móviles
También puede usar tokens Sanctum para autenticar las solicitudes de su aplicación móvil a su API. El proceso para autenticar solicitudes de aplicaciones móviles es similar al de autenticar solicitudes API de terceros; sin embargo, hay pequeñas diferencias en cómo emitirá los tokens API.
#Emisión de Tokens API
Para comenzar, cree una ruta que acepte el correo electrónico / nombre de usuario del usuario, la contraseña y el nombre del dispositivo, y luego intercambie esas credenciales por un nuevo token Sanctum. El "nombre del dispositivo" que se da a este endpoint es solo para fines informativos y puede ser cualquier valor que desee. En general, el valor del nombre del dispositivo debe ser un nombre que el usuario reconozca, como "iPhone 12 de Nuno".
Normalmente, hará una solicitud al endpoint de tokens desde la pantalla de "login" de su aplicación móvil. El endpoint devolverá el token API en texto plano que luego puede almacenarse en el dispositivo móvil y usarse para hacer solicitudes API adicionales:
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\ValidationException;
Route::post('/sanctum/token', function (Request $request) {
$request->validate([
'email' => 'required|email',
'password' => 'required',
'device_name' => 'required',
]);
$user = User::where('email', $request->email)->first();
if (! $user || ! Hash::check($request->password, $user->password)) {
throw ValidationException::withMessages([
'email' => ['Las credenciales proporcionadas son incorrectas.'],
]);
}
return $user->createToken($request->device_name)->plainTextToken;
});
Cuando la aplicación móvil usa el token para hacer una solicitud API a su aplicación, debe pasar el token en el encabezado Authorization como un token Bearer.
Al emitir tokens para una aplicación móvil, también puede especificar habilidades de token.
#Protección de Rutas
Como se documentó anteriormente, puede proteger rutas para que todas las solicitudes entrantes deban estar autenticadas adjuntando el guardia de autenticación sanctum a las rutas:
Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
return $request->user();
});
#Revocación de Tokens
Para permitir que los usuarios revoquen tokens API emitidos a dispositivos móviles, puede listarlos por nombre, junto con un botón "Revocar", dentro de una sección de "configuración de cuenta" en la interfaz de usuario de su aplicación web. Cuando el usuario haga clic en el botón "Revocar", puede eliminar el token de la base de datos. Recuerde, puede acceder a los tokens API de un usuario mediante la relación tokens proporcionada por el trait Laravel\Sanctum\HasApiTokens:
// Revocar todos los tokens...
$user->tokens()->delete();
// Revocar un token específico...
$user->tokens()->where('id', $tokenId)->delete();
#Pruebas
Mientras realiza pruebas, el método Sanctum::actingAs puede usarse para autenticar un usuario y especificar qué habilidades deben otorgarse a su token:
use App\Models\User;
use Laravel\Sanctum\Sanctum;
public function test_task_list_can_be_retrieved(): void
{
Sanctum::actingAs(
User::factory()->create(),
['view-tasks']
);
$response = $this->get('/api/task');
$response->assertOk();
}
Si desea otorgar todas las habilidades al token, debe incluir * en la lista de habilidades que se pasa al método actingAs:
Sanctum::actingAs(
User::factory()->create(),
['*']
);