13 sept 2025·8 min

Menús de navegación con control de permisos que siguen aplicando el acceso

Los menús de navegación conscientes de permisos mejoran la claridad, pero la seguridad debe residir en el backend. Ve patrones sencillos para roles, políticas y ocultación segura en la interfaz.

Menús de navegación con control de permisos que siguen aplicando el acceso

Qué problema resuelven en realidad los menús conscientes de permisos

Cuando la gente dice “oculta el botón”, normalmente quieren decir una de dos cosas: reducir el desorden para usuarios que no pueden usar una función, o evitar un uso indebido. Solo el primer objetivo es realista en el frontend.

Los menús conscientes de permisos son principalmente una herramienta de UX. Ayudan a que alguien abra la app y vea de inmediato lo que puede hacer, sin toparse con pantallas de “Acceso denegado” a cada rato. También reducen la carga de soporte al evitar confusiones como “¿Dónde apruebo facturas?” o “¿Por qué esta página da error?”.

Ocultar la UI no es seguridad. Es claridad.

Incluso un compañero curioso todavía puede:

  • Escribir un enlace profundo a una página oculta si conoce la URL
  • Abrir un marcador antiguo a una pantalla de admin
  • Llamar a tu API directamente con un script o una herramienta
  • Modificar peticiones desde el navegador e intentar con IDs diferentes

Así que el problema real que resuelven los menús conscientes de permisos es dar una guía honesta. Mantienen la interfaz alineada con el trabajo, rol y contexto del usuario, y dejan claro cuando algo no está disponible.

Un buen estado final se parece a esto:

  • Los usuarios ven mayormente acciones que tienen sentido para ellos y raramente se topan con errores sorpresa.
  • Los desarrolladores tienen una única fuente de verdad para las reglas de acceso, en vez de comprobaciones dispersas.
  • Los cambios de producto son más seguros porque un nuevo elemento de menú te obliga a decidir qué permiso requiere.
  • La seguridad se aplica donde importa: el backend rechaza acciones prohibidas siempre, aunque la UI las muestre por error.

Ejemplo: en un CRM pequeño, un Sales Rep debería ver Leads y Tasks, pero no User Management. Si aun así pega la URL de gestión de usuarios, la página debería fallar cerrada y el servidor debe bloquear cualquier intento de listar usuarios o cambiar roles.

Visibilidad no es autorización

La visibilidad es lo que la interfaz decide mostrar. La autorización es lo que el sistema realmente permitirá cuando una petición llegue al servidor.

Los menús conscientes de permisos reducen la confusión. Si alguien nunca podrá ver Billing o Admin, ocultar esos elementos mantiene la app limpia y baja las solicitudes de soporte. Pero ocultar un botón no es un candado. La gente aún puede intentar el endpoint subyacente usando herramientas de desarrollador, un marcador antiguo o una petición copiada.

Una regla práctica: decide la experiencia que quieres y luego aplica la regla en el backend sin importar lo que haga la UI.

Al presentar una acción, tres patrones cubren la mayoría de los casos:

  • Ocultar cuando la función debe ser no descifrable para la mayoría de roles (por ejemplo, herramientas internas).
  • Deshabilitar cuando la función existe pero el usuario no puede usarla ahora (por ejemplo, Export deshabilitado hasta mejorar el plan, hasta seleccionar un registro o mientras se cargan datos).
  • Mostrar con explicación cuando los usuarios necesitan entender por qué no pueden continuar (“Puedes ver facturas, pero solo los Owners pueden editar métodos de pago”).

“Puedes ver pero no editar” es común y merece diseño explícito. Trátalo como dos permisos: uno para leer datos y otro para cambiarlos. En el menú podrías mostrar Detalles del cliente a todos los que pueden leer, pero solo mostrar Editar cliente a los que tienen acceso de escritura. En la página, renderiza campos como solo lectura y bloquea controles de edición, permitiendo al mismo tiempo que la página cargue.

Lo más importante: el backend decide el resultado final. Incluso si la UI oculta todas las acciones de admin, el servidor aún necesita comprobar permisos en cada petición sensible y devolver una respuesta clara de “no permitido” cuando alguien lo intenta.

Elige un modelo de permisos que puedas mantener

