Javascript
Front End
context.api
React.js
La autenticación es un aspecto fundamental de las aplicaciones web modernas, permitiendo a los usuarios acceder de forma segura a contenido y funcionalidades personalizadas. La API Context de React proporciona una solución elegante para gestionar el estado de autenticación en toda tu aplicación sin necesidad de prop drilling.
Vamos a explorar cómo implementar un contexto de autenticación que servirá como base para proteger rutas y gestionar sesiones de usuario.
El patrón de Contexto de Autenticación centraliza la lógica y el estado de autenticación del usuario en un solo lugar, haciéndolo accesible para cualquier componente en el árbol de tu aplicación. Este enfoque ofrece varios beneficios:
La API Context resuelve esto proporcionando una forma de compartir valores como el estado de autenticación a través del árbol de componentes sin pasar explícitamente props a través de cada nivel. Esto es particularmente útil para la autenticación porque:
Aquí tienes una implementación básica de un Contexto de Autenticación en React:
1import React, { createContext, useContext, useState, useEffect } from "react"; 2 3// Crear el contexto de autenticación 4const AuthContext = createContext(); 5 6// Hook personalizado para usar el contexto de autenticación 7export const useAuth = () => { 8 return useContext(AuthContext); 9}; 10 11// Componente Provider que envuelve tu aplicación y hace que el objeto auth esté disponible 12export const AuthProvider = ({ children }) => { 13 const [currentUser, setCurrentUser] = useState(null); 14 const [loading, setLoading] = useState(true); 15 16 // Función de inicio de sesión 17 const login = async (email, password) => { 18 try { 19 // Llamada a API simulada - reemplazar con tu servicio de autenticación real 20 const response = await mockAuthService.login(email, password); 21 setCurrentUser(response.user); 22 localStorage.setItem("authToken", response.token); 23 return response.user; 24 } catch (error) { 25 throw new Error(error.message); 26 } 27 }; 28 29 // Función de cierre de sesión 30 const logout = () => { 31 setCurrentUser(null); 32 localStorage.removeItem("authToken"); 33 }; 34 35 // Comprobar si el usuario está autenticado 36 const isAuthenticated = () => { 37 return !!currentUser; 38 }; 39 40 // Cargar usuario desde token en el renderizado inicial 41 useEffect(() => { 42 const checkAuthStatus = async () => { 43 try { 44 const token = localStorage.getItem("authToken"); 45 if (token) { 46 // Llamada a API simulada para verificar token - reemplazar con tu servicio de autenticación 47 const user = await mockAuthService.verifyToken(token); 48 setCurrentUser(user); 49 } 50 } catch (error) { 51 // El token es inválido 52 localStorage.removeItem("authToken"); 53 } finally { 54 setLoading(false); 55 } 56 }; 57 58 checkAuthStatus(); 59 }, []); 60 61 // Objeto de valor del contexto que será compartido 62 const value = { 63 currentUser, 64 login, 65 logout, 66 isAuthenticated, 67 loading 68 }; 69 70 return ( 71 <AuthContext.Provider value={value}> 72 {!loading && children} 73 </AuthContext.Provider> 74 ); 75};
Vamos a desglosar las partes clave de esta implementación:
Creación del Contexto: Creamos un nuevo contexto usando createContext()
. Este contexto contendrá nuestro estado de autenticación y métodos.
Hook Personalizado: El hook useAuth
proporciona una forma fácil de acceder al contexto de autenticación desde cualquier componente. Utiliza internamente el hook useContext
.
Componente Provider: El componente AuthProvider
es donde gestionamos el estado de autenticación y lo proporcionamos al resto de la aplicación. Incluye:
Métodos de Autenticación:
login
: Maneja la autenticación del usuario, almacena el token y actualiza el estado del usuariologout
: Limpia el estado del usuario y elimina el token almacenadoisAuthenticated
: Verifica si un usuario está actualmente autenticadoPersistencia de Token: El hook useEffect
verifica la existencia de un token al montar el componente e intenta restaurar la sesión del usuario si existe un token válido.
La estructura anterior introduce:
AuthContext
para almacenar el estado de autenticaciónuseAuth
para acceder fácilmente al contextoAuthProvider
que gestiona el estado de autenticación y proporciona funcionesPara usar el contexto de autenticación, envuelve tu aplicación con el componente AuthProvider
:
1import React from "react"; 2import ReactDOM from "react-dom"; 3import App from "./App"; 4import { AuthProvider } from "./context/AuthContext"; 5 6ReactDOM.render( 7 <React.StrictMode> 8 <AuthProvider> 9 <App /> 10 </AuthProvider> 11 </React.StrictMode>, 12 document.getElementById("root") 13);
Esta configuración asegura que el contexto de autenticación esté disponible en toda tu aplicación. El componente AuthProvider
debe colocarse alto en tu árbol de componentes, típicamente a nivel raíz, para que todos los componentes puedan acceder al estado de autenticación.
Ahora, cualquier componente en tu aplicación puede acceder al estado de autenticación y funciones usando el hook useAuth
:
1import React, { useState } from "react"; 2import { useAuth } from "../context/AuthContext"; 3 4function LoginForm() { 5 const [email, setEmail] = useState(""); 6 const [password, setPassword] = useState(""); 7 const [error, setError] = useState(""); 8 const { login } = useAuth(); 9 10 const handleSubmit = async (e) => { 11 e.preventDefault(); 12 setError(""); 13 14 try { 15 await login(email, password); 16 // Redireccionar o mostrar mensaje de éxito 17 } catch (error) { 18 setError("Error al iniciar sesión. Por favor, verifica tus credenciales."); 19 } 20 }; 21 22 return ( 23 <form onSubmit={handleSubmit}> 24 <div> 25 <label htmlFor="email">Email</label> 26 <input 27 id="email" 28 type="email" 29 value={email} 30 onChange={(e) => setEmail(e.target.value)} 31 required 32 /> 33 </div> 34 <div> 35 <label htmlFor="password">Contraseña</label> 36 <input 37 id="password" 38 type="password" 39 value={password} 40 onChange={(e) => setPassword(e.target.value)} 41 required 42 /> 43 </div> 44 {error && <div className="error">{error}</div>} 45 <button type="submit">Iniciar Sesión</button> 46 </form> 47 ); 48}
Este componente de formulario de inicio de sesión demuestra cómo usar el contexto de autenticación en un escenario práctico. El hook useAuth
nos da acceso a la función login
, que podemos llamar cuando se envía el formulario. El componente también maneja estados de error y proporciona retroalimentación al usuario.
Con el contexto de autenticación en su lugar, crear un componente de perfil de usuario se vuelve sencillo:
1import React from "react"; 2import { useAuth } from "../context/AuthContext"; 3 4function Profile() { 5 const { currentUser, logout } = useAuth(); 6 7 if (!currentUser) { 8 return <div>Por favor, inicia sesión para ver tu perfil.</div>; 9 } 10 11 return ( 12 <div className="profile-container"> 13 <h2>Perfil de Usuario</h2> 14 <div className="profile-info"> 15 <p><strong>Email:</strong> {currentUser.email}</p> 16 <p><strong>Nombre:</strong> {currentUser.name}</p> 17 <p><strong>Cuenta creada:</strong> {new Date(currentUser.createdAt).toLocaleDateString()}</p> 18 </div> 19 <button onClick={logout}>Cerrar Sesión</button> 20 </div> 21 ); 22}
El componente de perfil muestra cómo acceder y mostrar información del usuario desde el contexto de autenticación. Utiliza el estado currentUser
para mostrar los detalles del usuario y la función logout
para manejar el cierre de sesión. El componente también incluye una renderización condicional para mostrar un mensaje cuando no hay usuario conectado.
Para aplicaciones más complejas, es posible que necesites manejar roles y permisos de usuario. Mejoremos nuestro contexto de autenticación para soportar roles de usuario:
1// Dentro de AuthProvider.js 2const AuthProvider = ({ children }) => { 3 // ... código anterior 4 5 // Verificar si el usuario tiene un rol específico 6 const hasRole = (role) => { 7 if (!currentUser || !currentUser.roles) return false; 8 return currentUser.roles.includes(role); 9 }; 10 11 // Verificar si el usuario tiene un permiso 12 const hasPermission = (permission) => { 13 if (!currentUser || !currentUser.permissions) return false; 14 return currentUser.permissions.includes(permission); 15 }; 16 17 const value = { 18 currentUser, 19 login, 20 logout, 21 isAuthenticated, 22 hasRole, 23 hasPermission, 24 loading 25 }; 26 27 // ... resto del proveedor 28};
Esta mejora añade control de acceso basado en roles a nuestro contexto de autenticación. Las funciones hasRole
y hasPermission
nos permiten verificar si un usuario tiene roles o permisos específicos, lo cual es útil para implementar control de acceso en tu aplicación.
Esta mejora te permite renderizar componentes condicionalmente basados en roles y permisos de usuario:
1function AdminDashboard() { 2 const { hasRole } = useAuth(); 3 4 if (!hasRole("admin")) { 5 return <div>No tienes permiso para acceder a esta página.</div>; 6 } 7 8 return ( 9 <div className="admin-dashboard"> 10 <h1>Panel de Administración</h1> 11 {/* Contenido solo para administradores */} 12 </div> 13 ); 14}
El componente AdminDashboard
demuestra cómo usar el control de acceso basado en roles para restringir el acceso a ciertas partes de tu aplicación. Verifica si el usuario actual tiene el rol "admin" antes de renderizar el contenido del panel de administración.
Para una mejor experiencia de usuario, querrás persistir el estado de autenticación a través de las actualizaciones de página. Ya hemos implementado esto usando localStorage
en nuestro ejemplo, pero puedes mejorarlo aún más con estas consideraciones:
localStorage
cuando estén disponibles1// Verificación de token mejorada con comprobación de expiración 2const verifyToken = async (token) => { 3 // Primero verifica si el token ha expirado decodificándolo (si usas JWT) 4 const decodedToken = decodeJWT(token); // Necesitarás una función de decodificación JWT 5 const currentTime = Date.now() / 1000; 6 7 if (decodedToken.exp < currentTime) { 8 throw new Error("Token expirado"); 9 } 10 11 // Luego verifica con el servidor 12 return await authService.verifyToken(token); 13};
Esta función mejorada de verificación de token añade una comprobación de expiración para asegurar que los tokens expirados se manejen correctamente. Primero decodifica el token JWT para verificar su tiempo de expiración, luego lo verifica con el servidor si aún es válido.
Implementar un contexto de autenticación en tu aplicación React proporciona una base robusta para gestionar el estado del usuario a través de los componentes. Este patrón centraliza la lógica de autenticación, simplifica el control de acceso y crea un código más mantenible.
Beneficios clave de usar el patrón de Contexto de Autenticación:
En la próxima lección, exploraremos cómo usar este contexto de autenticación para implementar rutas privadas con React Router, asegurando que ciertas partes de tu aplicación solo sean accesibles para usuarios autenticados.