Laravel Sanctum: Guía Completa

 


¿Qué es Sanctum?

Laravel Sanctum es un sistema de autenticación ligero para aplicaciones SPA (Single Page Applications)APIs móviles y APIs simples basadas en tokens. Anteriormente conocido como "Laravel Airlock", Sanctum resuelve dos problemas principales:

  1. Autenticación para SPAs (aplicaciones Vue/React/ Angular que consumen tu API)

  2. Autenticación basada en tokens para APIs móviles o de terceros

Características principales

1. Autenticación para SPAs

php
// Cuando tu frontend y backend están en el mismo dominio
// Sanctum usa cookies de sesión Laravel tradicionales

2. Autenticación por Tokens API

php
// Para aplicaciones móviles o clientes de terceros
// Genera tokens de acceso personales

3. Sistema ligero y simple

  • Sin OAuth complicado

  • Sin Passport (más pesado)

  • Perfecto para proyectos que no necesitan OAuth2

¿Cómo funciona?

Arquitectura de Sanctum

text
┌─────────────────────────────────────────┐
│           Aplicación Web SPA            │
│         (Vue.js, React, Angular)        │
└─────────────────┬───────────────────────┘
                  │
                  │ Mismo dominio/subdominio
                  │
┌─────────────────▼───────────────────────┐
│            API Laravel                  │
│      (Autenticación con cookies)        │
└─────────────────┬───────────────────────┘
                  │
                  │ Token API
                  │
┌─────────────────▼───────────────────────┐
│       Aplicación Móvil / Terceros       │
│      (Autenticación con tokens)         │
└─────────────────────────────────────────┘

Instalación y Configuración

Paso 1: Instalar Sanctum

bash
composer require laravel/sanctum

Paso 2: Publicar archivos de configuración

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

Paso 3: Ejecutar migraciones

bash
php artisan migrate

Esto crea la tabla personal_access_tokens:

php
Schema::create('personal_access_tokens', function (Blueprint $table) {
    $table->id();
    $table->morphs('tokenable'); // Relación polimórfica
    $table->string('name');       // Nombre del token
    $table->string('token', 64)->unique();
    $table->text('abilities')->nullable();
    $table->timestamp('last_used_at')->nullable();
    $table->timestamps();
});

Paso 4: Configurar modelo User

php
// app/Models/User.php
use Laravel\Sanctum\HasApiTokens;

class User extends Authenticatable
{
    use HasApiTokens, HasFactory, Notifiable;
    // ...
}

Casos de Uso

1. Autenticación para SPA (mismo dominio)

Configuración en config/sanctum.php:

php
'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf(
    '%s%s',
    'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1',
    Sanctum::currentApplicationUrlWithPort()
))),

Frontend (Vue.js con Axios):

javascript
// Configurar axios para enviar credenciales
axios.defaults.withCredentials = true;

// Login
axios.get('/sanctum/csrf-cookie').then(() => {
    axios.post('/login', {
        email: 'user@example.com',
        password: 'password'
    });
});

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

Backend (rutas API):

php
// routes/api.php
Route::middleware('auth:sanctum')->group(function () {
    Route::get('/user', function (Request $request) {
        return $request->user();
    });
    
    Route::get('/posts', 'PostController@index');
});

2. Autenticación por Tokens (APIs)

Crear token:

php
// En un controlador o tinker
$user = User::find(1);

// Token simple
$token = $user->createToken('token-name');

// Token con habilidades (scopes)
$token = $user->createToken('server-access', ['server:update', 'server:delete']);

// Obtener el string del token
$plainTextToken = $token->plainTextToken;
// Ejemplo: "1|abcdef123456..."

Usar token en solicitudes:

bash
# Headers de la solicitud
Authorization: Bearer 1|abcdef123456...

Proteger rutas:

php
// routes/api.php
Route::middleware('auth:sanctum')->group(function () {
    Route::get('/user', function (Request $request) {
        return $request->user();
    });
});

// O con habilidades específicas
Route::middleware(['auth:sanctum', 'ability:server:update'])
    ->put('/server/{id}', 'ServerController@update');

Ejemplo Práctico Completo

API de Tareas (Todo List) con Sanctum

php
// routes/api.php
use App\Http\Controllers\TaskController;

Route::post('/login', [AuthController::class, 'login']);

Route::middleware('auth:sanctum')->group(function () {
    Route::apiResource('tasks', TaskController::class);
    Route::post('/logout', [AuthController::class, 'logout']);
    
    // Obtener tokens del usuario
    Route::get('/tokens', function (Request $request) {
        return $request->user()->tokens;
    });
    
    // Crear nuevo token
    Route::post('/tokens', function (Request $request) {
        $token = $request->user()->createToken(
            $request->token_name,
            $request->abilities ?: ['*']
        );
        
        return ['token' => $token->plainTextToken];
    });
    
    // Eliminar token
    Route::delete('/tokens/{id}', function (Request $request, $id) {
        $request->user()->tokens()->where('id', $id)->delete();
        return response()->noContent();
    });
});
php
// app/Http/Controllers/AuthController.php
public function login(Request $request)
{
    $credentials = $request->validate([
        'email' => 'required|email',
        'password' => 'required',
    ]);
    
    if (Auth::attempt($credentials)) {
        $user = Auth::user();
        
        // Para SPA (cookie-based)
        $request->session()->regenerate();
        
        // Para API (token-based) - opcional
        if ($request->wantsJson()) {
            $token = $user->createToken('api-token')->plainTextToken;
            return response()->json([
                'user' => $user,
                'token' => $token
            ]);
        }
        
        return redirect()->intended('/dashboard');
    }
    
    return back()->withErrors(['email' => 'Credenciales incorrectas']);
}