La forma más rápida de entregar menús conscientes de permisos es empezar con un modelo que tu equipo pueda explicar en una frase. Si no puedes explicarlo, no lo mantendrás correcto.

Usa roles para agrupar, no para significar todo. Admin y Support son buckets útiles. Pero cuando los roles empiezan a multiplicarse (Admin-West-Coast-ReadOnly), la UI se vuelve un laberinto y el backend una suposición.

Prefiere permisos como fuente de verdad sobre lo que alguien puede hacer. Mantenlos pequeños y basados en acciones, como invoice.create o customer.export. Esto escala mejor que la proliferación de roles porque las nuevas funciones suelen añadir nuevas acciones, no nuevos títulos de trabajo.

Luego añade políticas (reglas) para el contexto. Aquí es donde manejas “puede editar solo su propio registro” o “puede aprobar facturas solo por debajo de $5,000”. Las políticas evitan crear docenas de permisos casi idénticos que difieren solo por una condición.

Una capa mantenible se ve así:

  • Roles agrupan permisos (función laboral)
  • Permisos describen acciones (qué)
  • Políticas añaden contexto (cuándo, qué registros)

El nombre importa más de lo que se espera. Si tu UI dice Export Customers pero la API usa download_all_clients_v2, acabarás ocultando lo incorrecto o bloqueando lo correcto. Mantén nombres humanos, consistentes y compartidos entre frontend y backend:

  • Usa sustantivo.verbo (o recurso.acción) consistentemente
  • Alinea las etiquetas de la UI con la intención del permiso, no con nombres internos de código
  • Mantén los nombres de permisos antiguos funcionando cuando renombres funciones

Ejemplo: en un CRM, un rol Sales podría incluir lead.create y lead.update, pero una política limita las actualizaciones a leads que el usuario posee. Eso mantiene tu menú claro mientras tu backend sigue estricto.

Paso a paso: implementar roles y permisos de extremo a extremo

Los menús conscientes de permisos se sienten bien porque reducen desorden y previenen clics accidentales. Pero solo ayudan cuando el backend sigue a cargo. Piensa en la UI como una pista y en el servidor como el juez.

Empieza por escribir qué estás protegiendo. No páginas, sino acciones. Ver la lista de clientes es distinto a exportar clientes o borrar un cliente. Esto es la columna vertebral de menús conscientes de permisos que no se convierten en teatro de seguridad.

Una receta práctica de extremo a extremo

  1. Inventario de acciones en UI y API. Para cada función, lista operaciones concretas: leer, crear, actualizar, borrar, exportar, invitar, cambiar facturación, ejecutar informes admin.
  2. Define permisos en el servidor como fuente de verdad. Almacena el mapeo rol→permiso en la base de datos (o config) y haz que cada handler de la API pregunte “¿tiene este usuario el permiso X sobre el recurso Y?”.
  3. Devuelve una pequeña carga de capacidades para la UI. Tras el login (o al refrescar sesión), devuelve booleanos como canEditCustomers, canDeleteCustomers, canExport, o una lista compacta de strings de permisos. Manténlo mínimo.
  4. Aplica comprobaciones en cada write, y en lecturas sensibles. Todas las mutaciones deben comprobar permisos. Algunas lecturas también (por ejemplo, salarios, logs de auditoría, exportes o cualquier endpoint que eluda el filtrado normal).
  5. Añade algunas pruebas centradas en roles. Elige 2-3 roles clave y prueba las acciones de mayor riesgo. Ejemplo: Sales puede crear deals pero no exportar clientes; Admin puede invitar usuarios.

Una regla importante: nunca confíes en flags de roles o permisos proporcionados por el cliente. La UI puede ocultar botones según capacidades, pero la API debe rechazar peticiones no autorizadas.

Patrones de frontend que se mantienen honestos

Refactoriza con confianza
Toma snapshots antes de refactorizar permisos para poder revertir rápido si un rol falla.

Los menús conscientes de permisos deberían ayudar a las personas a encontrar lo que pueden hacer, no fingir que hacen cumplir seguridad. El frontend es una proteccióndirigida. El backend es la cerradura.

Construye menús desde una única fuente de verdad

