Autenticación en Laravel: login, sesiones, Sanctum y permisos
La autenticación en Laravel permite identificar quién está utilizando una aplicación mediante sesiones, cookies o credenciales de API, mientras la autorización determina qué acciones puede realizar después de autenticarse. Comprender esa diferencia es fundamental para construir login, paneles privados, aplicaciones multiusuario, SPAs, aplicaciones móviles y APIs realmente seguras.
¿Cómo funciona la autenticación en Laravel?
Laravel identifica a un usuario mediante su sistema de autenticación y mantiene esa identidad durante las solicitudes utilizando mecanismos como sesiones o autenticación para APIs.
En una aplicación web tradicional, el usuario puede iniciar sesión mediante email y contraseña, Laravel crea una sesión y el navegador conserva una cookie asociada.
Para APIs, Laravel Sanctum puede autenticar una SPA propia mediante sesión y cookies o utilizar tokens para determinados clientes móviles, integraciones y consumidores de API.
Después de autenticar al usuario, Policies, Gates y otras reglas de autorización determinan si puede leer, crear, editar o eliminar un recurso concreto.
La autenticación es solo una parte del sistema de acceso
Registro
Crea la identidad del usuario.
Login
Verifica sus credenciales.
Sesión
Mantiene autenticado al navegador.
Sanctum
Cubre SPA y autenticación de APIs.
Policies
Controlan acciones sobre recursos.
Gates
Resuelven permisos más generales.
Autenticación y autorización no son lo mismo
Esta distinción es una de las bases de cualquier sistema multiusuario.
Autenticación
Responde:
¿Quién eres?
Por ejemplo, después de introducir email y contraseña, la aplicación determina que la solicitud pertenece al usuario 145.
Autorización
Responde:
¿Puedes realizar esta acción?
El usuario 145 puede estar autenticado, pero eso no significa que pueda editar el pedido del usuario 892.
Solicitud
↓
¿Quién eres?
↓
Autenticación
↓
¿Qué puedes hacer?
↓
Autorización
↓
Acción
Guards y providers cumplen responsabilidades distintas
Cuando empiezas con Laravel, estos conceptos pueden parecer innecesariamente abstractos.
La idea, sin embargo, es bastante simple.
Guard
Define cómo se mantiene o comprueba la autenticación durante una solicitud.
Una aplicación web, por ejemplo, puede utilizar autenticación basada en sesión.
Provider
Define cómo localizar al usuario desde el almacenamiento persistente.
En una aplicación habitual, ese usuario puede recuperarse mediante un model Eloquent.
Guard
↓
¿Cómo autentico esta solicitud?
Provider
↓
¿Cómo encuentro al usuario?
La configuración relacionada con estos mecanismos se encuentra alrededor del sistema de autenticación de Laravel.
Administrador, editor, cliente o profesional son conceptos de autorización del negocio, no automáticamente guards distintos.
Crear un usuario comienza validando y almacenando correctamente sus credenciales
Una implementación simplificada puede recibir:
Nombre.
Email.
Contraseña.
Primero validamos.
<?php
$datos = $request->validate([
'name' => [
'required',
'string',
'max:150'
],
'email' => [
'required',
'email',
'max:255',
'unique:users,email'
],
'password' => [
'required',
'confirmed'
]
]);
La contraseña nunca debería almacenarse como texto plano.
<?php
use Illuminate\Support\Facades\Hash;
$user = User::create([
'name' => $datos['name'],
'email' => $datos['email'],
'password' => Hash::make(
$datos['password']
)
]);
El sistema compara la contraseña proporcionada contra su representación protegida.
Auth::attempt() puede verificar las credenciales e iniciar una sesión
Para una aplicación web tradicional, podemos recibir email y contraseña.
<?php
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
public function login(
Request $request
) {
$credentials = $request->validate([
'email' => [
'required',
'email'
],
'password' => [
'required'
]
]);
if (Auth::attempt($credentials)) {
$request->session()
->regenerate();
return redirect()
->intended('/panel');
}
return back()
->withErrors([
'email' =>
'Las credenciales no son correctas.'
])
->onlyInput('email');
}
¿Por qué regenerar la sesión?
Después de autenticar correctamente al usuario, regenerar el identificador de la sesión ayuda a proteger el cambio de estado de anónimo a autenticado.
El sistema de autenticación se encarga de verificarla contra la contraseña almacenada.
El middleware auth evita repetir comprobaciones de login en cada controller
Una ruta privada puede protegerse directamente.
<?php
Route::get(
'/panel',
[PanelController::class, 'index']
)->middleware('auth');
También puedes proteger un conjunto completo de rutas.
Route::middleware('auth')
->group(function () {
Route::get(
'/panel',
[PanelController::class, 'index']
);
Route::get(
'/perfil',
[PerfilController::class, 'show']
);
});
Esto centraliza una regla sencilla:
si no existe un usuario autenticado, esas rutas no deberían ejecutarse como si fuera un usuario válido.
Una vez autenticado puedes obtener al usuario de la solicitud
<?php
$user = $request->user();
También puedes utilizar la fachada Auth.
<?php
use Illuminate\Support\Facades\Auth;
$user = Auth::user();
$userId = Auth::id();
A partir de esa identidad puedes comenzar a aplicar reglas de autorización.
Siempre que corresponda, deriva esa identidad desde la sesión o credencial autenticada.
Logout no consiste solamente en redirigir al usuario
En autenticación basada en sesión puedes cerrar la sesión mediante Auth.
<?php
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
public function logout(
Request $request
) {
Auth::logout();
$request->session()
->invalidate();
$request->session()
->regenerateToken();
return redirect('/');
}
De esta manera eliminas la información de autenticación, invalidas la sesión anterior y renuevas la protección CSRF.
“Recordarme” modifica la duración práctica de la autenticación
Si tu aplicación ofrece esta opción, la decisión debería ser explícita.
$remember = $request->boolean(
'remember'
);
if (Auth::attempt(
$credentials,
$remember
)) {
$request->session()
->regenerate();
}
No todos los sistemas necesitan sesiones persistentes.
Una aplicación financiera, un panel administrativo o una plataforma con datos sensibles puede requerir políticas diferentes a un sitio de consumo general.
No todas las aplicaciones deberían autenticarse de la misma forma
Antes de implementar tokens, debes saber quién consumirá la aplicación.
Aplicación Laravel tradicional
Si Laravel renderiza directamente la interfaz web, las sesiones y cookies del framework suelen ser la solución natural.
SPA propia
Una SPA de primera parte conectada a Laravel puede autenticarse mediante Sanctum usando sesiones y cookies.
Aplicación móvil
Puede necesitar autenticación mediante token.
Integración externa
Un tercero que consume tu API puede necesitar tokens o un protocolo de autorización más avanzado.
El tipo de cliente determina buena parte de la estrategia.
Sanctum cubre dos necesidades relacionadas, pero diferentes
Sanctum puede ayudarte con:
Autenticación stateful de una SPA propia.
API tokens para determinados clientes.
Puedes instalar el soporte API mediante:
php artisan install:api
Después la estrategia concreta dependerá de si estás construyendo:
una SPA, una aplicación móvil, una integración o una API para terceros.
Antes de profundizar en Sanctum, entiende el ciclo completo de una API Laravel
Routes, Resources, HTTP, validación y Eloquent.
El model de usuario puede emitir tokens mediante Sanctum
Para trabajar con personal access tokens, el model puede utilizar el trait correspondiente.
<?php
use Laravel\Sanctum\HasApiTokens;
class User extends Authenticatable
{
use HasApiTokens;
}
Después puedes generar un token.
$token = $user
->createToken('mobile');
return [
'token' =>
$token->plainTextToken
];
No lo publiques, no lo registres indiscriminadamente en logs y evita exponerlo repetidamente.
auth:sanctum protege rutas que necesitan una identidad autenticada
<?php
Route::get(
'/usuario',
function (Request $request) {
return $request->user();
}
)->middleware('auth:sanctum');
También puedes proteger un grupo completo.
Route::middleware(
'auth:sanctum'
)->group(function () {
Route::post(
'/productos',
[ProductoController::class, 'store']
);
Route::patch(
'/productos/{producto}',
[ProductoController::class, 'update']
);
});
Esto verifica la autenticación.
Todavía necesitamos resolver si ese usuario puede crear o modificar ese producto.
Para una SPA propia, Sanctum puede utilizar la autenticación de sesión de Laravel
Este punto es importante:
no necesitas tratar obligatoriamente tu propia SPA como un tercero que almacena un Bearer token.
Sanctum puede utilizar cookies y sesiones de Laravel para una SPA considerada de primera parte.
En la configuración de middleware puedes habilitar este comportamiento.
->withMiddleware(
function (Middleware $middleware): void {
$middleware->statefulApi();
}
)
Tu SPA y la API también deben configurarse correctamente en relación con dominio, cookies y CORS.
Una SPA autenticada mediante cookies debe considerar protección CSRF
Antes del login, el frontend puede inicializar la protección CSRF utilizando el endpoint de Sanctum.
GET /sanctum/csrf-cookie
Después, el cliente puede enviar la solicitud de login.
GET /sanctum/csrf-cookie
↓
POST /login
↓
Sesión Laravel
↓
Cookie
↓
auth:sanctum
Esto es diferente a la estrategia donde un cliente externo envía un Bearer token en cada request.
Un cliente basado en token envía la credencial en cada solicitud
Authorization: Bearer TU_TOKEN
Este enfoque puede resultar apropiado para determinados:
Clientes móviles.
Integraciones.
Scripts.
Consumidores externos.
No confundas token con usuario
El token es una credencial utilizada para asociar una solicitud con una identidad.
La identidad y sus permisos siguen siendo conceptos separados.
Puedes limitar qué capacidades tiene un token
Un token no necesariamente necesita acceso total.
$token = $user->createToken(
'integracion-stock',
[
'productos:read',
'stock:update'
]
);
Después puedes comprobar si la credencial posee una capacidad.
if (
$request->user()
->tokenCan('stock:update')
) {
// El token declara esta capacidad.
}
Tener stock:update no necesariamente significa poder modificar productos de cualquier empresa.
Los tokens deberían poder revocarse
Si una credencial se pierde, deja de utilizarse o un dispositivo ya no es confiable, deberías poder invalidarla.
Revocar el token actual
$request->user()
->currentAccessToken()
->delete();
Revocar todos
$user->tokens()
->delete();
También puedes mantener varios tokens independientes asociados a dispositivos o integraciones.
La duración de un token también forma parte del modelo de seguridad
Una integración interna, una app móvil y un acceso temporal pueden necesitar políticas diferentes.
Puedes diseñar tokens con una fecha de expiración cuando sea apropiado.
$token = $user->createToken(
'acceso-temporal',
['productos:read'],
now()->addDays(7)
);
Expirar tokens no elimina la necesidad de poder revocarlos antes.
401 y 403 representan problemas diferentes
401 Unauthorized
A pesar de su nombre histórico, normalmente indica que la solicitud no está correctamente autenticada.
Por ejemplo:
no existe una sesión válida o el token no es aceptado.
403 Forbidden
El sistema reconoce la identidad, pero no permite la acción.
401
↓
No estás autenticado correctamente.
403
↓
Sé quién eres,
pero no puedes hacer esto.
Esta diferencia resulta especialmente importante al construir APIs.
Después del login necesitas decidir quién puede hacer qué
Imagina una aplicación con proyectos.
Un usuario intenta:
PATCH /api/proyectos/856
El usuario puede tener:
Sesión válida.
Token válido.
Cuenta activa.
Y aun así no ser propietario de ese proyecto.
Ese último problema no pertenece al login.
Pertenece a la autorización.
Una Policy agrupa reglas de autorización alrededor de un recurso
Puedes generar una Policy asociada a un model.
php artisan make:policy ProyectoPolicy --model=Proyecto
Después puedes definir una regla como:
<?php
public function update(
User $user,
Proyecto $proyecto
): bool {
return $user->id
=== $proyecto->user_id;
}
La pregunta queda claramente expresada:
¿puede este usuario actualizar este proyecto concreto?
Una Policy puede representar varias acciones
viewAny()
view()
create()
update()
delete()
restore()
forceDelete()
La autorización debería ejecutarse antes de realizar una acción sensible
Una ruta puede aplicar autorización antes de ejecutar la lógica.
Route::patch(
'/proyectos/{proyecto}',
[ProyectoController::class, 'update']
)->can(
'update',
'proyecto'
);
Si el usuario no está autorizado, la solicitud no debería continuar como si tuviera permiso.
Gates son útiles para permisos que no necesariamente pertenecen a un model concreto
Por ejemplo, acceder a un dashboard administrativo.
<?php
use Illuminate\Support\Facades\Gate;
Gate::define(
'ver-panel-administracion',
function (User $user): bool {
return $user->es_admin;
}
);
Conceptualmente:
Permiso general
↓
Gate
Acción sobre un recurso
↓
Policy
No necesitas escoger una de las dos herramientas para toda la aplicación.
Pueden convivir.
Roles y permisos son una capa del negocio, no una sustitución de Policies
Puedes tener roles como:
Administrador.
Editor.
Cliente.
Profesional.
Sin embargo, un rol puede ser demasiado general para responder preguntas como:
¿puede este cliente editar esta solicitud?
¿puede este profesional ver esta propuesta?
¿puede este administrador modificar una cuenta perteneciente a otra empresa?
Combina contexto con permisos
Rol
+
Propietario
+
Empresa
+
Estado del recurso
+
Regla del negocio
=
Autorización final
Centraliza reglas cuando representen una política real de acceso.
En aplicaciones SaaS, estar autenticado tampoco significa pertenecer a la empresa correcta
Imagina dos empresas:
Empresa A
├── Usuario 1
└── Proyecto 10
Empresa B
├── Usuario 2
└── Proyecto 900
El Usuario 1 puede estar perfectamente autenticado.
Pero no debería poder obtener:
GET /api/proyectos/900
si ese proyecto pertenece a la Empresa B.
El aislamiento debe aplicarse en el backend
No basta con que el frontend nunca muestre el ID 900.
El login seguro empieza mucho antes de Auth::attempt()
También debes considerar:
Hash correcto de contraseñas.
Validación de registro.
Recuperación segura.
Confirmación en acciones sensibles.
Protección contra intentos automatizados.
Nunca compares contraseñas manualmente
Utiliza los mecanismos de hashing y autenticación del framework en vez de inventar un sistema criptográfico propio.
Un endpoint de login no debería aceptar intentos ilimitados sin controles
Una persona puede equivocarse al escribir su contraseña.
Un atacante también puede automatizar miles de intentos.
Por eso un sistema de autenticación profesional debería considerar límites adecuados para operaciones sensibles.
email + contraseña 1
email + contraseña 2
email + contraseña 3
email + contraseña 4
...
miles de intentos
El objetivo no es bloquear indiscriminadamente a usuarios legítimos, sino aumentar el costo de la automatización maliciosa.
Autenticar una cuenta no demuestra automáticamente que controla el email registrado
Para determinadas aplicaciones puede ser importante verificar la dirección de correo.
Esto resulta especialmente útil cuando el email será utilizado para:
Recuperar acceso.
Recibir información sensible.
Confirmar acciones.
Establecer una identidad confiable.
Nuevamente:
email verificado, usuario autenticado y usuario autorizado son tres afirmaciones diferentes.
Diseña “olvidé mi contraseña” como parte central del sistema de autenticación
En una aplicación real, las personas olvidarán su contraseña.
No diseñes un login pensando únicamente en el camino:
Email correcto
+
Contraseña correcta
=
Login
También necesitas considerar:
recuperación, expiración de enlaces, cambio de contraseña y sesiones previamente activas.
Cambiar una contraseña puede requerir invalidar sesiones activas en otros dispositivos
Imagina que una cuenta estaba abierta en un equipo perdido.
Cambiar la contraseña puede no ser suficiente si mantienes sesiones antiguas válidas sin una política definida.
En determinados escenarios puedes requerir cierre de otras sesiones después de confirmar la contraseña actual.
Auth::logoutOtherDevices(
$currentPassword
);
Las decisiones de sesiones deberían responder al riesgo de la aplicación.
Los permisos deben probarse también desde el lado negativo
No basta con comprobar que el propietario puede editar un proyecto.
También deberías comprobar que otro usuario no puede.
<?php
public function test_otro_usuario_no_puede_editar_proyecto(): void
{
$owner = User::factory()
->create();
$otro = User::factory()
->create();
$proyecto = Proyecto::factory()
->for($owner)
->create();
$this
->actingAs($otro)
->patchJson(
"/api/proyectos/{$proyecto->id}",
[
'nombre' => 'Intento'
]
)
->assertForbidden();
}
Prueba una matriz de acceso
Invitado.
Usuario autenticado.
Propietario.
Usuario ajeno.
Administrador.
No necesitas implementar OAuth2 completo para cada API Laravel
Sanctum cubre muy bien muchos escenarios habituales:
SPA, móvil, first-party UI y APIs con tokens sencillos.
Passport cobra sentido cuando realmente necesitas las capacidades completas de un servidor OAuth2.
SPA propia
↓
Sanctum
App móvil propia
↓
Sanctum
API tokens sencillos
↓
Sanctum
OAuth2 completo
↓
Evaluar Passport
Sesiones, Sanctum o tokens: una guía práctica
| Escenario | Enfoque habitual |
|---|---|
| Laravel renderiza la web | Sesiones / cookies |
| SPA propia con Laravel backend | Sanctum stateful |
| App móvil propia | Sanctum token |
| Script o integración propia | Token según necesidad |
| API sencilla para terceros | Evaluar Sanctum |
| OAuth2 completo | Evaluar Passport |
Qué evitar al implementar autenticación en Laravel
En qué orden aprender autenticación en Laravel
HTTP y sesiones
Comprende request, cookies y estado.
Registro
Usuarios y contraseñas.
Login
Auth::attempt y sesión.
Middleware
Protege rutas.
Logout
Invalida correctamente la sesión.
Sanctum
SPA y APIs.
Policies
Autoriza recursos.
Roles
Modela niveles de acceso.
Tokens
Abilities, expiración y revocación.
Testing
Comprueba acceso y rechazo.
Un buen sistema de autenticación identifica al usuario sin confundir identidad con permisos
Protege las contraseñas correctamente.
Regenera la sesión después del login.
Utiliza middleware para proteger rutas privadas.
Cierra e invalida sesiones correctamente.
Utiliza Sanctum según el tipo de cliente.
Para una SPA propia, comprende cookies, sesiones y CSRF.
Para APIs basadas en tokens, considera abilities, revocación y expiración.
Separa siempre autenticación de autorización.
Centraliza permisos de recursos mediante Policies cuando corresponda.
Prueba también que usuarios sin permiso sean rechazados.
El verdadero objetivo de la autenticación no es simplemente hacer funcionar un formulario de login: es establecer una identidad confiable y utilizarla como base para aplicar reglas de acceso coherentes en toda la aplicación.
Continúa profundizando en Laravel
Preguntas sobre autenticación en Laravel
Laravel identifica usuarios mediante su sistema de autenticación. Una aplicación web puede mantener la autenticación mediante sesiones y cookies, mientras una API puede utilizar Sanctum y otros mecanismos según el tipo de cliente.
La autenticación determina quién realiza una solicitud. La autorización determina si ese usuario puede realizar una acción concreta. Un usuario autenticado no necesariamente está autorizado para modificar todos los recursos.
Sanctum es una solución de autenticación para escenarios como SPAs de primera parte, aplicaciones móviles y APIs basadas en tokens. Puede autenticar una SPA mediante sesiones y cookies o trabajar con API tokens.
No necesariamente. Para una SPA propia, Sanctum puede utilizar la autenticación basada en sesiones y cookies de Laravel, junto con protección CSRF, en lugar de obligar a la SPA a gestionar un API token como credencial persistente.
Es un middleware utilizado para exigir que una solicitud esté autenticada mediante Sanctum. Dependiendo del cliente, Sanctum puede reconocer una sesión stateful o una credencial de API válida.
Las Policies son clases que organizan reglas de autorización alrededor de un model o recurso. Por ejemplo, pueden decidir si un usuario puede ver, actualizar o eliminar un proyecto determinado.
Un Gate resulta útil para determinadas reglas generales de autorización, mientras una Policy organiza permisos alrededor de un model o recurso. Ambas herramientas pueden coexistir en la misma aplicación.
En una autenticación basada en sesión puedes utilizar Auth::logout(), invalidar la sesión actual y regenerar el token CSRF. En una API basada en tokens, también puede ser necesario revocar el token correspondiente.
Un 401 normalmente indica que la solicitud no está correctamente autenticada. Un 403 indica que la identidad ya es conocida, pero no tiene permiso para realizar la acción solicitada.
Una aplicación segura primero identifica al usuario y después verifica cada acción
Domina sesiones, login y middleware; después aprende Sanctum, tokens, Policies, Gates y autorización contextual. Cuando estas capas están separadas, construir aplicaciones multiusuario y APIs seguras resulta mucho más claro.
Continuar con APIs Laravel →