TechLead

Autenticación y Patrones de Auth

Rutas protegidas, contexto de auth, manejo de JWT/sesiones y flujos de login en React

Autenticación en React

La autenticación es una parte crítica de la mayoría de las aplicaciones web. React maneja el lado del frontend — formularios de login/registro, almacenamiento de tokens, protección de rutas y gestión de sesiones de usuario. La lógica real de autenticación ocurre en el backend.

🔐 Flujo de Autenticación

  1. El usuario ingresa credenciales en un formulario de login
  2. El frontend envía las credenciales a la API del backend
  3. El backend verifica y devuelve un token (JWT) o establece una cookie de sesión
  4. El frontend almacena el token y lo incluye en las solicitudes API posteriores
  5. Las rutas protegidas verifican la autenticación válida antes de renderizar

Construyendo un Auth Context

import { createContext, useContext, useState, useEffect } from 'react';

const AuthContext = createContext(null);

export function AuthProvider({ children }) {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => { checkAuth(); }, []);

  async function checkAuth() {
    try {
      const token = localStorage.getItem('token');
      if (!token) { setLoading(false); return; }

      const res = await fetch('/api/auth/me', {
        headers: { Authorization: `Bearer ${token}` },
      });
      if (res.ok) setUser(await res.json());
      else localStorage.removeItem('token');
    } finally {
      setLoading(false);
    }
  }

  async function login(email, password) {
    const res = await fetch('/api/auth/login', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ email, password }),
    });
    if (!res.ok) throw new Error('Inicio de sesion fallido');
    const { user, token } = await res.json();
    localStorage.setItem('token', token);
    setUser(user);
  }

  function logout() {
    localStorage.removeItem('token');
    setUser(null);
  }

  return (
    <AuthContext.Provider value={{ user, loading, login, logout }}>
      {children}
    </AuthContext.Provider>
  );
}

export function useAuth() {
  const context = useContext(AuthContext);
  if (!context) throw new Error('useAuth debe usarse dentro de AuthProvider');
  return context;
}

Rutas Protegidas

import { Navigate, useLocation } from 'react-router-dom';
import { useAuth } from '../context/AuthContext';

export function ProtectedRoute({ children }) {
  const { user, loading } = useAuth();
  const location = useLocation();

  if (loading) return <LoadingSpinner />;
  if (!user) return <Navigate to="/login" state={{ from: location }} replace />;
  return children;
}

// Protección basada en roles
export function AdminRoute({ children }) {
  const { user, loading } = useAuth();
  if (loading) return <LoadingSpinner />;
  if (!user) return <Navigate to="/login" replace />;
  if (user.role !== 'admin') return <Navigate to="/unauthorized" replace />;
  return children;
}

// Uso en el router
<Routes>
  <Route path="/login" element={<LoginForm />} />
  <Route path="/dashboard" element={
    <ProtectedRoute><Dashboard /></ProtectedRoute>
  } />
  <Route path="/admin" element={
    <AdminRoute><AdminPanel /></AdminRoute>
  } />
</Routes>

Solicitudes API Autenticadas

export async function authFetch(url, options = {}) {
  const token = localStorage.getItem('token');

  const res = await fetch(url, {
    ...options,
    headers: {
      'Content-Type': 'application/json',
      ...(token && { Authorization: `Bearer ${token}` }),
      ...options.headers,
    },
  });

  if (res.status === 401) {
    localStorage.removeItem('token');
    window.location.href = '/login';
    throw new Error('Sesion expirada');
  }

  return res;
}

🔒 Mejores Practicas de Seguridad

  • Nunca almacenes datos sensibles en localStorage en producción — usa cookies httpOnly
  • Siempre valida en el servidor — la auth del frontend es para UX, no seguridad
  • Usa HTTPS para todas las solicitudes API
  • Implementa refresh de tokens — no uses tokens de acceso de larga duración
  • Limpia el estado de auth al cerrar sesión

✅ Librerías de Auth para Producción

  • NextAuth.js / Auth.js — Auth completo para Next.js
  • Firebase Auth — Servicio de auth de Google
  • Supabase Auth — Auth open-source con seguridad a nivel de fila
  • Clerk — Componentes de auth listos para usar
  • Auth0 — Auth empresarial con personalización extensa