En lugar de esparcir comprobaciones de permisos por cada botón, define la navegación desde una configuración que incluya el permiso requerido para cada ítem y luego renderiza a partir de esa config. Esto mantiene las reglas legibles y evita comprobaciones olvidadas en rincones extraños de la UI.

Un patrón simple se ve así:

const menu = [
  { label: "Contacts", path: "/contacts", requires: "contacts.read" },
  { label: "Export", action: "contacts.export", requires: "contacts.export" },
  { label: "Admin", path: "/admin", requires: "admin.access" },
];

const visibleMenu = menu.filter(item => userPerms.includes(item.requires));

Prefiere ocultar secciones completas (como Admin) en lugar de esparcir comprobaciones en cada enlace de página admin. Son menos lugares para equivocarse.

Estados honestos: oculto vs deshabilitado

Oculta ítems cuando el usuario nunca podrá usarlos. Deshabilita ítems cuando el usuario tiene permiso pero falta contexto.

Ejemplo: Eliminar contacto debe estar deshabilitado hasta que se seleccione un contacto. Mismo permiso, solo falta contexto. Cuando deshabilites, añade un breve “por qué” cerca del control (tooltip, texto de ayuda o nota inline): Selecciona un contacto para eliminar.

Un conjunto de reglas que aguanta:

  • Oculta cuando el usuario carece del permiso.
  • Deshabilita cuando el usuario tiene permiso pero no hay elemento seleccionado, el formulario es inválido o faltan datos.
  • Nunca trates el almacenamiento local como verdad para permisos. Es solo una caché.
  • Recomprueba permisos tras login, cambio de rol o cambio de cuenta.

Aplicación en el backend que no se puede eludir

Ocultar items en el menú ayuda a las personas a concentrarse, pero no protege nada. El backend debe ser el juez final porque las peticiones pueden reproducirse, editarse o dispararse fuera de tu UI.

Una buena regla: cada acción que cambia datos necesita una comprobación de autorización, en un solo lugar, por el que pase toda petición. Puede ser middleware, un wrapper de handler o una pequeña capa de políticas que llames al inicio de cada endpoint. Elige un enfoque y cíñete a él o perderás caminos.

Pon la comprobación en la ruta de la petición

Mantén la autorización separada de la validación de entrada. Primero decide “¿puede este usuario hacer esto?” y luego valida el payload. Si validas primero, puedes filtrar detalles (como si cierto ID existe) a quien ni siquiera debería saber que la acción es posible.

Un patrón que escala:

  • Autentica al llamador y construye una identidad clara (user id, org id, roles, permisos).
  • Carga el contexto del recurso que necesites para autorizar (a menudo solo owner id u org id).
  • Llama a una única función de política (por ejemplo: Can(user, "invoice.delete", invoice)).
  • Si denegado, para inmediatamente y devuelve el estado correcto.
  • Solo entonces valida y ejecuta la lógica de negocio.

Devuelve respuestas claras y consistentes

Usa códigos de estado que ayuden tanto a tu frontend como a tus logs:

  • 401 Unauthorized cuando la llamada no está autenticada.
  • 403 Forbidden cuando está autenticada pero no tiene permiso.

Ten cuidado con usar 404 Not Found como disfraz. Puede servir para no revelar que un recurso existe, pero si lo mezclas aleatoriamente, depurar se vuelve doloroso. Elige una regla consistente por tipo de recurso.

Asegúrate de que la misma autorización se ejecute si la acción vino de un clic, una app móvil, un script o una llamada directa a la API.

Finalmente, registra intentos denegados para depuración y auditoría, pero mantén los logs seguros. Registra quién, qué acción y qué tipo de recurso a alto nivel. Evita campos sensibles, payloads completos o secretos.

Casos límite que hacen o rompen el diseño

La mayoría de bugs de permisos aparecen cuando los usuarios hacen algo que tu menú nunca esperó. Por eso los menús conscientes de permisos son útiles, pero solo si diseñas también para las rutas que los evaden.

Enlaces profundos y pantallas “ocultas”