Gestión de Tokens

Listar tokens de un usuario:

php
$tokens = $user->tokens;
foreach ($tokens as $token) {
    echo $token->name;
    echo $token->last_used_at;
    echo $token->abilities; // ['*'] o ['server:update']
}

Revocar tokens:

php
// Revocar todos los tokens
$user->tokens()->delete();

// Revocar un token específico
$user->tokens()->where('id', $tokenId)->delete();

// Revocar el token actual
$request->user()->currentAccessToken()->delete();

Verificar habilidades (scopes):

php
$token = $user->tokens()->first();

// Verificar si tiene una habilidad
if ($token->can('server:update')) {
    // Puede actualizar servidores
}

// Verificar varias habilidades
if ($token->can(['server:update', 'server:delete'])) {
    // Tiene ambos permisos
}

// En middleware
Route::middleware(['auth:sanctum', 'ability:server:update,server:delete'])
    ->put('/server/{id}', 'ServerController@update');

Configuración Avanzada

Personalizar expiración de tokens

php
// En config/sanctum.php
'expiration' => 525600, // minutos (1 año)

// O por token individual
$token = $user->createToken(
    name: 'token-name',
    abilities: ['*'],
    expiresAt: now()->addDays(7) // Expira en 7 días
);

Usar diferentes modelos tokenizables

php
// No solo User, también Company, Device, etc.
use Laravel\Sanctum\HasApiTokens;

class Device extends Model
{
    use HasApiTokens;
    
    public function tokens()
    {
        return $this->morphMany(PersonalAccessToken::class, 'tokenable');
    }
}

Manejo de dominios para SPA

env
# .env
SANCTUM_STATEFUL_DOMAINS=localhost,app.midominio.com
SESSION_DOMAIN=.midominio.com

Comparación: Sanctum vs Passport vs Jetstream

CaracterísticaSanctumPassportJetstream
PropósitoAPIs simples y SPAsOAuth2 completoScaffolding UI
ComplejidadSimpleComplejoIntermedio
TokensTokens simplesTokens OAuth2Tokens Sanctum
SPA Auth✅ (Cookies)✅ (con Sanctum)
OAuth2
Ideal paraAPIs propias, SPAsAPIs públicas, tercerosApps web completas

Seguridad y Mejores Prácticas

1. Configurar CORS correctamente

php
// config/cors.php
'paths' => ['api/*', 'sanctum/csrf-cookie'],
'allowed_origins' => ['http://localhost:3000'],
'allowed_methods' => ['*'],
'allowed_headers' => ['*'],
'allow_credentials' => true,

2. Limitar tasa de peticiones

php
// app/Http/Kernel.php
'api' => [
    'throttle:api',
    \Illuminate\Routing\Middleware\SubstituteBindings::class,
],

3. Usar HTTPS siempre

php
// .env
SESSION_SECURE_COOKIE=true

4. Rotación de tokens

php
// Programar tarea para eliminar tokens viejos
$schedule->call(function () {
    PersonalAccessToken::where('last_used_at', '<', now()->subDays(30))->delete();
})->daily();

Problemas Comunes y Soluciones

Problema: Error de CORS con SPA

php
// Solución: Verificar config/cors.php
'paths' => ['api/*', 'sanctum/csrf-cookie'],
'supports_credentials' => true,

Problema: Token no funciona después de logout

php
// Solución: Usar diferentes guardias
'guards' => [
    'web' => [
        'driver' => 'session',
        'provider' => 'users',
    ],
    'api' => [
        'driver' => 'sanctum',
        'provider' => 'users',
    ],
],

Problema: Múltiples tokens por usuario

php
// Solución: Limitar tokens por usuario
class User extends Authenticatable
{
    use HasApiTokens;
    
    public function createToken(string $name, array $abilities = ['*'])
    {
        // Limitar a 5 tokens por usuario
        if ($this->tokens()->count() >= 5) {
            $this->tokens()->oldest()->first()->delete();
        }
        
        return parent::createToken($name, $abilities);
    }
}

Conclusión

Sanctum es ideal cuando:
✅ Necesitas autenticación para tu SPA (Vue/React/Angular)
✅ Tu frontend y backend están en el mismo dominio
✅ Necesitas una API simple con tokens
✅ No quieres la complejidad de OAuth2
✅ Usas Jetstream (viene integrado)

No uses Sanctum cuando:
❌ Necesitas OAuth2 completo (usa Passport)
❌ Creas una API pública para terceros
❌ Necesitas scopes complejos de OAuth

Ejemplo de uso común:

bash
# App con Vue.js frontend + Laravel backend
# App móvil que consume tu API
# Microservicios internos
# Dashboard administrativo SPA

Sanctum es la solución más simple y elegante de Laravel para autenticación de APIs y SPAs, manteniendo la seguridad sin la complejidad de OAuth2

Comentarios

Entradas más populares de este blog

crear controladores separados para API y Web

Laravel tanto para web como para API al mismo tiempo.

Creación de una API RESTful con Laravel (sin autenticación)