Andrés Esteban Rodríguez Jiménez
Raycris Maldonado
abigail hilario
Implementar autenticación y autorización en React Router DOM v6 te permite controlar qué rutas ve cada usuario según su estado de sesión y rol. Aquí aprendes a construir un flujo completo de fake authentication con Context API y hooks personalizados, ideal para desarrolladores que ya manejan rutas básicas y quieren proteger contenido privado.
Por qué necesitas un sistema de autenticación en React Router
No todo el contenido de una aplicación debe ser público. Hay páginas como el perfil de usuario que solo deberían mostrarse a personas autenticadas, y otras como el formulario de login que carecen de sentido cuando ya iniciaste sesión.
La autenticación verifica quién eres, mientras que la autorización define qué puedes hacer. Un editor de Platzi puede modificar la descripción de un curso; tú como estudiante no. Esa diferencia de roles se traduce en rutas visibles u ocultas dentro del menú.
¿Qué es fake authentication en React? Es una simulación del flujo de login sin backend real. Sirve para practicar la lógica de rutas privadas, redirects y manejo de sesión usando solo estado local y Context API.
Cómo crear las páginas de login y logout en React Router
Lo primero es separar las rutas en componentes independientes [02:15]. En lugar de renderizar el mismo componente en /login, /logout y /profile, creas archivos dedicados: LoginPage.js y LogoutPage.js.
La página de login contiene un formulario controlado con un input vinculado a un estado local mediante useState:
jsx const [username, setUsername] = React.useState('');
<input value={username} onChange={(event) => setUsername(event.target.value)} />
El botón de tipo submit dispara una función login en el evento onSubmit del formulario, que ejecuta event.preventDefault() para evitar la recarga de la página.
Cómo manejar el formulario de logout
La página de logout reutiliza la misma estructura, pero sin input. Solo necesitas un mensaje de confirmación tipo ¿segura de que quieres salir? y un botón que dispare la función de cierre de sesión.
Cómo construir un AuthProvider con React Context
El siguiente paso es centralizar la información del usuario para que toda la aplicación tenga acceso a ella [08:40]. Esto se logra con React Context y un archivo auth.js que exporta varios elementos:
React.createContext().value.React.useContext(AuthContext).La convención de nombrar el archivo en minúsculas indica que exporta múltiples elementos, no un solo componente.
jsx const AuthContext = React.createContext();
function AuthProvider({ children }) { const [user, setUser] = React.useState(null);
const login = ({ username }) => setUser({ username }); const logout = () => setUser(null);
const auth = { user, login, logout };
return ( <AuthContext.Provider value={auth}> {children} </AuthContext.Provider> ); }
function useAuth() { const auth = React.useContext(AuthContext); return auth; }
¿Por qué crear un hook useAuth en vez de usar useContext directamente? Para encapsular la lógica y evitar importar
useContextyAuthContexten cada componente. Solo importasuseAuthy obtienes el objeto de autenticación listo para usar.
Dónde colocar el AuthProvider respecto al HashRouter
Una decisión clave de arquitectura: el AuthProvider debe ir dentro del HashRouter, no fuera [14:20]. La razón es que el provider necesita acceder a métodos de navegación como useNavigate para hacer redirects tras un login exitoso.
La regla general es simple: si el componente A necesita usar hooks o métodos del componente B, entonces A debe estar anidado dentro de B.
Cómo conectar login y logout con redirección automática
Con el contexto en su lugar, los formularios dejan de hacer console.log y llaman a los métodos del objeto auth. En LoginPage extraes auth con useAuth() y disparas auth.login(username) al enviar el formulario.
Para mejorar la experiencia, combinas el login con useNavigate de React Router:
jsx const auth = useAuth(); const navigate = useNavigate();
const login = (event) => { event.preventDefault(); auth.login({ username }); navigate('/profile'); };
El flujo de logout funciona igual: llama a auth.logout() y redirige al home con navigate('/').
Cómo mostrar datos del usuario en la página de perfil
En ProfilePage también consumes el hook useAuth para acceder a auth.user.username y mostrar un saludo personalizado. Pero hay un problema: si entras a /profile sin haber hecho login, auth.user es null y la app crashea al intentar leer username de un valor nulo.
Esto revela dos necesidades pendientes:
/login si alguien intenta acceder a una ruta privada sin autenticarse.Esa lógica de rutas privadas y públicas condicionadas es el siguiente paso natural del flujo y abre la puerta a implementar autorización por roles.
¿Cómo organizarías tú los enlaces del menú según el estado de autenticación? Cuéntame en los comentarios.
Andrés Esteban Rodríguez Jiménez
Raycris Maldonado
abigail hilario
Luis Angel Olivárez Estrada
WALT 🤖
Sebastián Andrés Sanhueza Tapia
Josue Tapia Linarez
Donovan RM
Alexander Flores Rayme
JOSE RANGEL
Jhon Jairo Gomez Calderon
Santiago Andres Alvarez Cuadros
Max Andy Diaz Neyra
Josue Tapia Linarez
Arnold Laínez
Raycris Maldonado
Simón Alejandro Azócar C
Juan Castro
ProfesorJose Ever Muñoz Muñoz
Jose Tellez
Oscar Enrique Machado Quintana
Juan Castro
ProfesorJavier Damiani
Harold Zurita Simon
Charles Castillo Rosas
Juan Diego Mejia Maestre
Nicolas Forero Quevedo
Raycris Maldonado
Sebastian Carballo
useAuth: login y logout
Ahora vamos a aprender como desplegar contenido en base a la Autenticación de usuarios, ósea que no todo el contenido de una aplicación va a ser público pata todos los usuarios, sino que tengamos algunas rutas privadas para personas que no se hayan autenticado, ósea hecho login y así mismo no mostrar contenido que es innecesario para personas que ya están registradas, como el mismo registro de usuario o contenido introductorio.
Vamos a hacer contenido que dependiendo el usuario se muestre o no se muestre, y también permitir tener privilegios o permisos dependiendo del rol que tengas.
Vamos entonces a crear nuevas rutas que nos permitan hacer todo esto:
import { LoginPage } from './Components/Routes/LoginPage/LoginPage'; import { LogoutPage } from './Components/Routes/LogoutPage/LogoutPage'; function App() { return ( <> <HashRouter> <Routes> ... <Route path='/login' element={<LoginPage />} /> <Route path='/logout' element={<LogoutPage />} /> <Route path='/profile' element={<ProfilePage />} /> </Routes> </HashRouter> </> ) }
Y debemos crear nuestros respectivos componentes:
LoginPage.js
import React from 'react'; function LoginPage() { /* Para manejar el registro de usuario con el formilario haremos uso del estado de React */ const [username, setUsername] = React.useState(''); /* Esta es la función que se ejecutará cuando ocurra el evento de Submit */ const login = (e) => { e.preventDefault(); } // Aquí creamos un formulario para poder auteticarnos return ( <> <h2>Login</h2> <form onSubmit={login}> <label>Escribe tu nombre de usuario</label> <input value={username} onChange={ e => setUsername(e.target.value)} /> <button type="submit">Entrar</button> </form> </> ); } export { LoginPage }
LogoutPage.js
import React from 'react'; function LogoutPage() { const logout = (e) => { e.preventDefault(); } return ( <> <h2>Logout</h2> <form onSubmit={logout}> <label>¿Segurx de que quieres salir?</label> <button type="submit">Salir</button> </form> </> ); } export { LogoutPage }
En nuestro Menu.js vamos a crear dos nuevas rutas en nuestro Array routes:
... const routes = []; ... routes.push({ to: '/login', text: 'Logout' }); routes.push({ to: '/logout', text: 'Login' });
Si lo pensamos bien, no nos debería salir el componente Profile y Logout sin antes haber hecho un Login, vamos a trabajar entonces en esa lógica creando un archivo de autenticación:
auth.js
import React from 'react'; const AuthContext = React.createContext(); function AuthProvider({ children }) { const auth = { user, login, logout, } return ( <AuthContext.Provider value={auth} > {children} </AuthContext.Provider> ) } function useAuth() { const auth = React.useContext(AuthContext); return auth; } export { AuthProvider, useAuth, }
Vamos a importar esta lógica al App.js:
... import { AuthProvider, useAuth } from './Components/auth/auth' function App() { return ( <> {/* En este caso AuthProvider tiene que ir dentro de HastRouter en caso de que AuthProvider necesite de algún método o contenido que pueda proveer HashRouter */} <HashRouter> <AuthProvider> <Menu /> <Routes> <Route path='/' element={<HomePage />} /> <Route path='/blog' element={<BlogPage />}> <Route path=':slug' element={<BlogPost />} /> </Route> <Route path='/login' element={<LoginPage />} /> <Route path='/logout' element={<LogoutPage />} /> <Route path='/profile' element={<ProfilePage />} /> <Route path='/*' element={<p>Not Found</p>} /> </Routes> </AuthProvider> </HashRouter> </> ) } ...
En este punto, en LoginPage.js ya podemos empezar a utilizar nuestro useAuth:
import { useAuth } from '../../auth/auth'; ... function LoginPage() { const auth = useAuth(); ... }
Vamos de nuevo auth.js y seguimos con la lógica:
... function AuthProvider({ children }) { /* Siguiendo la lógica, si user es null significa que no estamos autenticados */ const [user, setUser] = React.useState(null); // Ahora necesitamos darle un valor a nuestro usuario const login = ({ username }) => { setUser({ username }); } // De la misma forma, debemos poder cerrar la sesión const logout = () => { setUser(null); } ... }
Ahora en LoginPage.js nuestro useAuth nos debe dar acceso al usuario y sus métodos:
function LoginPage() { const login = (e) => { e.preventDefault(); auth.login({ username }) } }
Vamos de nuevo a auth.js y vamos a complementar la lógica:
function AuthProvider({ children }) { const navigate = useNavigate(); const login = ({ username }) => { setUser({ username }); /* Ahora cada vez que hagamos login nos redireccionará a la página de profile */ navigate('/profile'); } const logout = () => { setUser(null); /* Aquí haremos redirect a la página principal */ navigate('/'); }
Pero para esto debemos añadir el contexto de autentificación a LogoutPage.js:
function LogoutPage() { const auth = useAuth(); const logout = (e) => { e.preventDefault(); auth.logout(); } }
Ahora en nuestra ProfilePage.js debemos completar esta lógica:
import { useAuth } from '../../auth/auth'; function ProfilePage() { const auth = useAuth(); return ( /* Ahora si nosotros nos registramos deberíamos poder ver nuestro nombre de usuario en nuestra página de perfil */ <> <h2>Perfil</h2> <h2>Welcome {auth.user.username}</h2> </> ); }
Y listo, ahora solo nos queda el que no podamos entrar a profile sino hasta que hayamos hecho login, lo cual haremos a continuación.
Excelente resumen amigo. Así se puede entender mejor despues de ver la clase
gracias es realmente de mucha ayuda <3
Por si alguien tien este error: usando Vite
[vite] Internal server error: Failed to parse source for import analysis because the content contains invalid JS syntax. If you are using JSX, make sure to name the file with the .jsx or .tsx extension. Plugin: vite:import-analysis File: /home/naiper/cursos/33-react-router/src/helpers/auth.js 33 | logout 34 | }; 35 | return <AuthContext.Provider value={auth}>{children}</AuthContext.Provider>; | ^ 36 | } 37 | at formatError (file:///home/naiper/cursos/33-react-router/node_modules/vite/dist/node/chunks/dep-db16f19c.js:40862:46) at TransformContext.error (file:///home/naiper/cursos/33-react-router/node_modules/vite/dist/node/chunks/dep-db16f19c.js:40858:19) at TransformContext.transform (file:///home/naiper/cursos/33-react-router/node_modules/vite/dist/node/chunks/dep-db16f19c.js:37530:22) at async Object.transform (file:///home/naiper/cursos/33-react-router/node_modules/vite/dist/node/chunks/dep-db16f19c.js:41111:30) at async loadAndTransform (file:///home/naiper/cursos/33-react-router/node_modules/vite/dist/node/chunks/dep-db16f19c.js:37373:29)
Lo que hice fue cambiar el archivo auth.js por un auth.jsx
Otra solución que encontré fue cambiar la configuración del vite.config.js
Agregué una regla: "Enable hmr overlay with vite react and ts"
Pero lamentablemente no me sirvió, sólamente me quitó el error en Vite pero me dejó en consola.
Es porque en vite es requerido usar JSX
me funcionó tu solución y además al momento de importar auth.jsx en app.jsx y en LoginPage.jsx hay que importarlo con con la extensión .jsx Así
import { useAuth } from "./auth.jsx";
Decidí organizar los archivos de esta manera.
Me too!, no podia soportar tando desorden, Desus Meus!
Mi Profe Facherito
Gracias Juan. De hecho en primera instancia creí que era mejor que el AuthProvider fuera el componente de más arriba. Pero luego de tu explicación, comprendo qué lógica debo tener a la hora de definir cómo organizar estos Providers.
Cuando relacione el label con el input utilizando for, react me sugirió cambiarlo por htmlFor.
<label htmlFor='input-login'>Escribe tu nombre de usuario:</label> <input value={username} onChange={e => setUsername(e.target.value)} id='input-login'/>
Por si alguien también está haciendo el proyecto con TypeScript, comparto cómo me quedó auth.tsx:
import React, { useContext, useState } from 'react' import type { PropsWithChildren } from 'react' import { useNavigate } from 'react-router-dom' interface Auth { user: { username: string } | null login: (username: string) => void logout: () => void } const AuthContext = React.createContext({}) function AuthProvider({ children }: PropsWithChildren) { const navigate = useNavigate() const [user, setUser] = useState<Auth['user']>(null) function login(username: string) { if (username) setUser({ username }) navigate('/profile') } function logout() { setUser(null) navigate('/') } const auth: Auth = { user, login, logout } return ( <AuthContext.Provider value={auth}> { children } </AuthContext.Provider> ) } function useAuth() { const authContext = useContext(AuthContext) as Auth return authContext } export { AuthProvider, useAuth }
Código de TypeScript de la clase: AuthStore.tsx
import { createContext, FC, useState, PropsWithChildren } from 'react' import { useNavigate } from 'react-router-dom' interface User { username: string } interface Auth { user: User | null login: (user: User) => void logout: () => void } // interface Auth { // user: { username: string } | null // login: (user: Auth['user']) => void // logout: () => void // } const AuthContext = createContext({} as Auth); const AuthProvider:FC<PropsWithChildren> = ({ children }) => { const navigate = useNavigate(); const [user, setUser] = useState<Auth['user']>(null); const login:Auth['login'] = (user) => { setUser(user); navigate('/profile') } const logout:Auth['logout'] = () => { setUser(null); navigate('/'); } const auth:Auth = { user, login, logout } return ( <AuthContext.Provider value={auth}> { children } </AuthContext.Provider> ) } export { AuthProvider, AuthContext }
useAuth.ts
import { useContext } from "react" import { AuthContext } from "./AuthStore" const useAuth = () => { const auth = useContext(AuthContext); return auth; } export { useAuth }
ProfilePage
import { useAuth } from "./useAuth" const ProfilePage = () => { const { user } = useAuth(); return( <> <h2>Perfil</h2> <p>Welcome, { user?.username} </p> </> ) } export { ProfilePage }
Podrían verificar que que exista un usuario con:
<h2>{auth.user?.username}</h2>
el operador '?.' verifica que dentro de objeto auth.user exista username, si no existe devolverá undefined y el h1 en la pagina estará vació. . o podrían hacer una condicional para mostrar un username por default:
const username = auth.user?.username ?? 'User936' return ( <h2>{username}</h2> )
El operador de coalescencia nula ?? solo devuelve el valor del lado derecho ('User936') si el valor del lado izquierdo es null o undefined. Y si no existe un 'username' en 'auth.user?.username' devolverá undefined
Esta genial el curso Juan pero más de 20 min de fake auth no me sirve. :(
Esta clase estuvo bien intensa, tuve que mirarla 3 veces para poder entender bien.
Por qué se pasa por parámetro ({ username }) y no simplemente (username) sin los curly brackets?
Lo he puesto así en todas las llamadas al auth.login() y en las partes donde hago uso del username y funciona perfecto
Solo por gusto personal. Me gusta mucho la sintaxis de los objetos y RORO, sobre todo cuando son muuuuchas propiedades. En este caso que solo es una da lo mismo. :D
interesante dato, no lo habia notado
Basicamente lo que hace el metodo "login" es setear el estado de user name con el nombre de usuario que le pasamos, que viene del input El metodo "logout" lo que hace es setear ese estado como null Y todo esta guardado en el contexto al cual tenemos acceso con el hook de useAuth Así lo compartimos con todos los componentes
Como es el atajo para las importaciones?
Control + Espacio
AuthConsumer pensé en OutConsumer uwu, si alguien jugaba NBA en PlayStation allá por el año 2012 de repente conoce a ese Youtuber jajaja
useAuth: login y logout
.
Empezamos a desarrollar un flujo de navegación completo para aplicaciones que tengan autenticación y autorización. Esto significa que no todos los usuarios podrán tener acceso a rutas que deberían de ser privadas u ocultas si no se han autenticado. Incluso si ya hicieron login hay cosas que no queremos que vean, contenido que no cualquier usuario debe poder ver por cuestiones de autorización.
.
Lo primero que haremos en App es crear 2 nuevas rutas /login y logout con sus respectivos componentes a renderizar.
.
<Route path="/login" element={<LoginPage />} /> <Route path="/logout" element={<LogoutPage />} />
.
En Menu también añadimos estas 2 nuevas rutas al array de rutas.
.
const routes = []; routes.push({ to: '/', text: 'Home', }); routes.push({ to: '/blog', text: 'Blog', }); routes.push({ to: '/profile', text: 'Profile', }); routes.push({ to: '/login', text: 'Login', }); routes.push({ to: '/logout', text: 'Logout', });
.
Vamos a necesitar un archivo auth.js el cual nos va permitir simular nuestro flujo de autenticación. Su objetivo principal es exportar un objeto que contenga nuestro AuthProvider y nuestro react hook useAuth.
.
En este archivo creamos un contexto para la autenticación. Dentro del provider de este contexto por medio de la propiedad value le pasamos un objeto auth que va a contener el estado de nuestro usuario user que por defecto es null cuando no está logeado; también contiene a las funciones de login y logout que lo que hacen es setear el estado del usuario y navigar como corresponda mediante useNavigate.
.
import React, { useContext } from 'react'; import { useNavigate } from 'react-router-dom'; const AuthContext = React.createContext(); function AuthProvider({ children }) { const navigate = useNavigate(); const [user, setUser] = React.useState(null); const login = ({ username }) => { setUser({ username }); navigate('/profile'); }; const logout = () => { setUser(null); navigate('/'); }; const auth = { user, login, logout }; return ( <AuthContext.Provider value={auth}> {children} </AuthContext.Provider> ); } function useAuth() { const auth = React.useContext(AuthContext); return auth; } export { AuthProvider, useAuth, };
.
El react hook useAuth lo que hace es simplemente llamar al contexto, guardarlo y exportarlo en una variable.
.
Finalmente, el AuthProvider es un componente que nos permitirá encapsular el contenido de nuestro HashRouter en App. Por lo que tendrá acceso a la información que puede proporcionar HashRouter, pero a la vez los componentes contenidos por AuthProvider como Menu o las rutas, tendrán acceso a las propiedades del objeto auth que proporciona el provider.
.
function App() { return ( <> <HashRouter> <AuthProvider> <Menu /> <Routes> <Route path="/" element={<HomePage />} /> <Route path="/blog" element={<BlogPage />}> <Route path=":slug" element={<BlogPost />} /> </Route> <Route path="/login" element={<LoginPage />} /> <Route path="/logout" element={<LogoutPage />} /> <Route path="/profile" element={<ProfilePage />} /> <Route path="*" element={<p>Not found</p>} /> </Routes> </AuthProvider> </HashRouter> </> ); }
.
Posteriormente, creamos o modificamos los componentes necesarios para este flujo, empezando por LoginPage, LogoutPage y Profile.
.
En LoginPage lo que podemos observar es que tenemos un pequeño formulario que nos permite ingresar el username, y mediante el react hook useAuth al realizar el evento de submit utilizaremos la función login que a su vez llama a auth.login para setear el valor del nombre de usuario y redirigirnos a la página de Profile de este.
.
import React from 'react'; import { useAuth } from './auth'; function LoginPage() { const auth = useAuth(); const [username, setUsername] = React.useState(''); const login = (e) => { e.preventDefault(); auth.login({ username }); }; return ( <> <h2>Login</h2> <form onSubmit={login}> <label>Escribe tu nombre de usuario:</label> <input value={username} onChange={e => setUsername(e.target.value)} /> <button type="submit">Entrar</button> </form> </> ); } export { LoginPage };
.
En LogoutPage también se tiene un pequeño formulario que al realizar el evento de submit en este caso va a setear el estado del usuario en null y redirigirnos a la página principal / por medio del react hook useAuth y la función logout que a su vez utiliza a auth.logout.
.
import React from 'react'; import { useAuth } from './auth'; function LogoutPage() { const auth = useAuth(); const logout = (e) => { e.preventDefault(); auth.logout(); }; return ( <> <h2>Logout</h2> <form onSubmit={logout}> <label>¿Segura de que quieras salir?</label> <button type="submit">Salir</button> </form> </> ); } export { LogoutPage };
.
Finalmente para completar esta parte del flujo tenemos a ProfilePage que va renderizar la página de perfil de usuario. Este utiliza al rect hook useAuth para recuperar la información del nombre de usuario y poder renderizarlo.
.
Sin embargo dará error si no se ha pasado a través de las etapas del flujo adecuadamente. Por lo que aún se debe implementar una forma de proteger las rutas para evitar este tipo de error, o condicionar el renderizado de forma que si el usuario es null entonces manejar el renderizado de otra manera.
.
import React from 'react'; import { useAuth } from './auth'; function ProfilePage() { const auth = useAuth(); return ( <> <h2>Perfil</h2> <p>Welcome, {auth.user.username}</p> </> ); } export { ProfilePage };
useAuth: login y logout
Definiendo rutas de Login y Logout.
<Route path="/login" element={<LoginPage />} /> <Route path="/logout" element={<LogoutPage />} />
Creando componentes LoginPage y LogoutPage.
import React from "react"; function LoginPage() { return <h2>Login</h2>; } export { LoginPage };
import React from "react"; function LogoutPage() { return <h2>Logout</h2>; } export { LogoutPage };
Agregar Links para login y logout al menú.
import React from "react"; import { NavLink } from "react-router-dom"; function Menu() { return ( <nav> <ul> {routes.map(route => ( <li key={route.to}> <NavLink style={({ isActive }) => ({ color: isActive ? 'red' : 'blue' })} to={route.to} > {route.text} </NavLink> </li> ))} </ul> </nav> ) } const routes = [ { to: '/', text: 'Home' }, { to: '/blog', text: 'Blog' }, { to: '/profile', text: 'Profile' }, **{ to: '/login', text: 'Login' }, { to: '/logout', text: 'Logout' }** ]; export { Menu };
Creando un formulario para el login.
import React from "react"; function LoginPage() { **const [username, setUsername] = React.useState(''); const login = (e) => { e.preventDefault(); console.log(username); }** return ( <> **<h2>Login</h2> <form onSubmit={login}> <label>Escribe tu nombre de usuario:</label> <input value={username} onChange={e => setUsername(e.target.value)} /> <button type="submit">Entrar</button> </form>** </> ); } export { LoginPage };
Creando un formulario para logout.
import React from "react"; function LogoutPage() { **const logout = (e) => { e.preventDefault(); console.log('Logout'); }** return ( **<> <h2>Logout</h2> <form onSubmit={logout}> <label>¿Seguro de que quieres salir?</label> <button type="submit">Salir</button> </form> </>** ); } export { LogoutPage };
Creamos un provider y un hook para la autentificación.
**import React from "react"; const AuthContext = React.createContext() function AuthPrvider({ children }) { const auth = {} return ( <AuthContext.Provider value={auth}> { children } </AuthContext.Provider> ) } function useAuth() { const auth = React.useContext(AuthContext) return auth; } export { AuthProvider, useAuth }**
Implementar el provider en la aplicación.
import "./App.css"; // more imports... **import { AuthProvider } from "./api/auth";** function App() { return ( <> <HashRouter> **<AuthProvider>** <Menu /> <Routes> <Route path="/" element={<HomePage />} /> {/* More routes... */} </Routes> **</AuthProvider>** </HashRouter> </> ); } export default App;
.... const AuthContext = React.createContext() function AuthProvider({ children }) { **const navigate = useNavigate() const [user, setUser] = useState() const login = ({ username }) => { setUser({ username }) navigate('/profile') } const logout = () => { setUser(null); navigate('/') } const auth = { user, login, logout };** return ( <AuthContext.Provider value={auth}> { children } </AuthContext.Provider> ) } ...
import React from "react" import { useAuth } from "../api/auth" function LoginPage() { **const auth = useAuth()** const [username, setUsername] = React.useState(''); const login = (e) => { e.preventDefault() **auth.login({ username })** } return ( <> <h2>Login</h2> <form onSubmit={login}> <label>Escribe tu nombre de usuario:</label> <input value={username} onChange={e => setUsername(e.target.value)} /> <button type="submit">Entrar</button> </form> </> ); } export { LoginPage };
import React from "react"; import { useAuth } from "../api/auth"; function LogoutPage() { **const auth = useAuth()** const logout = (e) => { e.preventDefault(); **auth.logout()** } return ( <> <h2>Logout</h2> <form onSubmit={logout}> <label>¿Seguro de que quieres salir?</label> <button type="submit">Salir</button> </form> </> ); } export { LogoutPage };
Modificamos ProfilePage para mostrar el nombre del usuario logeado.
import React from "react"; import { useAuth } from "../api/auth"; function ProfilePage() { **const auth = useAuth()** return ( **<> <h2>Perfil</h2> <p>Welcome, {auth.user.username}</p> </>** ); } export { ProfilePage };
🔐 Autenticación y Autorización en React Router DOM
📌 Conceptos Fundamentales
🔑 Autenticación vs Autorización
ConceptoDefiniciónEjemplo
🔐 Autenticación
Verificar quién es el usuario
Login con email/contraseña
🛡️ Autorización
Verificar qué puede hacer el usuario
Admin vs Usuario normal
🏗️ Arquitectura del Sistema
┌─────────────────┐ │ AuthContext │ ← Estado global de autenticación └────────┬────────┘ │ ┌────┴────┐ │ App │ └────┬────┘ │ ┌────┴────────────────┐ │ │ ┌───┴────┐ ┌────┴─────┐ │ Rutas │ │ Rutas │ │Públicas│ │ Privadas │ └────────┘ └──────────┘
🎯 Implementación Paso a Paso
1️⃣ Crear el Contexto de Autenticación
javascript
import React, { createContext, useState, useContext } from 'react'; const AuthContext = createContext(); export const AuthProvider = ({ children }) => { const [user, setUser] = useState(null); const login = (username) => { setUser({ username }); }; const logout = () => { setUser(null); }; return ( <AuthContext.Provider value={{ user, login, logout }}> {children} </AuthContext.Provider> ); }; // Hook personalizado para acceder al contexto export const useAuth = () => useContext(AuthContext);
📝 Datos clave:
createContext para estado globaluseState(null) → usuario no autenticadouseAuth() para acceso fácil2️⃣ Componente de Login
javascript
import React, { useState } from 'react'; import { useNavigate } from 'react-router-dom'; import { useAuth } from './auth'; function LoginPage() { const [username, setUsername] = useState(''); const auth = useAuth(); const navigate = useNavigate(); const handleLogin = (event) => { event.preventDefault(); if (username.trim()) { auth.login(username); navigate('/profile'); // Redirige después del login } }; return ( <div> <h2>🔐 Iniciar Sesión</h2> <form onSubmit={handleLogin}> <label>Usuario:</label> <input type="text" value={username} onChange={(e) => setUsername(e.target.value)} placeholder="Escribe tu nombre" /> <button type="submit">Entrar</button> </form> </div> ); }
3️⃣ Componente de Ruta Protegida
javascript
import { Navigate } from 'react-router-dom'; import { useAuth } from './auth'; function ProtectedRoute({ children }) { const auth = useAuth(); // Si no hay usuario, redirige al login if (!auth.user) { return <Navigate to="/login" replace />; } // Si está autenticado, muestra el contenido return children; } export default ProtectedRoute;
🎯 Uso:
javascript
<Route path="/profile" element={ <ProtectedRoute> <ProfilePage /> </ProtectedRoute> } />
4️⃣ Estructura de Rutas Completa
javascript
import { AuthProvider } from './auth'; import { HashRouter, Routes, Route } from 'react-router-dom'; function App() { return ( <AuthProvider> <HashRouter> <Navigation /> <Routes> {/* Rutas públicas */} <Route path="/" element={<HomePage />} /> <Route path="/login" element={<LoginPage />} /> {/* Rutas protegidas */} <Route path="/profile" element={ <ProtectedRoute> <ProfilePage /> </ProtectedRoute> } /> <Route path="/dashboard" element={ <ProtectedRoute> <DashboardPage /> </ProtectedRoute> } /> </Routes> </HashRouter> </AuthProvider> ); }
5️⃣ Navegación Dinámica
javascript
function Navigation() { const auth = useAuth(); return ( <nav> <Link to="/">🏠 Inicio</Link> {/* Mostrar según estado de autenticación */} {!auth.user ? ( <Link to="/login">🔐 Login</Link> ) : ( <> <Link to="/profile">👤 Perfil</Link> <Link to="/dashboard">📊 Dashboard</Link> <button onClick={auth.logout}>🚪 Logout</button> </> )} </nav> ); }
🎮 Ejemplo Interactivo Completo
Flujo de Usuario
1️⃣ Usuario visita /profile sin login ↓ 2️⃣ ProtectedRoute detecta: auth.user === null ↓ 3️⃣ Redirige automáticamente a /login ↓ 4️⃣ Usuario ingresa credenciales ↓ 5️⃣ auth.login(username) actualiza el contexto ↓ 6️⃣ navigate('/profile') redirige al perfil ↓ 7️⃣ ProtectedRoute detecta: auth.user !== null ↓ 8️⃣ ✅ Muestra el contenido protegido
📊 Casos de Uso Avanzados
🎭 Autorización por Roles
javascript
const AuthContext = createContext(); export const AuthProvider = ({ children }) => { const [user, setUser] = useState(null); const login = (username, role) => { setUser({ username, role }); // Agrega el rol }; return ( <AuthContext.Provider value={{ user, login, logout }}> {children} </AuthContext.Provider> ); }; // Componente para rutas de admin function AdminRoute({ children }) { const auth = useAuth(); if (!auth.user) { return <Navigate to="/login" />; } if (auth.user.role !== 'admin') { return <Navigate to="/unauthorized" />; // Página de acceso denegado } return children; }
💾 Persistencia con LocalStorage
javascript
export const AuthProvider = ({ children }) => { const [user, setUser] = useState(() => { // Recuperar usuario al iniciar const saved = localStorage.getItem('user'); return saved ? JSON.parse(saved) : null; }); const login = (username) => { const userData = { username }; setUser(userData); localStorage.setItem('user', JSON.stringify(userData)); }; const logout = () => { setUser(null); localStorage.removeItem('user'); }; return ( <AuthContext.Provider value={{ user, login, logout }}> {children} </AuthContext.Provider> ); };
✨ Mejores Prácticas
✅ Hacer❌ Evitar
Usar Context API para estado global
Pasar props manualmente por todos los componentes
Validar autenticación en cada ruta protegida
Confiar solo en ocultar enlaces del menú
Persistir sesión en localStorage/cookies
Perder sesión al recargar la página
Redirigir con <Navigate replace />
Usar window.location.href
Crear componentes reutilizables (ProtectedRoute)
Duplicar lógica de protección en cada página
🔑 Datos Técnicos Clave
createContext() + useContext()<Navigate> en lugar de <Redirect>useState(null) para usuario no autenticadoprofe ayuda, tengo una pregunta, porque cuando estoy haciendo el login, sucede todo normal, pero puse que me imprimiera en la consola el user que acabo de cambiar, pero, pongo mi nombre en el input, luego le doy a entrar y aparece en la pantalla welcome y mi nombre, pero en la consola que puse que me imprimiera el user, me sale null, o el valor anterior de user, aqui te muestro
asi queda mi pagina y la consola despues de hacer el login, si funciona cuando le pasa el nombre al parrafo al html, pero a la consola pasa el anterior valor, ayudame a entender porque porfavor, y lo otro es que estoy usando react router v6.8, y la verdad cambio bastante, por eso estoy usando unas cuantas cosas distintas
Hola Nicolas, segun pude ver, estas imprimiendo en consola el user que si te fijas lo tienes declarado en el useState como null al principio cuando esta clase se llama. En ves de imprimir en consola el user, deberías de imprimir el userName. ahí si te funcionará correctamente.
English version branch and commit
Branch name:
feat/auth-context-setup
Commit message:
git commit -m "feat: implement initial authentication context
- Add AuthContext and AuthProvider components
- Set up basic authentication state management
- Implement useAuth custom hook for auth state access
- Add initial login/logout functionality structure"