Si el menú oculta Billing para un rol, un usuario aún puede pegar una URL guardada o abrirla desde el historial. Trata cada carga de página como una petición nueva: obtén los permisos actuales del usuario y haz que la pantalla misma se niegue a cargar datos protegidos cuando falte el permiso. Un mensaje amable de “No tienes acceso” está bien, pero la protección real es que el backend no devuelve nada.

Llamadas directas a la API y endpoints masivos

Cualquiera puede llamar a tu API desde dev tools, un script o otro cliente. Así que comprueba permisos en cada endpoint, no solo en pantallas admin. El riesgo fácil de pasar por alto son las acciones masivas: un /items/bulk-update puede permitir accidentalmente que un no-admin cambie campos que nunca ven en la UI.

Los roles también pueden cambiar en mitad de sesión. Si un admin quita un permiso, el usuario puede seguir con un token viejo o estado de menú en caché. Usa tokens de corta vida o una consulta de permisos en servidor y maneja respuestas 401/403 refrescando permisos y actualizando la UI.

Los dispositivos compartidos crean otro problema: el estado de visibilidad en caché puede filtrarse entre cuentas. Almacena la visibilidad del menú indexada por user ID, o evita persistirla por completo.

Cinco pruebas útiles antes del lanzamiento:

  • Abre un enlace profundo a una página oculta estando logueado con un rol limitado
  • Llama a una API protegida con una petición copiada desde dev tools
  • Cambia el rol del usuario mientras está activo y vuelve a intentar acciones
  • Cierra sesión, inicia con otro usuario y verifica que el menú se reinicia
  • Prueba endpoints masivos con campos permitidos y no permitidos mezclados

Escenario de ejemplo: un menú CRM simple con aplicación real

Hacer cumplir en el backend
Añade comprobaciones de autorización en el servidor a cada handler, no solo al menú.

Imagina un CRM interno con tres roles: Sales, Support y Admin. Todos inician sesión y la app muestra un menú a la izquierda, pero el menú es solo una conveniencia. La seguridad real está en lo que el servidor permite.

Aquí hay un conjunto simple de permisos que sigue siendo legible:

  • Leads: view, create, edit, delete, export
  • Tickets: view, create, edit, assign
  • Billing: view, edit
  • Users: view, manage

La UI empieza pidiendo al backend las acciones permitidas del usuario actual (a menudo como una lista de strings de permisos) además de contexto básico como user id y equipo. El menú se construye desde eso. Si no tienes billing.view, no ves Billing. Si tienes leads.export, ves un botón Export en la pantalla de Leads. Si solo puedes editar tus propios leads, el botón Edit aún puede aparecer pero debe deshabilitarse o mostrar un mensaje claro cuando el lead no es tuyo.

Ahora la parte importante: cada endpoint de acción aplica las mismas reglas.

Ejemplo: Sales puede crear leads y editar leads que posee. Support puede ver tickets y asignarlos, pero no tocar facturación. Admin puede gestionar usuarios y facturación.

Cuando alguien intenta borrar un lead, el backend comprueba:

  1. ¿Tiene el usuario leads.delete?
  2. Si la regla es own-only, ¿lead.owner_id == user.id?
  3. Si el lead está bloqueado (por ejemplo, ya facturado), ¿la eliminación está bloqueada para todos excepto Admin?

Aunque un Support llame manualmente al endpoint de eliminación, recibe una respuesta forbidden. El elemento oculto del menú nunca fue la protección; la decisión fue del backend.

Errores comunes y trampas

La mayor trampa es pensar que terminaste el trabajo cuando el menú se ve correcto. Ocultar botones reduce confusión, pero no reduce el riesgo.

Errores que aparecen con más frecuencia:

  • “No visible” se convierte en “no posible”. Alguien aún puede llamar a la API directamente, reutilizar una URL antigua o disparar la acción vía dev tools. Trata ocultar en la UI como conveniencia, no como puerta.
  • Permisos comprobados solo en la UI. Si el frontend decide quién puede borrar un registro, ya perdiste. El backend debe ser el juez final para cada acción protegida.
  • Un flag gigante isAdmin para todo. Parece rápido y luego se propaga. Pronto cada excepción es un caso especial y nadie puede explicar las reglas de acceso.
  • Confiar en que el cliente diga su rol. Nunca aceptes role, isAdmin o permissions del navegador como verdad. Deriva identidad y acceso desde tu propia sesión o token y luego consulta roles y permisos en el servidor.
  • Olvidar permisos de lectura. Los equipos se enfocan en escrituras, pero las fugas de datos suelen venir de lecturas. Listar clientes, ver facturas, exportar CSVs y búsquedas a menudo necesitan comprobaciones también.

