Configurar Laravel Sanctum para la autenticación SPA y por API Tokens

Video thumbnail

Laravel Sanctum es un paquete para la autenticación de aplicaciones de página única (SPAs) y aplicaciones móviles que podemos usar para proteger la Rest Api en Laravel mediante autenticación requerida; Laravel Sanctum y APIs simples basadas en tokens; recuerda que no es tan directa como agregar una sesión, y esto se debe a que una Rest Api se recomienda que sea sin estado y es aquí donde entra Laravel Sanctum al juego. Permite que cada usuario de su aplicación genere múltiples tokens de API para su cuenta. Laravel Sanctum proporciona un sistema de autenticación liviano para SPAs y API simples

Laravel sanctum es una maravilla para cuando queremos proteger una Rest Api creada con Laravel ya sea mediante tokens o un hibrido entre sección y cookies; antes de mostrarte como puedes hacer la implementación más tradicional, vamos a entrar un poco en la materia e introducir que es Sanctum.

Usando Laravel Sanctum nos aseguramos de que solo los usuarios autenticados puedan acceder a las rutas que lo requieran.

La idea detrás de Sanctum es la de proteger las API de las aplicaciones Laravel mediante autenticación y tokens de acceso. Al hacerlo, se garantiza que solo se permitan solicitudes autenticadas y autorizadas, lo que a su vez asegura la integridad de la aplicación y los datos involucrados. Espero que esto te haya ayudado a entender un poco más sobre la teoría detrás de Laravel Sanctum.

Laravel Sanctum nos ofrece dos esquemas para trabajar.

¿Qué es Laravel Sanctum?

Laravel Sanctum es un paquete de autenticación para Laravel  que proporciona un método simple y seguro para autenticar aplicaciones de página única (SPAs) mediante los usuarios y proteger su API mediante tokens de API. Sanctum permite generar y administrar tokens de API, que se emiten mediante un recurso rest que implementamos y que es pasado al cliente para que este, cuando quiera acceder a recursos protegidos, deba de emplear dicho token de acceso y poder conectarse al recurso y consumir la respuesta de la API. 

Estos tokens son estupendos ya que los puedes usar para autenticar solicitudes de API y también para restringir el acceso a rutas, ya que, con el mismo, desde Laravel sabes de que usuario se trata que intenta consumir un recurso determinado y realizar alguna traza o limitante.

1 Autenticación vía SPA

Esto es útil cuando tenemos una página SPA y quieres agregar autenticación par proteger una Rest Api y acceder al usuario; esta opción se basa en un esquema entre autenticación por sesión y cookies qué tenemos que configurar; vale decir, que es la más sencilla de las dos:

A nivel del kernel, tenemos que habilitar el servicio de cookies y sesión para Sanctum:

//App/Http/Kernel
 
//***
class Kernel extends HttpKernel
{
    //***
 
        'api' => [      \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
            'throttle:api',
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],
    ];

Especificar el dominio que vas a emplear para la autenticación basado en las cookies en el archivo de config/sanctum.php:

  'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf(
        '%s%s',
        'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1,laraprimerospasos.test',
        env('APP_URL') ? ','.parse_url(env('APP_URL'), PHP_URL_HOST) : ''
    ))),

Si estás empleando Mac o Linux con Sail, no hace falta modificar este archivo ya que, el localhost se encuentra presente; también, puedes colocar la URL de tu dominio en producción.

En el archivo de config/cors.php tienes que habilitar el supports_credentials en true.

