Mostrando entradas con la etiqueta laravel sanctum. Mostrar todas las entradas
Mostrando entradas con la etiqueta laravel sanctum. Mostrar todas las entradas

sábado, 21 de junio de 2025

Aplicación monolítica con mejoras de javascript

Si quiero una app que use rutas y controllers web para la navegación, pero que algunos componentes envíen mensajes a una API a través de AJAX. Primero¿Es posible esto? Y segundo ¿Necesito doble autenticación, una estándar de Laravel y la otra de Sanctum?



¡Sí, esto es totalmente posible y, de hecho, es una arquitectura muy común y efectiva! Estás describiendo lo que a menudo se conoce como una "aplicación híbrida" o "aplicación monolítica con mejoras de JavaScript".


1. ¿Es posible tener rutas y controladores web para navegación y AJAX a API?

Absolutamente sí. Esta es una estrategia muy extendida y madura en el desarrollo web.

Así es como funciona:

  • Rutas y Controladores Web (HTML Rendering): Para la mayor parte de la navegación (cuando un usuario va de /perfil a /configuracion), Laravel se encarga de todo. Una ruta web (Route::get('/perfil', ...)) llama a un controlador que devuelve una vista Blade (return view('profile');). El navegador recibe y renderiza el HTML completo, y la página se recarga.
  • AJAX a la API (Interacciones Dinámicas): Dentro de esas vistas Blade (o cualquier parte de tu HTML), puedes tener pequeños componentes interactivos de JavaScript (puedes usar React, Vue, o simplemente JavaScript vainilla) que hagan llamadas AJAX (Asynchronous JavaScript and XML) a endpoints de tu API.
    • Estas llamadas AJAX no recargan la página. Solo envían o reciben pequeños paquetes de datos (generalmente JSON) y actualizan partes específicas de la interfaz de usuario de forma dinámica.
    • Ejemplo: Tienes un botón "Me gusta" en una publicación. Cuando el usuario hace clic, un pequeño script JavaScript en tu Blade envía una solicitud AJAX (un POST) a un endpoint de tu API (/api/posts/{id}/like). La API procesa el "me gusta" y devuelve una confirmación, y tu JavaScript actualiza el contador de "Me gusta" en la página sin recargarla.

Este enfoque te permite tener lo mejor de ambos mundos: la simplicidad y el SEO de las aplicaciones tradicionales basadas en HTML para la navegación principal, y la interactividad y fluidez de una SPA para componentes específicos.


2. ¿Necesito doble autenticación (estándar de Laravel y Sanctum)?

No, no necesitas "doble autenticación" en el sentido de implementar dos sistemas de autenticación completamente separados y redundantes para el mismo usuario.

En este escenario, Laravel se encarga de la autenticación de sesión tradicional, y Sanctum se integra perfectamente con esa misma sesión.