Un ejemplo concreto: ocultas Export leads para no managers. Si el endpoint de exportación no comprueba permisos, cualquier usuario que adivine la petición (o la copie de un compañero) puede descargar el archivo.

Lista rápida antes del lanzamiento

Convierte aprendizajes en créditos
Comparte lo que construiste con Koder.ai y gana créditos para tu próximo proyecto.

Antes de publicar menús conscientes de permisos, haz una última pasada centrada en lo que los usuarios pueden hacer realmente, no en lo que pueden ver. Recorre tu app como cada rol principal y prueba las mismas acciones. Hazlo en la UI y también llamando al endpoint directamente (o usando dev tools) para asegurarte de que el servidor es la fuente de verdad.

Checklist:

  • Para cada acción protegida, confirma que existe una comprobación de autorización en el servidor en el punto de ejecución (no solo al construir el menú).
  • Haz que la UI renderice acciones desde capacidades provistas por el servidor (por ejemplo, un can-list desde la sesión o un endpoint de permisos) en vez de adivinar a partir de nombres de roles.
  • Verifica que la UI trate las respuestas forbiddens como normales: muestra un mensaje claro, mantiene la página estable y evita dejar al usuario en un estado roto.
  • Prueba cambios de rol y permiso. Cuando un admin actualiza acceso, debería aplicarse rápido y de forma predecible (refresco de token, invalidación de sesión o versionado de permisos).
  • Ejecuta algunas pruebas de alto valor que cubran roles y acciones que tocan dinero, exportes, borrados y ajustes admin.

Una forma práctica de detectar huecos: elige un botón “peligroso” (eliminar usuario, exportar CSV, cambiar facturación) y traza su flujo de punta a punta. El ítem del menú debe ocultarse cuando corresponda, la API debe rechazar llamadas no autorizadas y la UI debe recuperarse con gracia al recibir un 403.

Próximos pasos: publicar de forma segura sin frenar el ritmo

Empieza pequeño. No necesitas una matriz de acceso perfecta el día uno. Elige el puñado de acciones que más importan (ver, crear, editar, borrar, exportar, gestionar usuarios), asígnalas a los roles que ya tienes y sigue adelante. Cuando llegue una nueva función, añade solo las acciones nuevas que introduce.

Antes de construir pantallas, haz una pasada de planificación que liste acciones, no páginas. Un ítem de menú como Invoices oculta muchas acciones: ver lista, ver detalles, crear, devolver, exportar. Escribir eso primero hace que tanto la UI como las reglas del backend sean más claras y evita el error común de proteger una página completa mientras dejas un endpoint riesgoso sin protección.

Cuando refactorices reglas de acceso, trátalo como cualquier otro cambio riesgoso: mantén una red de seguridad. Los snapshots te permiten comparar comportamiento antes y después. Si un rol de repente pierde acceso necesario o gana acceso que no debe, hacer rollback es más rápido que arreglar en caliente con usuarios bloqueados.

Una rutina de lanzamiento simple ayuda a los equipos a moverse rápido sin adivinar:

  • Escribe una lista de acciones para la función y nombra cada permiso.
  • Añade comprobaciones backend primero, luego conecta la visibilidad de la UI a los mismos permisos.
  • Prueba un rol permitido y uno denegado para cada acción riesgosa.
  • Registra intentos denegados (sin datos sensibles) para detectar huecos.
  • Toma un snapshot antes del despliegue para revertir rápido si hace falta.

Si construyes con una plataforma basada en chat como Koder.ai (koder.ai), la misma estructura aplica: define permisos y políticas una vez, deja que la UI lea capacidades del servidor y haz que las comprobaciones backend sean obligatorias en cada handler.

Preguntas frecuentes

¿Qué resuelven realmente los menús conscientes de permisos?