Para poder emplear axios (que lo haremos más adelante, debes de buscar el archivo resources/js/bootstrap.js y colocar:

axios.defaults.withCredentials = true;

Esto es para habilitar el uso de las cookies con las credenciales del usuario, y poder usarlo de manera transparente con axios.

Finalmente, ya con esto, podemos emplear el middleware para la autenticación de las rutas; de manera ejemplificada, protegeremos un conjunto de rutas:

Route::group(['middleware' => 'auth:sanctum'], function () {
    Route::resource('category', CategoryController::class)->except(["create", "edit"]);
    Route::resource('post', PostController::class)->except(["create", "edit"]);
});

Por lo demás lo único que tienes que hacer es iniciar sesión y ya estamos listos; puedes usar este esquema con axios.

2 En base a API Tokens

El siguiente esquema es más manual y a la final es una autenticación en base a tokens; para ello, ahora al momento de autenticarnos, generamos el tokens de autenticación:

Antes de comenzar, vamos a deshabilitar el esquema anterior de Sanctum para la autenticación vía SPA:

'api' => [           //\Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
***
],

Generar tokens de acceso

Laravel Sanctum nos provee un sencillo mecanismo con el cual agregar autenticación a Rest Api, mediante Token de autenticación o por tokens de autenticación, en esta primera entrada, veremos cómo usar el mecanismo del token de autenticación que sería el esquema más tradicional.

Cabe mencionar que, no es necesario instalar Sanctum ya que, desde hace mucho tiempo, Sanctum forma parte de un proyecto en Laravel.

En nuestro controlador rest, para manejar el usuario, en el caso de nuestro curso en Laravel, sería el siguiente:

app\Http\Controllers\Api\UserController.php

Crearemos una función de login, usando la función de attempt con la cual, podemos comprobar credenciales de usuario que son provistas en la petición:

    $credentials = [
        'email' => $request->email,
        'password' => $request->password
    ];

Una vez comprobada las credenciales, creamos el token y devolvemos la respuesta:

Auth::user()->createToken('myapptoken')->plainTextToken;

Finalmente, el código completo:

public function login(Request $request)
{
    $credentials = [
        'email' => $request->email,
        'password' => $request->password
    ];
    if (Auth::attempt($credentials)) {
        $token = Auth::user()->createToken('myapptoken')->plainTextToken;
     session()->put('token', $token);
        return response()->json($token);
    }
    return response()->json("Usuario y/o contraseña inválido", 422);
}

Lo más importante es notar que, aunque sanctum provee de todos los mecanismos para generar el token, ya depende del usuario implementar la lógica necesaria para generar el token.

Para crear los tokens para nuestros usuarios, vamos a crear una función de login que permite generar estos tokens (esta función también podrías emplear en la autenticación vía SPA pero sin generar el token):

$ php artisan make:controller Api/UserController

En el cual, definiremos el siguiente contenido:

<?php
 
namespace App\Http\Controllers\Api;
 
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
 
class UserController extends Controller
{
    public function login(Request $request)
    {
        $credentials = [
            'email' => $request->email,
            'password' => $request->password
        ];
 
        if(Auth::attempt($credentials)){
            $token = Auth::user()->createToken('myapptoken')->plainTextToken;
 
            return response()->json($token);
        }
        return response()->json("Usuario y/o contraseña inválido");
    }
}

Importante notar que Sanctum ya viene instalado en los proyectos modernos de Laravel.

Más ejemplos con Sanctum

Por aquí te dejo otros ejemplos con autenticación en Laravel sanctum y protección de rutas.

En el caso de que el proyecto ya no disponga de Sanctum, debes instalar Laravel Sanctum usando Composer:

composer require laravel/sanctum

Luego, publicarás los archivos de configuración y migraciones de Sanctum:

php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
php artisan migrate

A continuación, ya podemos proteger nuestras rutas, para ello, usamos un middleware de autenticación en tu archivo de rutas. Por ejemplo:

routes/api.php

Route::middleware('auth:sanctum')->group(function () {
   Route::get('/post', function () {
       return view('dashboard');
   });
});

En este ejemplo, la ruta "/post" solo será accesible si el usuario está autenticado mediante el middleware de Sanctum.

Después de eso, puedes generar un token de API para un usuario autenticado. Por ejemplo:

$token = $user->createToken('token-name')->plainTextToken;

Este código generará un token de API para el usuario dado con el nombre "token-name". Luego, puedes usar el token para autenticar solicitudes de API; aquí tienes otro ejemplo de login con sanctum:

routes/api.php

use App\\Models\\User;
use Illuminate\\Http\\Request;
use Illuminate\\Support\\Facades\\Hash;

Route::post('/login', function (Request $request) {
    $user = User::where('email', $request->email)->first();

    if (! $user || ! Hash::check($request->password, $user->password)) {
        throw ValidationException::withMessages([
            'email' => ['The provided credentials are incorrect.'],
        ]);
    }

    $token = $user->createToken($request->device_name)->plainTextToken;

    return ['token' => $token];
});

Eso es solo un ejemplo básico de cómo puedes utilizar Laravel Sanctum en tu proyecto Laravel. Espero que te haya sido útil. Si tienes más preguntas, no dudes en hacerlas.

Estrategias para Renovar el Token de Acceso (JWT) en una Rest API - Caso Laravel Sanctum

Video thumbnail

Laravel Sanctum es la herramienta estándar dentro del ecosistema de Laravel para gestionar la autenticación en aplicaciones modernas. Al construir plataformas híbridas —que integran clientes web como aplicaciones de página única (SPA) en Vue y clientes móviles nativos—, es indispensable diseñar una estrategia sólida para la administración, persistencia y ciclo de vida de los tokens de acceso personal (Personal Access Tokens).

A diferencia de los esquemas tradicionales de sesión basados exclusivamente en la web, la coexistencia de múltiples dispositivos introduce retos de seguridad y rendimiento que deben resolverse directamente en la arquitectura del backend.

1. Autenticación Híbrida: Cookies frente a Tokens

Sanctum ofrece un enfoque dual para proteger los recursos de la API, permitiendo combinar dos mecanismos de forma paralela:

  • Autenticación basada en Cookies (Stateful): Diseñada específicamente para SPA o aplicaciones web alojadas en el mismo dominio de primer nivel. Emplea cookies encriptadas junto con protección contra ataques CSRF. Este método es el que viene activo por defecto para el entorno de escritorio.
  • Autenticación basada en Tokens (Bearer Tokens): Es obligatoria para dispositivos móviles o servicios externos de terceros que no comparten el almacenamiento de cookies del navegador. El cliente recibe una cadena en texto plano tras validar sus credenciales y debe adjuntarla en la cabecera Authorization de cada petición HTTP.

El servidor procesa de manera automática ambas vías a través del middleware de Sanctum, traduciendo el origen en una única instancia del usuario autenticado.

2. El Ciclo de Vida del Token: Mitigación de Registros Muertos

Por defecto, la configuración de Sanctum permite que los tokens creados tengan una vigencia indefinida si la directiva expiration en el archivo config/sanctum.php se define como null.

config/sanctum.php

'expiration' => null, // El token no expira por defecto

Aunque este enfoque simplifica el desarrollo inicial, representa una debilidad en términos de infraestructura y seguridad. Si los usuarios inician sesión con frecuencia desde múltiples navegadores o dispositivos, la tabla personal_access_tokens acumulará miles de registros obsoletos ("registros muertos") que ralentizarán las consultas en la base de datos al no existir un proceso de depuración automática.

Para solventar este problema, existen tres estrategias principales de diseño:

Estrategia Cero: Depuración Programada (Cron Jobs)

Consiste en mantener los tokens sin fecha de expiración explícita dentro de la configuración de Sanctum, pero delegando la limpieza a un proceso en segundo plano de Laravel (Scheduled Tasks). El script analiza de forma periódica la columna last_used_at y elimina de forma definitiva aquellos registros que no hayan registrado actividad en un rango de varios meses.

Estrategia Uno: Renovación Activa (Silent Refresh)

Esta técnica establece un periodo de expiración estricto en la configuración (por ejemplo, 30 días). El cliente (ya sea en Vue o Flutter) cuenta con un endpoint dedicado en la API que, antes de alcanzar la fecha límite, invalida el token actual y genera uno nuevo, actualizando el almacenamiento local del dispositivo (LocalStorage o SharedPreferences) de forma transparente para el usuario.

Estrategia Dos: Extensión Dinámica del Token

Es la estrategia adoptada en arquitecturas de alta disponibilidad. En lugar de rotar la cadena de texto plano del token, el backend extiende dinámicamente la validez del registro modificando sus metadatos internos cada vez que el usuario consume la API. De este modo, una sesión que se utiliza a diario nunca expira, pero se invalida automáticamente si el dispositivo deja de realizar peticiones durante un tiempo prolongado.

3. Implementación Práctica: Validación Temprana y Extensión de Sesión

Independientemente de la estrategia seleccionada, el principio fundamental de la arquitectura de clientes dictamina que la aplicación debe verificar la integridad de la sesión en su primer arranque. No se debe esperar a que el usuario intente realizar una acción crítica (como una descarga o una compra) para descubrir que su token ha sido revocado, ya que esto degrada la experiencia de usuario con errores inesperados en cascada.

Tanto en entornos web como móviles, se debe consumir un endpoint ligero de verificación (por ejemplo, /api/v1/user-info) en los ganchos de inicialización globales del ciclo de vida (App.vue o el método main() de Flutter). Si el servidor retorna un código de estado 401 Unauthorized, el cliente intercepta la respuesta, limpia las variables de estado locales de inmediato y redirige al formulario de inicio de sesión.

A continuación, se detalla la implementación en el backend para un endpoint de verificación híbrido que extiende el ciclo de vida del token mediante la manipulación forzada del registro:

use Illuminate\Http\Request;
use Laravel\Sanctum\TransientToken;
use Laravel\Sanctum\PersonalAccessToken;
Route::middleware('auth:sanctum')->get('/v1/user-info', function (Request $request) {
   $user = $request->user();
   $currentToken = $user->currentAccessToken();
   // Comprobamos que el token provenga de la base de datos y no de una cookie (TransientToken)
   if ($currentToken instanceof PersonalAccessToken) {
       /** 
        * Forzamos la actualización de la fecha de creación.
        * Al desplazar 'created_at' al instante actual, extendemos de forma 
        * automática la ventana de expiración de 30 días configurada en Sanctum.
        */
       $currentToken->forceFill([
           'created_at' => now(),
       ])->save();
   }
   return response()->json([
       'success' => true,
       'data'    => $user,
   ]);
});

Justificación de la Verificación de Instancia

La validación instanceof PersonalAccessToken es obligatoria en infraestructuras híbridas. Cuando el usuario accede desde el cliente web mediante cookies, el método currentAccessToken() retorna un objeto de tipo TransientToken. Dado que este objeto no reside físicamente en la tabla de la base de datos, carece del método forceFill(). Intentar actualizarlo provocaría un error fatal en la ejecución del backend.

4. Centralización e Identificación Única de Dispositivos

Para implementar políticas comerciales restrictivas —como limitar el número de sesiones simultáneas permitidas en cuentas compartidas—, es imprescindible centralizar la lógica de emisión de tokens en un único servicio o clase controladora.

Al momento de generar un token a través del método createToken(), se debe evitar el uso de cadenas genéricas o estáticas. En su lugar, se analiza el encabezado User-Agent de la petición HTTP entrante para registrar el nombre del navegador o el entorno de ejecución nativo del cliente:

public function generateDeviceToken(Request $request, User $user): string
{
   $userAgent = $request->header('User-Agent', 'Generic Device');
   $deviceName = 'Unknown Device';
   // Clasificación modular del origen de la petición
   if (str_contains($userAgent, 'Dart')) {
       $deviceName = 'Mobile Application (Flutter)';
   } elseif (str_contains($userAgent, 'Firefox')) {
       $deviceName = 'Mozilla Firefox';
   } elseif (str_contains($userAgent, 'Chrome')) {
       $deviceName = 'Google Chrome';
   } elseif (str_contains($userAgent, 'Safari')) {
       $deviceName = 'Apple Safari';
   }
   // Limitación de longitud para cumplir con las restricciones del esquema de la tabla
   $sanitizedName = substr($deviceName, 0, 180);
   // Centralización de reglas de negocio antes de la persistencia
   return $user->createToken($sanitizedName)->plainTextToken;
}

Limitaciones del Control por Dispositivo

Aunque registrar el nombre del navegador aporta claridad al usuario en sus paneles de configuración y le permite revocar accesos específicos, no es un identificador unívoco infalible para aplicar bloqueos estrictos. Si un usuario opera de forma legítima desde dos ordenadores distintos utilizando el mismo navegador (por ejemplo, Google Chrome en su equipo de trabajo y en su equipo personal), ambas peticiones compartirán el mismo nombre de dispositivo.

Si la lógica de negocio exige bloquear o destruir sesiones duplicadas basándose únicamente en esa etiqueta, se cerrará la sesión válida del segundo dispositivo de manera errónea. Para escenarios que requieran una firma de identidad única por hardware, el cliente móvil debe extraer el identificador único del dispositivo (UUID) a nivel de sistema operativo y transmitirlo explícitamente en los parámetros de la petición de autenticación hacia la API.

Ahora, conozcamos las entrañas del motor de plantillas de Laravel, Blade.

Código fuente:

https://github.com/libredesarrollo/book-course-laravel-base-api-11/releases/tag/v0.1

Vamos a aprender sobre Laravel Sanctum y configurar Laravel Sanctum vía SPA o un API Tokens para proteger una rest api con autenticación requerida.


Únete a la comunidad de desarrolladores que han decidido dejar de picar código y empezar a construir productos reales. Recibe mis mejores trucos de arquitectura cada semana:

Acepto recibir anuncios de interes sobre este Blog.

Andrés Cruz

EN In english