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:
Autenticación para SPAs (aplicaciones Vue/React/ Angular que consumen tu API)
Autenticación basada en tokens para APIs móviles o de terceros
Características principales
1. Autenticación para SPAs
// Cuando tu frontend y backend están en el mismo dominio
// Sanctum usa cookies de sesión Laravel tradicionales2. Autenticación por Tokens API
// Para aplicaciones móviles o clientes de terceros
// Genera tokens de acceso personales3. 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
┌─────────────────────────────────────────┐
│ 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
composer require laravel/sanctumPaso 2: Publicar archivos de configuración
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"Paso 3: Ejecutar migraciones
php artisan migrateEsto crea la tabla personal_access_tokens:
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
// 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:
'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):
// 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):
// 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:
// 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:
# Headers de la solicitud
Authorization: Bearer 1|abcdef123456...Proteger rutas:
// 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
// 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();
});
});// 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:
$tokens = $user->tokens;
foreach ($tokens as $token) {
echo $token->name;
echo $token->last_used_at;
echo $token->abilities; // ['*'] o ['server:update']
}Revocar tokens:
// 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):
$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
// 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
// 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
SANCTUM_STATEFUL_DOMAINS=localhost,app.midominio.com
SESSION_DOMAIN=.midominio.comComparación: Sanctum vs Passport vs Jetstream
| Característica | Sanctum | Passport | Jetstream |
|---|---|---|---|
| Propósito | APIs simples y SPAs | OAuth2 completo | Scaffolding UI |
| Complejidad | Simple | Complejo | Intermedio |
| Tokens | Tokens simples | Tokens OAuth2 | Tokens Sanctum |
| SPA Auth | ✅ (Cookies) | ❌ | ✅ (con Sanctum) |
| OAuth2 | ❌ | ✅ | ❌ |
| Ideal para | APIs propias, SPAs | APIs públicas, terceros | Apps web completas |
Seguridad y Mejores Prácticas
1. Configurar CORS correctamente
// 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
// app/Http/Kernel.php
'api' => [
'throttle:api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],3. Usar HTTPS siempre
// .env
SESSION_SECURE_COOKIE=true4. Rotación de tokens
// 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
// Solución: Verificar config/cors.php
'paths' => ['api/*', 'sanctum/csrf-cookie'],
'supports_credentials' => true,Problema: Token no funciona después de logout
// Solución: Usar diferentes guardias
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'sanctum',
'provider' => 'users',
],
],Problema: Múltiples tokens por usuario
// 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:
# App con Vue.js frontend + Laravel backend
# App móvil que consume tu API
# Microservicios internos
# Dashboard administrativo SPASanctum 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
Publicar un comentario