En ambiente de desarrollo tengo frontend y backend expuestos en localhost, pero el primero en el puerto 3000 y el segundo en el puerto 8000. ¿Cómo hago para usar autenticación mediante cookies de sesión?
Usar la autenticación de cookies de sesión de Laravel Sanctum con un frontend en localhost:3000
y un backend en localhost:8000
es un escenario muy común en desarrollo. Aunque ambos están en localhost
, el hecho de que usen puertos diferentes los convierte en orígenes diferentes desde la perspectiva del navegador (por la Política del Mismo Origen).
Para que esto funcione, necesitas configurar correctamente tanto el backend (Laravel) como el frontend.
I. Configuración en el Backend (Laravel - localhost:8000
)
Asumimos que ya tienes Laravel Sanctum instalado y configurado lo básico (migraciones ejecutadas, HasApiTokens
trait en el modelo User
).
1. Configurar CORS (Cross-Origin Resource Sharing)
Este es el paso más crítico para permitir que localhost:3000
hable con localhost:8000
y que las credenciales (cookies) se envíen.
Abre el archivo de configuración config/cors.php
y ajústalo así:
<?php
return [
/*
* Rutas a las que se aplicarán las reglas CORS.
* Generalmente, las rutas de tu API y el endpoint de CSRF de Sanctum.
*/
'paths' => ['api/*', 'sanctum/csrf-cookie'],
/*
* Orígenes permitidos para realizar solicitudes de origen cruzado.
* DEBE ser el origen exacto de tu frontend. ¡NO uses '*' si supports_credentials es true!
*/
'allowed_origins' => ['http://localhost:3000'],
/*
* Encabezados permitidos. '*' es común en desarrollo.
*/
'allowed_headers' => ['*'],
/*
* Métodos HTTP permitidos. '*' es común en desarrollo.
*/
'allowed_methods' => ['*'],
/*
* Si las solicitudes CORS pueden incluir credenciales (cookies, encabezados de autorización).
* ¡ESTO DEBE SER TRUE para la autenticación basada en cookies de Sanctum!
*/
'supports_credentials' => true,
/*
* Tiempo en segundos que la respuesta preflight puede ser cacheada.
*/
'max_age' => 3600,
];
2. Configurar los Dominios "Stateful" de Sanctum
Sanctum necesita saber qué dominios tratar como "first-party" para la autenticación basada en cookies.
Abre el archivo de configuración config/sanctum.php
y modifica la clave stateful
. Lo más fácil es hacerlo a través de tu archivo .env
:
-
En
config/sanctum.php
:PHP// ... 'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', 'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1')), // ...
(Normalmente,
localhost:3000
ya está incluido en el valor predeterminado si es una instalación reciente de Laravel/Sanctum. Si no, asegúrate de añadirlo). -
En tu archivo
.env
: (No es estrictamente necesario silocalhost:3000
ya está en el valor por defecto, pero si lo modificaste o tienes problemas, puedes forzarlo aquí):Code snippetSANCTUM_STATEFUL_DOMAINS=localhost:3000,localhost:8000,127.0.0.1:8000
Añade
localhost:3000
explícitamente para asegurar que Sanctum lo reconozca como un dominio "first-party" para cookies.
3. Configurar el Dominio de la Cookie de Sesión
Para localhost
con puertos diferentes, la configuración por defecto de Laravel suele funcionar. El dominio de la cookie de sesión debe ser null
o no establecido, lo que significa que la cookie se establecerá para el host actual (localhost
). El navegador, combinado con la configuración supports_credentials = true
de CORS, manejará correctamente el envío y recepción de la cookie entre los puertos.
Abre config/session.php
y asegúrate de que la clave 'domain'
esté configurada a null
(o que SESSION_DOMAIN
en tu .env
esté vacío/no definido):
// config/session.php
'domain' => env('SESSION_DOMAIN'), // Por defecto, `SESSION_DOMAIN` en .env está vacío, lo que resulta en null
- En tu archivo
.env
: No le pongas un punto (Code snippetSESSION_DOMAIN=
.
) aquí paralocalhost
(.localhost
). A veces puede causar problemas con algunos navegadores para el desarrollo local con puertos diferentes.
4. Rutas de Autenticación
Asegúrate de tener tus rutas de login y logout en routes/api.php
. El login será una ruta POST
que crea la sesión y envía una cookie (implícitamente si todo está configurado).
// routes/api.php
use App\Http\Controllers\Auth\LoginController;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
// Ruta para obtener el token CSRF (¡importante!)
Route::get('/sanctum/csrf-cookie', function () {
return response('')->withCookie(csrf_cookie());
});
Route::post('/login', [LoginController::class, 'login']);
Route::post('/logout', [LoginController::class, 'logout'])->middleware('auth:sanctum');
Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
return $request->user();
});
5. Reiniciar Servicios
Después de hacer estos cambios, detén y reinicia tu servidor Laravel (php artisan serve
) y también tu servidor de desarrollo frontend (si lo tenías corriendo).
II. Configuración en el Frontend (SPA - localhost:3000
)
Tu aplicación frontend (React, Vue, Angular) debe manejar el flujo de cookies/CSRF correctamente. La mayoría de los frameworks de frontend tienen herramientas o librerías HTTP que facilitan esto (como Axios).
1. Realizar la Solicitud CSRF al inicio
Antes de cualquier intento de login o cualquier otra solicitud autenticada (especialmente las no-GET), tu SPA debe hacer una solicitud a /sanctum/csrf-cookie
.
-
Usando Axios (Recomendado):
Axios es popular y tiene un excelente soporte para esta funcionalidad de Sanctum.
JavaScriptimport axios from 'axios'; // Configura la base URL de tu API axios.defaults.baseURL = 'http://localhost:8000/api'; // O http://localhost:8000 si tus rutas no están en /api axios.defaults.withCredentials = true; // ¡Esto es CRÍTICO! Le dice al navegador que envíe cookies cross-origin. // Interceptor para enviar el token CSRF en cada solicitud // (Axios 0.27+ lo hace automáticamente si la cookie XSRF-TOKEN está presente) // Para versiones antiguas de Axios o si quieres ser explícito: // axios.interceptors.request.use(request => { // const csrfToken = document.cookie.split('; ').find(row => row.startsWith('XSRF-TOKEN=')); // if (csrfToken) { // request.headers['X-XSRF-TOKEN'] = csrfToken.split('=')[1]; // } // return request; // }); // Tu componente/archivo principal (ej. App.js en React, main.js en Vue) // Antes de renderizar la app o antes de cualquier solicitud de login async function initializeApp() { try { // Obtener el token CSRF. Esto establecerá la cookie XSRF-TOKEN en el navegador. // Axios con withCredentials=true enviará automáticamente esta cookie en POST/PUT/DELETE // y la leerá para ponerla en el encabezado X-XSRF-TOKEN si la cookie está presente. await axios.get('http://localhost:8000/sanctum/csrf-cookie'); console.log('CSRF cookie obtained!'); // Ahora puedes iniciar tu aplicación y permitir el login } catch (error) { console.error('Failed to obtain CSRF cookie:', error); // Manejar el error (ej. mostrar un mensaje al usuario) } } initializeApp();
-
Usando Fetch API:
Con Fetch, necesitas manejar withCredentials y el encabezado X-XSRF-TOKEN más explícitamente:
JavaScriptasync function fetchCsrfToken() { try { await fetch('http://localhost:8000/sanctum/csrf-cookie', { method: 'GET', credentials: 'include' // ¡CRÍTICO! Permite enviar/recibir cookies cross-origin }); console.log('CSRF cookie obtained!'); // Ahora puedes proceder con el login } catch (error) { console.error('Failed to obtain CSRF cookie:', error); } } async function loginUser(email, password) { // Después de fetchCsrfToken() const csrfToken = document.cookie.split('; ').find(row => row.startsWith('XSRF-TOKEN='))?.split('=')[1]; const response = await fetch('http://localhost:8000/api/login', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', 'X-XSRF-TOKEN': csrfToken // Envía el token CSRF en el encabezado }, credentials: 'include', // ¡CRÍTICO! Envía la cookie de sesión y el token CSRF body: JSON.stringify({ email, password, device_name: 'browser' }) }); const data = await response.json(); console.log(data); } fetchCsrfToken().then(() => { // Luego puedes llamar a loginUser o cualquier otra función autenticada // loginUser('test@example.com', 'password'); });
Resumen de los puntos clave para localhost:3000
y localhost:8000
:
- Backend (
config/cors.php
):allowed_origins
debe ser['http://localhost:3000']
ysupports_credentials
debe sertrue
. - Backend (
config/sanctum.php
o.env
): Asegúrate de quelocalhost:3000
esté en losSANCTUM_STATEFUL_DOMAINS
. - Backend (
config/session.php
o.env
): ElSESSION_DOMAIN
debe sernull
(vacío). No uses.localhost
. - Frontend: Todas las solicitudes a la API deben incluir
credentials: 'include'
(AxioswithCredentials = true
). - Frontend: Haz una solicitud GET a
http://localhost:8000/sanctum/csrf-cookie
al inicio de tu aplicación para obtener la cookieXSRF-TOKEN
. Axios (conwithCredentials=true
) se encargará de enviar esta cookie en el encabezadoX-XSRF-TOKEN
para futuras solicitudes.
Con esta configuración, tu SPA en localhost:3000
podrá autenticarse usando las cookies de sesión de Laravel servidas por localhost:8000
, proporcionando una experiencia de desarrollo fluida.
No hay comentarios:
Publicar un comentario