Los menús conscientes de permisos principalmente resuelven claridad, no seguridad. Ayudan a los usuarios a centrarse en lo que realmente pueden hacer, reducen clics que llevan a callejones sin salida y disminuyen las preguntas de soporte tipo “¿por qué veo esto?”.

La seguridad debe seguir aplicándose en el backend, porque cualquiera puede intentar enlaces profundos, marcadores antiguos o llamadas directas a la API sin importar lo que muestre la UI.

¿Cuándo debo ocultar un elemento del menú en vez de deshabilitarlo?

Oculta cuando una función debería ser prácticamente indescrubrible para un rol y no existe una vía esperada para que la usen.

Deshabilita cuando el usuario podría tener acceso pero le falta contexto en ese momento, por ejemplo, cuando no hay ningún registro seleccionado, el formulario está inválido o los datos aún se están cargando. Si deshabilitas, añade una breve explicación para que no parezca roto.

¿Por qué “ocultar el botón” no es seguridad?

Porque visibilidad no es autorización. Un usuario puede pegar una URL, reutilizar una pantalla de administrador guardada o llamar a tu API fuera de la UI.

Trata la UI como guía. Trata al backend como el decisor final para cada petición sensible.

¿Cómo debe saber el frontend qué puede hacer el usuario?

Tu servidor debería devolver una pequeña respuesta de “capacidades” después del inicio de sesión o la actualización de sesión, basada en comprobaciones de permisos en el servidor. La UI renderiza menús y botones a partir de eso.

No confíes en flags proporcionados por el cliente como isAdmin desde el navegador; calcula permisos desde la identidad autenticada en el servidor.

¿Cuál es la forma más simple y completa para implementar permisos?

Empieza inventariando acciones, no páginas. Para cada característica separa cosas como ver, crear, actualizar, borrar, exportar, invitar y cambiar facturación.

Luego aplica cada permiso en el handler del backend (o en middleware/wrapper) antes de hacer trabajo. Vincula el menú a los mismos nombres de permiso para que UI y API permanezcan alineados.

¿Debo usar roles o permisos como modelo principal?

Por defecto práctico: los roles son buckets y los permisos son la fuente de verdad. Mantén los permisos pequeños y basados en acciones (por ejemplo, invoice.create) y asígnalos a roles.

Si los roles empiezan a multiplicarse para codificar condiciones (como región u ownership), mueve esas condiciones a políticas en lugar de crear variantes infinitas de roles.

¿Cómo manejo “puede ver pero no editar” y otros accesos condicionales?

Usa políticas para reglas contextuales como “puede editar solo su propio registro” o “puede aprobar facturas solo bajo cierto importe”. Así mantienes estable la lista de permisos mientras expresas restricciones del mundo real.

El backend debe evaluar la política usando el contexto del recurso (por ejemplo, owner ID u org ID), no supuestos desde la UI.

¿Realmente necesito comprobaciones de permisos en endpoints de lectura?

No siempre. Las lecturas que exponen datos sensibles o que evaden el filtrado normal también deben estar protegidas, por ejemplo exportes, logs de auditoría, datos salariales, listas de usuarios admin o cualquier endpoint que devuelva más de lo que la UI normalmente muestra.

Una buena regla: todas las escrituras deben comprobarse, y las lecturas sensibles también.

¿Cómo evito agujeros de seguridad con acciones masivas y endpoints “poderosos”?

Los endpoints bulk son fáciles de pasar por alto porque pueden cambiar muchos registros o campos en una sola petición. Un usuario bloqueado en la UI puede aún llamar a /items/bulk-update directamente.

Comprueba permisos para la acción bulk en sí y valida qué campos se permiten cambiar para ese rol; de lo contrario podrías permitir editar campos ocultos accidentalmente.

¿Qué debería pasar cuando los roles cambian a mitad de sesión o la caché de la UI está obsoleta?

Asume que los permisos pueden cambiar mientras alguien está conectado. Cuando la API devuelve 401 o 403, la UI debería manejarlo como un estado normal: refrescar capacidades, actualizar el menú y mostrar un mensaje claro.

Además, evita persistir la visibilidad del menú de forma que pueda filtrarse entre cuentas en dispositivos compartidos; si la cachéas, clavea por identidad de usuario o no la persistas.

Related posts