Carlos Eduardo Gomez García
Gaston Blanco
Juan Diego Bermudez
La autorización en React te permite decidir qué puede hacer cada usuario dentro de tu aplicación según su rol o relación con el contenido. Aquí aprendes a diferenciarla de la autenticación, asignar permisos de administrador y permitir que cada autor controle su propio contenido.
Cuál es la diferencia entre autenticación y autorización
Aunque suenan parecidas, no son lo mismo y conviene tenerlo claro antes de tocar código.
La autenticación identifica personas: responde a la pregunta de quién es quién dentro de tu app. La autorización, en cambio, define qué permisos tiene cada persona ya autenticada [01:00]. Las dos van de la mano, pero la magia ocurre cuando combinas ambas para modificar comportamientos según el usuario.
¿Qué es autorización en una app? Es el sistema que define qué acciones puede ejecutar un usuario autenticado, por ejemplo, eliminar un blog post si es administrador o autor del contenido.
Cómo crear una lista de administradores en el archivo auth
La idea es simular lo que normalmente haría un backend: devolver información sobre el rol del usuario al momento de hacer login.
Dentro de auth.js, se crea una constante adminList con un arreglo de usernames que tendrán superpoderes, por ejemplo Iris y Freddier [03:00]. En un entorno real, esa lista vendría de una base de datos junto con la respuesta de autenticación, pero para fines de aprendizaje se mantiene como arreglo manual.
Después, al construir el objeto user, se evalúa si el username del usuario actual está dentro de adminList usando el método some, que recorre el arreglo y devuelve true si encuentra coincidencia. Ese resultado se guarda en una variable isAdmin y se asigna como propiedad al usuario autenticado [04:30].
javascript const adminList = ['Iris', 'Freddier']; const isAdmin = adminList.some(admin => admin === username); const user = { username, isAdmin };
Cómo mostrar botones según el rol del usuario
Con la propiedad isAdmin lista, el siguiente paso es usarla en los componentes que dependen del permiso.
En blogPost.js se importa el hook useAuth y se evalúa si existe auth.user y si su propiedad isAdmin es true. Solo en ese caso se renderiza el botón de eliminar el blog post [06:30]. Esto significa que cualquier visitante o usuario común jamás verá ese botón, mientras que Iris o Freddier sí podrán hacerlo en cualquier publicación.
javascript {auth.user?.isAdmin && ( <button>Eliminar blog post</button> )}
Al iniciar sesión con Iris y entrar al blog, el botón aparece en todos los posts. Sin sesión o con un usuario común, no se muestra nada distinto.
Cómo permitir que cada autor edite su propio contenido
Un administrador puede borrar todo, pero también el autor original debería poder controlar lo que él mismo creó.
Para resolverlo se crea una variable canDelete que combina dos condiciones con un operador OR. Por un lado, que el usuario sea administrador. Por otro, que el author del blog post coincida con el username autenticado [09:30]. Si cualquiera se cumple, el botón aparece.
javascript const canDelete = auth.user?.isAdmin || blogPost.author === auth.user?.username;
El símbolo ?. es optional chaining, una característica reciente de ECMAScript que evita errores cuando una propiedad anidada no existe. En lugar de validar manualmente que auth.user exista antes de leer username, el operador devuelve undefined sin romper la app [11:00].
¿Para qué sirve el optional chaining en JavaScript? Permite acceder a propiedades anidadas sin verificar manualmente cada nivel. Si algo es
nulloundefined, devuelveundefineden lugar de lanzar un error.
Qué pasa al probar con distintos usuarios
La prueba final confirma que la lógica funciona en todos los escenarios.
Esa combinación de rol + autoría es la base de los sistemas de permisos en aplicaciones reales, desde un blog hasta un CMS o un dashboard corporativo.
Qué reto debes resolver antes de la siguiente clase
La propuesta es ampliar la lógica con más de una lista de roles para practicar el patrón.
No te quedes solo con administradores. Crea listas adicionales como editores, content creators, beta testers o revisores, y asigna a cada una un comportamiento distinto. Por ejemplo:
No necesitas implementar la acción real, basta con que el botón o la sección aparezca según el rol [14:00]. Cuéntame en los comentarios cómo lo resolviste.
Carlos Eduardo Gomez García
Gaston Blanco
Juan Diego Bermudez
Andrés Esteban Rodríguez Jiménez
Federico Ivan Llano
Federico Ivan Llano
Eduardo Lemus Zavala
Tomas Garcia
Diego Reyes Cabrera
Raycris Maldonado
Andres Felipe Leon Gomez
Juan Castro
ProfesorEudes Serpa
Rubén Ernesto Aragón Gil
Leandro Andres Suarez Vece
Seba Cardoso
Yaneth Hernández
Juan Diego Mejia Maestre
Sebastian Carballo
Harold Zurita Simon
Jonathan Amaya Mendieta
marlon mosquera rivera
Edgardo Luis Jimenez Nuñez
Javier Silva Avendaño
JUAN JOSE HERNANDEZ MUÑOZ
Max Andy Diaz Neyra
¡Wohoo! Soy el admin 😎 jajaja. Para el caso del final yo crearía roles. Asignarle roles a cada usuario (ya no solo un campo de user.isAdmin, sino un campo user.role) y con base en ese campo validar cada acción para saber si su rol le permite o no hacer X acción :p
Pensé en una idea similar. Le agregaría el campo que dices, pero que sea un objeto u array (no sabría que conviene en este caso) y comparas según donde corresponda.
user.role = { admin: false, editor: false, teacher: false, studien: true, }
Genial! En mi caso, del lado del backend lo había definido justo así, cada usuario tiene un rol y desde el mismo backend al momento de editar o eliminar algún elemento se valida si la persona que esta realizando la petición es admin o si es el creador.
Luego, desde reac unicamente se hace la validación del rol del usuario para mostrar los botones tal y como el profe lo indica 😊👌
Roles y permisos
La autorización es la que le permite a los usuarios tener cierto tipo de roles que nos permiten saber a que permisos tenemos acceso en nuestra aplicación, actualizar información o borrarla, tener permisos solo de lectura de la información, poder subir información etc .
Podemos tener distintos tipos de roles, por mencionar algunos pueden ser el administrador, moderadores, usuarios premiun o freemiun y demás.
Para empezar a implementar esta lógica de autorización vamos a nuestro archivo auth.js:
// Listas de Autorización const adminList = [ 'RetaxMaster', 'freddier', 'juandc' ]; const AuthContext = React.createContext(); function AuthProvider({ children }) { /* Dentro de nuestra función de login veremos si el usuario a ingresar es administrador */ const login = ({ username }) => { /* Esta es la validación para saber si el usuario que ingresa a la aplicación es administrador o no */ const isAdmin = adminList.find(admin => admin === username); /* Ahora nuestros usuarios van a tener una propiedad para saber si son admis */ setUser({ username, isAdmin }); navigate('/profile'); } }
Ahora en nuestra aplicación vamos a crear los permisos que van a tener nuestros administradores.
BlogPost.js
import { useAuth } from '../../auth/auth'; function BlogPost() { ... // Vamos a hacer uso de la autenticación const auth = useAuth(); /* Aqui buscamos al momento de registrarnos si el usuario es parte de la lista de administradores */ const blogpost = blogdata.find( post => post.slug == slug ); return ( <> ... {/* Si nuestro usuario existe y es admin vamos a renderizar este botón */} {auth.user?.isAdmin && ( <button>Eliminar blogpost</button> )} </> ); }
Ahora si entramos a alguno de los BlogPost con el nombre de algún administrador vamos a poder ver renderizado nuestro botón de eliminar BloPost.
Ya tenemos la lógica para mostrar contenido dependiendo del rol.
Ahora si vemos los blogpost que tienen autores, digamos que el autor del blog desea eliminarlo, pero ¿como lo va a hacer?
Vamos a crear la lógica para que los autores del contenido puedan eliminar sus propios Blogs.
function BlogPost() { ... /* Aquí validaremos si al acceder al blog soy el autor del blog y pueda eliminarlo o tenga el rol de administrador para ello */ const canDelete = auth.user?.isAdmin || blogpost.author === auth.user?.username; const returnToBlog = () => { navigate('/blog'); } return ( <> ... {/* Ahora vamos a preguntar si puedo borrar el BlogPost*/} {canDelete && ( <button>Eliminar blogpost</button> )} </> ); }
Ahora en caso de que seamos lo autores del Blog podemos eliminarlo, super fácil.
Pense que iba a ser complicado pero me resulto sencillo, cambiaria algunos nombres de los objetos. Todavia tengo dudas de como se crean las API y los objetos JSON, si tienen una regla de planificar y organizar
const roles = { admin: { type: 'admin', read: true, write: true, delete: true }, editor: { type: 'editor', read: true, write: true, delete: true }, student: { type: 'student', write: false, read: true, delete: false }, } const users = [{ name: 'ivana', rol: roles.admin }, { name: 'fred', rol: roles.admin }, { name: 'cris', rol: roles.admin }, { name: 'rocio', rol: roles.student }, { name: 'leonel', rol: roles.editor }] const login = ({username}) => { const admin = users.filter(admin => admin.rol.type === 'admin') const isAdmin = admin.find(admin => admin.name === username) . .. . . . . } //blogspot const canDelete = auth.user?.isAdmin || blogPost.author === auth.user?.username; // const filterA = auth.user?.admin?.filter( f => f.name && f.rol.delete) // console.log(filterA.find(f => auth.user?.username === f.name )) //devuelve un objeto con nombres de admin especificos
casi 3 meses sin pc, vuelvo y me encuentro mi codigo, no lo recordaba y no tenia nada en github
Me encanto tu código, creo q pudiste ahorrarte el user filter:
const isAdmin = users.find(user => user.role === roles.admin && user.username === username);
pero ya es cuestión de gustos jejej
Reto
Mi solución al reto fue crear un arreglo de roles, con permisos anidados:
auth.js
const roles = [ { role: "admin", update: true, delete: true }, { role: "creator", update: true, delete: true }, { role: "editor", update: true, delete: false }, ];
Luego, crear una función para validad que el permiso que recibimos en la consulta existe:
function checkRoles(role) { return roles.find((item) => item.role === role); }
Luego, crear un método que modifique el objecto user que se envia al provider:
function addPermissions(user) { if (!checkRoles(user.role)) return { ...user, update: false, delete: false }; const role = roles.filter((item) => item.role === user.role); const newUser = { ...user, ...role[0] }; return newUser; }
Al crear hacer login quedaría así:
const login = ({ username }) => { let user = { username, isAdmin: false, role: "editor", }; user = addPermissions(user); setUser(user); navigate("/profile"); };
En componente de BlogPost solo debe de determinar que tipo de permisos tiene el usuario:
BlogPost.js
const auth = useAuth(); ... {auth.user?.update && <button>Actualizar blogpost</button>} {auth.user?.delete && <button>Delete blogpost</button>}
Otra forma de saber si el usuario es admin:
const adminList = ["Irisval", "RetaxMaster", "freddier"] const isAdmin = adminList.includes(username)
MDN - Array.prototype.includes
efectivamente!
Leyendo varios aportes mi solución fue de la siguiente manera:
en Auth.js crear una constante para los roles y sus permisos respectivos; y despues un array de los usuarios registrados
const roles = { admin: { write: true, read: true, delete: true, }, editor: { write: true, read: true, delete: false, }, visitor: { write: false, read: true, delete: false, }, }; const users = [ { name: "Andres", role: roles.admin, }, { name: "Felipe", role: roles.editor, }, ];
Dentro de AuthProvider asignar el usuario y su rol
function AuthProvider({ children }) { //............ const login = (username) => { //revisar si el usuario existe o lo crea como visitante const rol = users.find((usu) => usu.name === username); rol !== undefined ? setUser(rol) : setUser({ name: username, role: roles.visitor }); navigate("/profile"); }; //............ }
finalmente en BlogPost.jsx hacer las validaciones para renderizar un boton o no dependiendo de los permisos de su rol
return ( <> <h3>{blog.title}</h3> <button onClick={handleBack}>Back</button> <p>{blog.author}</p> <p>{blog.content}</p> {user?.role.write && <button>Edit Post</button>} {user?.role.delete && <button>Delete Post</button>} {user?.name === blog.author && ( <> <button>Edit Post</button> <button>Delete Post</button> </> )} </> );
Me encanta esta solución :clap:
Les dejo un blogpost muy valioso donde explican un poco el tema de las rutas protegidas de una forma escalable. Espero les sirva como a mi 😉 https://www.robinwieruch.de/react-router-private-routes
Yo era de esos men, que tuvo su propio servidor Habbo Hotel. Este curso me recuerda mucho cuando modificaba y otorgaba los permisos como dueño de ese lugar.
Recuerdo que utilizaba PHP para quitar y poner.
!Ahhh, que recuerdos.!
Inspirado en unos de los compañeros dejo una simple y humilde solucion.
En el primero array determinaremos los roles y lo que pueden hacer. En el segundo crearemos usuarios con roles.
const roles = { admin: { type: 'admin', read: true, write: true, delete: true }, editor: { type: 'editor', read: true, write: true, delete: false }, } const users = [{ name: 'ivana', rol: roles.admin }, { name: 'fred', rol: roles.admin }, { name: 'leonel', rol: roles.editor }]
En la funcion de autenticacion de Loguin agregaremos una linea extra para extraer el rol.
const login = ({ username }) => { const Isrol = users.find(user => (user.name === username) ? user.rol : null) setUser({ username, Isrol }) navigator('/profile') }
y al final, en el blogPost veremos si tienen autorizacion o no.
const authorityDelet = auth.user?.Isrol?.rol.delete const authorityEdit = auth.user?.Isrol?.rol.write return ( <> <h3> {post.title}</h3> <p>{post.content}</p> <button onClick={returnToBlog}>Volver un paso atras</button> {authorityEdit && ( <button>Editar blog</button> )} {authorityDelet && ( <button>Eliminar blog</button> )} </> ) }
Para saber si un elemento existe en una lista podemos usar some de esta manera:
const isAdmin = adminList.some(admin => admin === username)
Apoyándome en los comentarios de los compañeros y lo aprendido en el curso, cree un array, que incluye a las personas autorizadas, los roles y permisos
const userType = [ { name: ['Irisval', 'RetaxMaster', 'freddier'], role: 'admin', permissions: { create: true, read: true, update:false, delete: true, } }, { name: ['yahe', 'pedro', 'pablo'], role: 'author', permissions: { create: true, read: true, update:true, delete: true, }, }, ];
Luego lo recorro para obtener las características del usuario(esto en la función login):
let role = [] let permissions = {} let userRole = '' const isAuthorized = userType.some(user => user.name.includes(username)) userType.forEach((user) => { if (user.name.includes(username) && isAuthorized) { role.push(user.role) userRole = role.join() permissions = { ...user.permissions } } else if (!user.name.includes(username) && !isAuthorized) { role = ['visitor'] userRole = role.join() permissions = { write: false, read: true, update: false, delete: false, } } }) setUser({ username, isAuthorized, userRole, permissions }); navigate('/profile');
En el blog, de acuerdo a los permisos que tiene cada rol, cree una condición para que el administrador y el autor puedan borrar, pero solo el autor de cada blog, pueda editar
const postList = blogData.find(post => post.slug === slug) const canDelete = (auth.user?.isAuthorized && auth.user?.permissions.delete)&& (postList.author === auth.user?.username || auth.user.userRole === 'admin') const canUpdate = auth.user?.isAuthorized && auth.user?.permissions.update && postList.author === auth.user?.username
El retorno quedaría así:
return ( <> <h3>{postList.title}</h3> <button onClick={returnToBlog}>Volver al blog</button> <p>{postList.author}</p> <p>{postList.content}</p> {canDelete && ( <button>Eliminar blogpost</button> )} {canUpdate && ( <button>Editar blogpost</button> )} </> );
🎭 Autorización por Roles en React
📌 Concepto Clave
🔐 Autenticación → ¿Quién eres? (Login) 🎭 Autorización → ¿Qué puedes hacer? (Permisos)
🏗️ Implementación Básica
1️⃣ Lista de Administradores
javascript
// auth.js const adminList = ['Iris', 'Retaxmaster', 'Freddy']; export const login = (username) => { const isAdmin = adminList.includes(username); return { username, isAdmin }; };
2️⃣ Uso en Componentes
javascript
import { useAuth } from './auth'; function BlogPost({ post }) { const { user } = useAuth(); // Solo admins y autores pueden eliminar const canDelete = user.isAdmin || post.author === user.username; return ( <div> <h2>{post.title}</h2> <p>Por: {post.author}</p> {canDelete && ( <button onClick={handleDelete}> 🗑️ Eliminar </button> )} </div> ); }
🎯 Sistema Multi-Rol
Configuración de Roles
javascript
// auth.js const roles = { admins: ['Iris', 'Retaxmaster'], editors: ['Ana', 'Carlos'], betaTesters: ['User1', 'User2'] }; export const AuthProvider = ({ children }) => { const [user, setUser] = useState(null); const login = (username) => { const userData = { username, isAdmin: roles.admins.includes(username), isEditor: roles.editors.includes(username), isBetaTester: roles.betaTesters.includes(username) }; setUser(userData); }; const logout = () => setUser(null); return ( <AuthContext.Provider value={{ user, login, logout }}> {children} </AuthContext.Provider> ); };
Permisos por Rol
javascript
function BlogPost({ post }) { const { user } = useAuth(); // Permisos específicos const isAuthor = post.author === user.username; const canDelete = user.isAdmin || isAuthor; const canEdit = canDelete || user.isEditor; const canViewDraft = user.isBetaTester || user.isEditor || user.isAdmin; return ( <article> <h2>{post.title}</h2> <p>Por: {post.author}</p> {canEdit && <button>✏️ Editar</button>} {canDelete && <button>🗑️ Eliminar</button>} {canViewDraft && post.isDraft && ( <span>👁️ Vista previa (borrador)</span> )} </article> ); }
🔧 Patrón Avanzado con Hook de Permisos
javascript
// permissions.js const PERMISSIONS = { DELETE_POST: 'delete_post', EDIT_POST: 'edit_post', VIEW_DRAFTS: 'view_drafts', MANAGE_USERS: 'manage_users' }; const rolePermissions = { admin: ['delete_post', 'edit_post', 'view_drafts', 'manage_users'], editor: ['edit_post', 'view_drafts'], betaTester: ['view_drafts'] }; // Hook personalizado export const usePermissions = () => { const { user } = useAuth(); const hasPermission = (permission) => { if (!user || !user.role) return false; return rolePermissions[user.role]?.includes(permission) || false; }; const canEditPost = (post) => { return hasPermission(PERMISSIONS.EDIT_POST) || post.author === user?.username; }; const canDeletePost = (post) => { return hasPermission(PERMISSIONS.DELETE_POST) || post.author === user?.username; }; return { hasPermission, canEditPost, canDeletePost }; };
Uso del Hook
javascript
function BlogPost({ post }) { const { canEditPost, canDeletePost } = usePermissions(); return ( <article> <h2>{post.title}</h2> {canEditPost(post) && <button>✏️ Editar</button>} {canDeletePost(post) && <button>🗑️ Eliminar</button>} </article> ); }
🎮 Ejemplo Completo Integrado
javascript
// auth.js import { createContext, useContext, useState } from 'react'; const AuthContext = createContext(); const adminList = ['Iris', 'Retaxmaster']; const editorList = ['Ana', 'Carlos']; const betaTesterList = ['User1', 'User2']; export const AuthProvider = ({ children }) => { const [user, setUser] = useState(null); const login = (username) => { const userData = { username, isAdmin: adminList.includes(username), isEditor: editorList.includes(username), isBetaTester: betaTesterList.includes(username) }; setUser(userData); }; const logout = () => setUser(null); return ( <AuthContext.Provider value={{ user, login, logout }}> {children} </AuthContext.Provider> ); }; export const useAuth = () => useContext(AuthContext); // BlogPost.jsx function BlogPost({ post }) { const { user } = useAuth(); if (!user) return null; const isAuthor = post.author === user.username; const canEdit = user.isAdmin || user.isEditor || isAuthor; const canDelete = user.isAdmin || isAuthor; const canViewDraft = user.isAdmin || user.isEditor || user.isBetaTester; return ( <article> <h2>{post.title}</h2> <p>📝 Por: {post.author}</p> <div>{post.content}</div> <div className="actions"> {canEdit && ( <button onClick={() => handleEdit(post.id)}> ✏️ Editar </button> )} {canDelete && ( <button onClick={() => handleDelete(post.id)}> 🗑️ Eliminar </button> )} {post.isDraft && canViewDraft && ( <span className="badge">👁️ Borrador</span> )} </div> </article> ); } // AdminPanel.jsx function AdminPanel() { const { user } = useAuth(); // Solo admins pueden ver el panel if (!user?.isAdmin) { return <p>⛔ Acceso denegado</p>; } return ( <div> <h1>⚙️ Panel de Administración</h1> <button>👥 Gestionar Usuarios</button> <button>📊 Ver Estadísticas</button> </div> ); }
📊 Jerarquía de Permisos
👑 Admin ├─ ✅ Eliminar cualquier post ├─ ✅ Editar cualquier post ├─ ✅ Ver borradores └─ ✅ Gestionar usuarios ✏️ Editor ├─ ❌ Eliminar posts ├─ ✅ Editar cualquier post └─ ✅ Ver borradores 🧪 Beta Tester ├─ ❌ Eliminar posts ├─ ❌ Editar posts └─ ✅ Ver borradores 👤 Autor ├─ ✅ Eliminar SU post ├─ ✅ Editar SU post └─ ❌ Ver otros borradores
✨ Mejores Prácticas
✅ Hacer:
PERMISSIONS.DELETE_POSTusePermissions()if (!canDelete) return nulluser.isAdmin || user.isEditor❌ Evitar:
if (role === 'admin')🛡️ Seguridad Real
javascript
// Frontend (UX mejorada) if (!user.isAdmin) { return null; // Oculta botón } // Backend (Seguridad real) app.delete('/api/posts/:id', (req, res) => { if (!req.user.isAdmin && req.user.id !== post.authorId) { return res.status(403).json({ error: 'Forbidden' }); } // Procesar eliminación });
⚠️ Crítico: Frontend = UX. Backend = Seguridad. ¡Siempre valida en el servidor!
🚀 Evolución del Sistema
Nivel 1: isAdmin boolean ↓ Nivel 2: Múltiples roles (isAdmin, isEditor) ↓ Nivel 3: Sistema de permisos (hasPermission) ↓ Nivel 4: Backend + Base de datos + JWT
Este sistema te permite controlar exactamente qué puede hacer cada usuario en tu aplicación, mejorando tanto la seguridad como la experiencia de usuario. 🎯
English version branch & commit following industry standards
Branch name:
feat/role-based-auth
Commit message:
git commit -m "feat: implement role-based authorization
- Add admin role functionality with adminList
- Implement isAdmin flag in user authentication state
- Add authorization checks for admin-only features
- Set up basic role verification in AuthContext
- Enable admin-specific UI elements and permissions"
Roles y permisos
.
La autenticación consiste en identificar personas, saber quiénes acceden a una aplicación. Mientras que la autorización consiste en saber qué permisos tiene cada persona que haya pasado por la autenticación.
.
Vamos a dar ciertos permisos a los usuarios para que puedan eliminar o editar un blog según el rol que tengan en la aplicación.
.
Lo primero que hacemos en el archivo auth.js es crear una lista de administradores. Al momento de loguear un usuario, revisaremos si el usuario es un administrador. Entonces, ademas del nombre de usuario tendremos también una nueva propiedad llamada isAdmin que indica si se trata de un usuario administrador.
.
const adminList = ['Irisval', 'RetaxMaster', 'freddier']; const AuthContext = React.createContext(); function AuthProvider({ children }) { ... const login = ({ username }) => { const isAdmin = adminList.find(admin => admin === username); setUser({ username, isAdmin }); navigate('/profile'); }; ... }
.
Posteriormente, desde BlogPost vamos a utilizar permisos para eliminar un blogpost. En primer lugar, hay 2 maneras para que alguien pueda eliminar un blogpost:
.
. Por lo tanto, si se cumple alguna de estas condiciones, guardaremos el resultado en la variable canDelete, lo que indicará si podemos renderizar un botón para eliminar un blogpost. .
function BlogPost() { const navigate = useNavigate(); const { slug } = useParams(); const auth = useAuth(); const blogpost = blogdata.find(post => post.slug === slug); const canDelete = auth.user?.isAdmin || blogpost.author === auth.user?.username; const returnToBlog = () => { navigate('/blog'); }; return ( <> <h3>{blogpost.title}</h3> <button onClick={returnToBlog}>Volver al blog</button> <p>{blogpost.author}</p> <p>{blogpost.content}</p> {canDelete && ( <button>Eliminar blogpost</button> )} </> ); } export { BlogPost };
Ya no definiría si el usuario puede hacer tal acción dentro de la app basado en isAdmin. Para ello generaria una estructura de roles donde se definan desde allí los permisos que aplican para cada uno de ellos, así basado en el rol que tenga cada usuario será las acciones que tienen permitidas.
yo lo hice asi
Yo lo realice de la siguiente manera, me guie de unos de los post de los companeros y fui desarrollando mi idea, si tienen alguna sugerencia por favor me lo pueden comentar abajo.
Como primera medida cree un archivo js para manejar los roles y la asignacion de estos en un objeto
Despues realice el manejo del estado en el archivo auth dependiendo del rol que tenga cada usuario
Me tomo bastante tiempo encontrar esta solucion pero me siento satisfecho de como lo maneje. Gracias
Mi solución
En mi experiencia diseñando interfaces de administración puedo decir que normalmente la información sobre el tipo de permisos que un usuario viene en un solo arreglo dentro de un json, no es recomendable ni practico crear listas separadas para administradores, autores o usuarios normales. Esto debido a que en cualquier tipo de iteración tendríamos que hacerlo sobre cada lista que tengamos, la cual va a variar según los tipos de permisos que nos soliciten afectando seriamente el performance de nuestra aplicación. Siguiendo esta idea en auth.js creé una lista mixta en donde pueden venir 3 tipos de roles:
auth.js
import React, { useContext } from "react"; import { Navigate, useNavigate } from "react-router-dom"; const AuthContext = React.createContext() const users = [ { username: 'Cortana', type: 'admin', }, { username: 'Jhon117', type: 'admin', }, { username: 'Nicobytes', type: 'author', }, { username: 'diannerd', type: 'author', }, { username: 'JuanDc', type: 'author', }, { username: 'Mixtaco', type: 'reader', } ] function AuthProvider({ children }){ const navigate = useNavigate() const [user, setUser] = React.useState(null); const assignRole = username => { const userFound = users.filter( user => user.username === username ) return userFound.length > 0 ? userFound[0].type : 'reader' } const login = ({ username }) => { const role = assignRole(username) setUser({ username, role }) navigate('/profile') } const logout = () => { setUser(null) navigate('/') } const auth = { user, login, logout }; return( <AuthContext.Provider value={auth}> { children } </AuthContext.Provider> ) } function useAuth(){ const auth = useContext(AuthContext); return auth; } function RedirectToLogin(props){ const auth = useAuth(); if (!auth.user) { return <Navigate to='/login' />; } return props.children; } export { AuthProvider, useAuth, RedirectToLogin }
Ahora yo cambie la búsqueda que el profe propone por una función llamada assignRole la cual retorna el nombre del tipo de permiso que le hemos asignado (Se supone que esta información esta almacenada en una base de datos(DB) ). Como nuestra aplicación acepta cualquier nombre como autenticación en caso de no encontrarse en la DB, le asignamos permisos de solo lectura para evitar errores a futuro. Ahora mi setUser actualiza pasando el username y rol independientemente de cual sea.
BlogPost.js
import React from "react"; import { useNavigate, useParams } from "react-router-dom"; import { blogdata } from "./blogdata"; import { useAuth } from "./auth"; export default function BlogPost(){ const { slug } = useParams(); const navigate = useNavigate(); const auth = useAuth() const blogpost = blogdata.find( post => post.slug === slug); const goToBack = () => { navigate(`/blog`); } return( <> <h3> { blogpost.title } </h3> <button onClick={goToBack}> Regresar </button> <p> { blogpost.content } </p> <p> { blogpost.author } </p> {(auth.user?.role === 'admin' || (auth.user?.role === 'author' && auth.user?.username === blogpost.author)) && ( <button> Eliminmar post </button> )} {(auth.user?.role === 'author' && auth.user?.username === blogpost.author) && ( <button> Editar post </button> )} </> ) }
Nos pasamos a BlogPost.js para hacer la comprobación y mostrar un botón, para mostrarse debe cumplir con los siguientes puntos:
Nuestra comprobación queda algo larga, pero esto nos refuerza la seguridad, pues aunque nuestro usuario sea el autor del post debe tener permisos de edición habilitados. Esta lógica nos abre la puerta para agregar un botón de edición el cual ni siquiera los "admins" pueden tener. Unicamente el autor del post puede tener este poder. Esto nos abre la posibilidad de observar el tipo de usuario desde Profile.js y no andar haciendo console.logs para saber que tipo de usuario tenemos
Profile.js
import React from "react"; import { useAuth } from "./auth"; export default function ProfilePage(){ const auth = useAuth() return( <> <h3>Profile</h3> <h4> {auth.user.username} </h4> <h5> Permisos: {auth.user.role} </h5> </> ); }
Esta fue la mejor solucion que pude encontrar al reto . Inicialmente defini los valores de entrada. Con los diferentes array de roles, los junte en un unico array ya que me parecio la manera mas comoda de trabajar.
Luego, lo que hice fue crear una variable para saber quien es el usuario actual y verificar que exista en alguna de las listas. . Para ello utilice el metodo some para que recorriera el Array de arrays y dentro del mismo para cada Array busque aquel que con el metodo find contenga el usuario que se loggeo con el auth. . Asi pude sobreescribir la variable currentUser y con esta acceder usando la notacion de corchetes para poder usar una variable en el objeto skills que contiene los diferentes roles con su respectivo componente (en este caso estructura html) a renderizar deacuerdo a sus funciones
Código de la clase en TypeScript :
interface User { username: string, isAdmin: boolean, } interface Auth { user: User | null login: (value: Omit<User, "isAdmin">) => void logout: () => void } const adminList = ['Max', 'Tony', 'Steve']; const AuthContext = createContext({} as Auth); const AuthProvider: FC<PropsWithChildren> = ({ children }) => { const navigate = useNavigate(); const [user, setUser] = useState<Auth['user']>(null); const login: Auth['login'] = ({ username }) => { const isAdmin = adminList.some(admin => admin === username); setUser({ username, isAdmin }); navigate('/profile') } const logout: Auth['logout'] = () => { setUser(null); navigate('/'); } const auth: Auth = { user, login, logout } return ( <AuthContext.Provider value={auth}> {children} </AuthContext.Provider> ) }
BlogSpot:
type IBlogPostParams = { slug: string } const BlogPost = () => { const navigate = useNavigate(); const { slug } = useParams<IBlogPostParams>(); const { user } = useAuth(); const blogpost = blogdata.find(post=> post.slug === slug); const canDelete = user?.isAdmin || blogpost?.author === user?.username; const returnToBlog = () =>{ navigate('/blog'); }; return( <> <h3>{ blogpost?.title }</h3> <button onClick={returnToBlog}>Volver al blog</button> <p>{ blogpost?.content }</p> <p>{ blogpost?.author }</p> {canDelete && ( <button>Eliminar blogpost</button> )} </> ) }