Autenticación SPA con Sanctum en Laravel
Uno de los dos problemas que intenta resolver Laravel Sanctum es el hecho de autenticar una aplicación frontend con el backend de laravel. Para esto, laravel utiliza un CSRF token o cookie autenticada y una cookie de sessión.
El siguiente diagrama representa el flujo de autenticación de una aplicación con el backend de laravel.
El primer paso es identificar si existe el Cross Site Request Forgery Token, el cual en Laravel es representado mediante la cookie XSRF-TOKEN
. Si no existe este valor deberás generarlo mediante el endpoint respectivo en el siguiente paso.
La manera de generar el CSRF Token en Laravel es mediante una llamada GET
al endpoint /csrf-token
. Laravel enviará este valor en forma de cookie mediante el header HTTP Set-Cookie
.
Una vez obtenido el primer CSRF Token (también podríamos llamarlo token no autenticado), podemos usarlo junto con la sesión de laravel (otra cookie) para autenticar el usuario realizando una llamada POST
al endpoint /login
.
Si las credentials enviadas en formato JSON son correctas, se enviará nuevamente una cookie XSRF-TOKEN
la cual puede ser usada para consumir los endpoints protegidos por sanctum (token autenticado). En cualquier otro caso, deberás enviar nuevamente las credenciales correctas para autenticar el usuario.
De aquí en adelante, vamos a ver este proceso un poco más a detalle.
Obtener el CSRF Token
En primer lugar, debes saber que Laravel utiliza cookies para autenticar los solicitudes entrantes en la API. Estas cookies son XSRF-TOKEN
y laravel_session
. Para obtenerlas no debes hacer más que consumir el siguiente endpoint usando el método HTTP GET.
GET /sanctum/csrf-cookie HTTP/1.1
Obtendrás una respuesta muy similar a la siguiente.
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: http://yourdomain.com
Access-Control-Allow-Credentials: true
Set-Cookie: XSRF-TOKEN=eyJpdiI6ImZDNnNOQStVdnd1U0hoYTl0dUhaRkE9PSIsInZhbHVlIjoiMURDZGp2azRpRE00Tk1sRXdTTDUzaG11NDVxWW9ScTlvQ0V3aFM5Mnh4VzROSXFXY3VUSFVnMzNlcTVmbmRNRUs4WlZ1K0ZyWElZREpIVkFNZW9PT3UyN0ZtWUp5Z3RXYmxCdWUyRUZtVnE5d3NFWTI1NmpqR240WXFHOHkzVmUiLCJtYWMiOiIzYWU3MjViYmVjYmNiNWU3ZGI0MWY0ZWEzY2VmMGYwYTA3MzIyMjQzMzFhNzM5ODEyOWJmOGMxMTEzMzViZjY2IiwidGFnIjoiIn0%3D; expires=Fri, 18 Aug 2023 04:32:02 GMT; Max-Age=7200; path=/; samesite=lax
Set-Cookie: laravel_session=eyJpdiI6IjAvT1dkaXJvbDNiTXZQL0crVG11Z1E9PSIsInZhbHVlIjoicm5RRWJKV1ZjUWd6YnN2WDVMZmN1WDNYcEdrREF5NzJBb0t4RnUzWEkvRXUyNWc4Q24vanNNQ3NLTUhrczdkNEEvelFSM3Iwb3RHVEE4eXhPZE5COTBmQWJkVXh3a1BnbjErT3NXSkR0Ri9ZK0hBKzVHbG5OVHdFRXplSkZWMWsiLCJtYWMiOiJmMzg2ZGNjOGMxN2FhMWM5N2Q5OGU2YmNmMDE0ZjkyMzI2ZWY4ZGY1MGE4NDc5MGEyYzU5MTcwNjE2NTA5OWQyIiwidGFnIjoiIn0%3D; expires=Fri, 18 Aug 2023 04:32:02 GMT; Max-Age=7200; path=/; httponly; samesite=lax
El header Access-Control-Allow-Credentials
nos indica que la respuesta de la API puede ser usada por aplicaciones front-end o javascript.
El header Access-Control-Allow-Origin
nos indica el dominio que puede acceder a dicho recurso. Este header es siempre enviado cuando "allow credentials" es true
.
Por simplicidad, de ahora en adelante utilizaremos valores más cortos para las cookies. Supongamos que las cookies que envió el servidor fueron las siguientes, las cuales no son más que valores representativos del valor real de la cookie.
XSRF-TOKEN: csrfXsrfToken
laravel_session: csrfLaravelSesion
Presta mucha atención a los valores generados, ya que se utilizarán en pasos posteriores.
Autenticarse en la API
Para consumir el endpoint de autenticación debes haber consumido previamente el endpoint para obtener el CSRF Token y la sesión de laravel. Estos valores deben enviarse en el header cookie
. Además de esto, debes enviar el XSRF-TOKEN
como request header.
POST /login HTTP/1.1
Content-Type: application/json
Accept: application/json
Cookie: laravel_session=csrfLaravelSesion; XSRF-TOKEN=csrfXsrfToken
XSRF-TOKEN: csrfXsrfToken
laravel_session: csrfLaravelSesion
{
"email": "admin@admin.com",
"password": "password"
}
De hecho, XSRF-TOKEN
en las cookies es meramente opcional ya que también está siendo enviado como header. Obtendrás una respuesta muy similar a la siguiente. Observa que las cookies han sido enviadas nuevamente por laravel con valores diferentes.
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: http://yourdomain.com
Access-Control-Allow-Credentials: true
Set-Cookie: XSRF-TOKEN=loginXsrfToken
Set-Cookie: laravel_session=loginLaravelSession
Reitero, los valores de las cookies han sido simplificados para efectos de este post.
Consumir la API usando la sesión de autenticación
Una vez obtienes una respuesta satisfactoria del endpoint de autenticación puedes empezar a consumir cualquier otro endpoint que use el middleware auth:sanctum
.
Supongamos que creaste un nuevo controlador llamado UserModelsController
. Para proteger el nuevo endpoint debes usar el middleware de sanctum de la siguiente forma.
Route::middleware(['auth:sanctum'])
->resource('user-models', UserModelsController::class);
Además, debes haber configurado previamente una de las siguientes dos variables en laravel.
APP_URL=http://yourdomain.com
FRONTEND_URL=http://yourdomain.com
Una vez hecho esto puedes realizar la petición agregando token obtenido del endpoint de autenticación y última sesión de laravel debe ser agregada al header cookie
.
POST /api/user-models HTTP/1.1
Content-Type: application/json
Accept: application/json
origin: http://yourdomain.com
Cookie: laravel_session=loginLaravelSesion
XSRF-TOKEN: loginXsrfToken
{
"user_id": 1,
"mode_name": "model name"
}
Para que la petición pueda ser autenticada debes enviear el header origin
con el dominio permitido para consumir la API. Si no envías el dominio habilitado obtendrás un error de autenticación. Este dominio es el mismo que debiste recibir en el header Access-Control-Allow-Origin
.
Una vez obtienes una respuesta satisfactoria de tu endpoint obtendrás nuevos headers. De esos headers debes enviar nuevamente laravel_session
. Sin embargo, podrás seguir utilizando el mismo valor para XSRF-TOKEN
hasta que expire. De todas formas, es siempre recomendable enviar el último valor obtenido.