Así es como funciona la autenticación:

  • Autenticación Estándar de Laravel (Basada en Sesiones y Cookies):

    • Cuando un usuario inicia sesión en tu aplicación web (a través de un formulario de login gestionado por una ruta web de Laravel), Laravel crea una sesión de usuario y envía una cookie de sesión al navegador.
    • Todas las solicitudes subsiguientes a tus rutas web (Blade views) se autentican automáticamente porque el navegador envía esa cookie de sesión, y Laravel la valida.
  • Autenticación de API con Sanctum (para tus llamadas AJAX):

    • Aquí es donde Sanctum brilla. Para las llamadas AJAX que haces desde tu frontend JavaScript a tus rutas api/*, Sanctum puede aprovechar la misma sesión de usuario que ya está activa.
    • Sanctum tiene un middleware llamado EnsureFrontendRequestsAreStateful (que normalmente está en tu grupo de middleware web o api por defecto, o se aplica a las rutas de Sanctum).
    • Este middleware, junto con la configuración adecuada de CORS y CSRF, permite que tus llamadas AJAX de origen cruzado (si tu API está en un subdominio o puerto diferente, como localhost:3000 y localhost:8000) se autentiquen usando la cookie de sesión existente.
    • Protección CSRF: Es crucial que tu JavaScript obtenga el token CSRF (haciendo una llamada GET a /sanctum/csrf-cookie al cargar la página) y lo envíe en un encabezado X-XSRF-TOKEN con tus solicitudes POST/PUT/PATCH/DELETE a la API. Esto protege tus llamadas AJAX contra ataques CSRF, que son una preocupación cuando se usan cookies.

En resumen:

La autenticación principal se maneja a través del sistema de sesiones de Laravel. Sanctum simplemente extiende la funcionalidad de ese sistema para permitir que tus llamadas AJAX a api/* se autentiquen utilizando la misma sesión, sin necesidad de tokens de API adicionales para ese mismo usuario en el mismo navegador.


Esta arquitectura es muy eficiente porque reutilizas el sistema de autenticación de Laravel y solo necesitas configurar Sanctum para que extienda esa funcionalidad a tus llamadas AJAX, manteniendo la seguridad y la simplicidad.

martes, 17 de junio de 2025

Implementar autenticación en una API de Laravel

Implementar autenticación en una API de Laravel es un paso fundamental para asegurar tus endpoints. Laravel ofrece varias opciones, pero la más moderna y recomendada para la mayoría de las API (especialmente las que son consumidas por Single Page Applications o aplicaciones móviles) es Laravel Sanctum.

Vamos a centrarnos en Laravel Sanctum como la solución principal, y luego mencionaremos brevemente otras alternativas.


Autenticación de API con Laravel Sanctum (Recomendado)

Laravel Sanctum es un paquete ligero que ofrece un sistema simple de autenticación basado en tokens para APIs. Es ideal para:

  1. SPAs (Single Page Applications): Donde tu frontend de JavaScript (React, Vue, Angular) y tu API de Laravel están en el mismo dominio o dominios relacionados. Aquí, Sanctum utiliza cookies/sesiones (con protección CSRF) para autenticar al usuario.

  2. Aplicaciones Móviles o Third-Party: Donde no puedes usar cookies. Para estos casos, Sanctum permite generar tokens de API que se envían en el encabezado Authorization.

Conceptos Clave de Sanctum:

  • Tokens de API: Son strings alfanuméricos largos y seguros que los usuarios pueden generar (o que se generan para ellos). Cada token está asociado a un usuario y puede tener "habilidades" (abilities/scopes) específicas que definen qué acciones puede realizar.

  • Trait HasApiTokens: Se añade a tu modelo User para que pueda generar y gestionar tokens.

  • Middleware auth:sanctum: Es el middleware de protección que usas en tus rutas de API.

  • SPA Authentication (Autenticación para SPAs): Si tu SPA y tu API están en el mismo dominio, Sanctum usa un proceso de "cookie-based authentication" que es similar a una sesión web tradicional, pero optimizado para SPAs. Implica el intercambio de un token CSRF al inicio de la sesión.

Implementación Paso a Paso de Laravel Sanctum:

1. Instalación de Sanctum

Abre tu terminal en la raíz de tu proyecto Laravel y ejecuta:

Bash

composer require laravel/sanctum


2. Publicar Migraciones y Ejecutar

Sanctum necesita una tabla en la base de datos para almacenar los tokens de API.

Bash

php artisan vendor:publish --tag="sanctum-migrations"

php artisan migrate


Esto creará una tabla personal_access_tokens en tu base de datos.

3. Configuración del Modelo User

Abre tu modelo app/Models/User.php y añade el trait HasApiTokens:

PHP

<?php


namespace App\Models;


use Illuminate\Contracts\Auth\MustVerifyEmail;

use Illuminate\Database\Eloquent\Factories\HasFactory;

use Illuminate\Foundation\Auth\User as Authenticatable;

use Illuminate\Notifications\Notifiable;

use Laravel\Sanctum\HasApiTokens; // <--- Añade esta línea


class User extends Authenticatable

{

    use HasApiTokens, HasFactory, Notifiable; // <--- Añade HasApiTokens aquí


    // ... el resto de tu modelo

}


4. Proteger Rutas de API

Para proteger tus rutas de API, usa el middleware auth:sanctum. Generalmente, esto se hace en tu archivo routes/api.php:

PHP

<?php


use Illuminate\Http\Request;

use Illuminate\Support\Facades\Route;


// Rutas públicas o de autenticación (login, registro)

Route::post('/register', [App\Http\Controllers\Auth\RegisterController::class, 'register']);

Route::post('/login', [App\Http\Controllers\Auth\LoginController::class, 'login']);


// Rutas protegidas por Sanctum

Route::middleware('auth:sanctum')->group(function () {

    Route::get('/user', function (Request $request) {

        return $request->user();

    });


    Route::post('/logout', [App\Http\Controllers\Auth\LoginController::class, 'logout']);


    // Ejemplo de ruta con habilidades/scopes

    Route::get('/orders', function (Request $request) {

        // El token debe tener la habilidad 'read:orders'

        if ($request->user()->tokenCan('read:orders')) {

            return response()->json(['message' => 'Lista de órdenes']);

        }

        return response()->json(['message' => 'No tienes permiso para leer órdenes'], 403);

    });

});


5. Implementar Lógica de Autenticación (Login/Logout)

Necesitarás controladores para manejar el registro y el login. Aquí un ejemplo simplificado de un controlador de Login:

PHP

<?php


namespace App\Http\Controllers\Auth;


use App\Http\Controllers\Controller;

use App\Models\User;

use Illuminate\Http\Request;

use Illuminate\Support\Facades\Hash;

use Illuminate\Validation\ValidationException;


class LoginController extends Controller

{

    public function login(Request $request)

    {

        $request->validate([

            'email' => ['required', 'email'],

            'password' => ['required'],

            'device_name' => ['required'], // Necesario para la autenticación de tokens

        ]);


        $user = User::where('email', $request->email)->first();


        if (! $user || ! Hash::check($request->password, $user->password)) {

            throw ValidationException::withMessages([

                'email' => ['Las credenciales proporcionadas son incorrectas.'],

            ]);

        }


        // Si el login es exitoso, crea un token

        // Puedes definir habilidades (scopes) aquí

        $token = $user->createToken($request->device_name, ['read', 'write:orders'])->plainTextToken;


        return response()->json(['token' => $token]);

    }


    public function logout(Request $request)

    {

        // Revocar el token actual usado para la autenticación

        $request->user()->currentAccessToken()->delete();


        return response()->json(['message' => 'Sesión cerrada exitosamente']);

    }

}


6. Cómo usar el Token en el Cliente (Móvil/Third-Party)

Una vez que el cliente obtiene el token (después de un login exitoso), debe enviarlo en el encabezado Authorization de todas las solicitudes subsiguientes a rutas protegidas.

Authorization: Bearer YOUR_GENERATED_TOKEN_HERE


Ejemplo con JavaScript (fetch API):

JavaScript

// Después de iniciar sesión y obtener el token

const token = 'tu_token_generado_aqui';


fetch('http://localhost:8000/api/user', {

    method: 'GET',

    headers: {

        'Accept': 'application/json',

        'Authorization': `Bearer ${token}` // Envía el token Bearer

    }

})

.then(response => response.json())

.then(data => console.log(data))

.catch(error => console.error('Error:', error));


7. Configuración para SPAs (Autenticación basada en Cookies)

Si tu SPA y tu API están en el mismo dominio (o subdominio), puedes usar la autenticación basada en cookies.

  1. Añade el middleware EnsureFrontendRequestsAreStateful:
    Abre app/Http/Kernel.php y asegúrate de que el middleware \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class esté en el grupo de middleware api o web. (Generalmente ya está en web por defecto).

  2. Configura tus dominios frontend en config/sanctum.php:
    Asegúrate de que el dominio de tu SPA esté listado en la propiedad stateful del archivo config/sanctum.php.

  3. PHP

// config/sanctum.php

'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', 'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1')),


  1. También configura CORS en config/cors.php.

  2. Llama al endpoint sanctum/csrf-cookie:
    Desde tu SPA, antes de intentar iniciar sesión, debes hacer una solicitud GET a /sanctum/csrf-cookie. Esto establece la cookie XSRF que Sanctum usará para las solicitudes subsiguientes (login, etc.).

  3. JavaScript

// En tu SPA, al cargar la aplicación o antes del login

fetch('http://localhost:8000/sanctum/csrf-cookie')

    .then(response => {

        // Ahora puedes proceder con la solicitud de login (POST /api/login)

        // que enviará automáticamente las cookies de sesión y CSRF.

    });


  1. Después de esto, las solicitudes de login y otras solicitudes API enviadas desde tu SPA al mismo dominio serán autenticadas a través de la cookie de sesión.


Otras Opciones de Autenticación de API en Laravel:

  1. Laravel Passport (OAuth2):

    • Cuándo usarlo: Si necesitas una solución OAuth2 completa para tu API, especialmente si vas a tener clientes de terceros (otras aplicaciones, no solo tu propia SPA/móvil) que necesiten interactuar con tu API y requerirán diferentes "grant types" (ej. Authorization Code, Client Credentials, Password Grant). Es más robusto y complejo que Sanctum.

    • Requiere: Instalación de laravel/passport y una configuración más extensa.

  2. JWT (JSON Web Tokens):

    • Cuándo usarlo: Si prefieres un enfoque completamente stateless y tienes experiencia con JWTs. Requiere una librería de terceros como tymon/jwt-auth. El token contiene toda la información del usuario en su interior (firmada, no cifrada).

    • Requiere: Instalación de tymon/jwt-auth y configuración manual del middleware.

¿Cuál elegir?

  • Para la mayoría de los casos (SPAs y aplicaciones móviles que tú controlas): Laravel Sanctum es la opción más sencilla, ligera y recomendada. Cubre perfectamente las necesidades de autenticación basada en tokens y sesiones para SPAs.

  • Para APIs públicas o si necesitas soporte OAuth2 completo (con diferentes tipos de "grants"): Laravel Passport es la solución adecuada.

  • Si tienes requisitos muy específicos o ya estás familiarizado con JWTs: tymon/jwt-auth puede ser una opción, pero para muchos, Sanctum ya cumple con los mismos objetivos de forma más integrada en Laravel.

Siempre recuerda la importancia de usar HTTPS para todas tus comunicaciones API, ya que los tokens se transmiten por la red.