Roles de usuario y un panel de administración real en Bolt
Cómo guardar los roles donde el usuario no pueda cambiarlos, hacerlos cumplir con RLS y funciones de servidor y armar en Bolt.new un panel admin de verdad.
Read in English →
Un panel de administración de verdad en una app de Bolt.new necesita tres cosas: roles guardados donde el usuario no pueda editarlos, políticas de base de datos que revisen esos roles en cada petición y funciones de servidor para las acciones que requieren permisos elevados. Esconder la página /admin o el botón "Admin" solo cambia lo que la gente ve. Cualquiera con la clave pública de tu app puede hablar directo con la base de datos, así que la regla tiene que vivir ahí.
En esta guía verás por qué una página escondida no protege nada, dónde guardar el rol, el SQL de una configuración simple y segura, cómo resolver las acciones exclusivas de administradores y cómo comprobar que un usuario común de verdad no puede entrar.
¿Por qué no alcanza con esconder la página de admin?
La documentación de seguridad de la base de Bolt lo dice sin vueltas: cualquiera que visite tu sitio puede mandar peticiones directo a tu base de datos, sin pasar por tus pantallas. Una guarda de ruta en React que revisa if (user.role === 'admin') frena a los usuarios honestos. No frena a quien abre las herramientas de desarrollo del navegador y consulta la misma tabla que lee la página de admin.
Entonces, la interfaz es comodidad y la base de datos es la cerradura. Cada tabla que el panel lee o modifica necesita una política que revise el rol del lado del servidor.
¿Dónde guardo el rol del usuario?
| Dónde | ¿El usuario puede cambiarlo? | Veredicto |
|---|---|---|
Metadatos del usuario al registrarse (user_metadata) | Sí, el usuario puede actualizar sus propios metadatos | Nunca para permisos |
Una columna role en profiles que el usuario puede editar | Sí, salvo que esa columna esté protegida aparte | Riesgoso; error frecuente en apps generadas con IA |
Una tabla user_roles aparte, sin política de escritura | No | Simple y seguro |
| Un dato agregado al token de sesión con un hook de autenticación | No | Seguro, con más configuración; mira la guía de RBAC de Supabase |
La segunda fila merece atención. Muchas apps generadas agregan role a la tabla de perfiles y una política que deja a cada usuario editar su propio perfil. Esa misma política le permite poner role = 'admin' desde la consola. Revisa esto antes que cualquier otra cosa.
¿Cómo configuro los roles de forma segura?
Bolt Database corre sobre Postgres con el mismo modelo de políticas que Supabase, así que este SQL sirve en los dos. Pídele a Bolt que lo aplique o ejecútalo tú en el editor SQL (Query en la vista Tables de Bolt, o el editor SQL de Supabase si conectaste o reclamaste un proyecto).
create table public.user_roles (
user_id uuid primary key references auth.users(id) on delete cascade,
role text not null default 'member' check (role in ('member', 'admin'))
);
alter table public.user_roles enable row level security;
-- Cada usuario ve su propio rol. Sin políticas de insert ni update, a propósito.
create policy "read own role" on public.user_roles
for select to authenticated
using ((select auth.uid()) = user_id);
create or replace function public.is_admin()
returns boolean
language sql stable security definer
set search_path = ''
as $
select exists (
select 1 from public.user_roles
where user_id = (select auth.uid()) and role = 'admin'
);
$;
Después usa la función en las políticas de cada tabla que necesite el panel. Por ejemplo, cada usuario lee sus pedidos y los administradores leen todos:
create policy "owners or admins read orders" on public.orders
for select to authenticated
using ((select auth.uid()) = user_id or (select public.is_admin()));
Dos detalles importan. security definer permite que la función lea user_roles sin depender de las políticas de quien la llama. Y set search_path = '' evita la advertencia "Function Search Path Mutable" que muestra la pestaña Security de la base en Bolt, la misma que reporta el Security Advisor de Supabase.
El primer administrador
Como ninguna política permite escribir en user_roles, la app no puede ascender a nadie. Esa es la idea. Crea el primer administrador a mano: busca tu ID de usuario en User Management dentro de la configuración de la base (se puede buscar por correo) y ejecuta:
insert into public.user_roles (user_id, role)
values ('<tu-id-de-usuario>', 'admin');
¿Cómo resuelvo las acciones solo para administradores?
Algunas acciones van más allá de leer filas: borrar un usuario, cambiarle el rol a alguien, hacer un reembolso. Necesitan la clave secreta, que se salta RLS, así que van en una función de servidor. El patrón siempre es el mismo:
- El panel llama a la función de servidor con la sesión del usuario conectado.
- La función identifica a quien llama a partir de esa sesión y revisa
is_admin()(o leeuser_roles). - Solo si pasa la revisión usa la clave secreta, guardada en Secrets, para hacer el trabajo.
- Registra una fila en una tabla
admin_audit_log: quién hizo qué, sobre qué registro y cuándo.
Nunca mandes "soy administrador" como parámetro desde el navegador. La función tiene que deducirlo de la sesión.
¿Qué le pido a Bolt?
Bolt lo hace bien cuando el prompt es concreto. Por ejemplo:
Add an admin area at /admin. Roles live in the user_roles table, checked
with the is_admin() function. Do not add a role column to profiles and do
not create insert or update policies on user_roles. The admin area lists
all orders and lets admins change an order's status. Enforce access in RLS
policies, not only in the React route.
Después bloquea en la vista de código (Lock file) los archivos que tienen la lógica de roles, para que un prompt posterior sobre colores no los reescriba.
¿Cómo compruebo que funciona de verdad?
- Crea dos cuentas de prueba: una de administrador y una común.
- Entra con la cuenta común y abre
/admin. Deberías ser redirigido o no ver nada. - Con esa misma cuenta, abre la pestaña Network, copia una petición que haría la página de admin y repítela. La respuesta debe venir vacía o con error, nunca con los datos.
- Intenta actualizar tu propia fila en
user_rolesdesde la consola. Tiene que fallar. - Corre el chequeo de Security de la base en Bolt y resuelve lo que marque, sobre todo
RLS Policy Always True.
Si el paso 3 devuelve datos, falta la política o es demasiado amplia. Nuestras guías de ejemplos de políticas RLS en Supabase y de cuando un usuario ve datos de otro explican cómo rastrearlo.
Respuestas rápidas
¿Puedo decidir quién es admin revisando el correo? Sirve para una persona, pero se rompe cuando cambia el correo y deja una regla fija repartida por el código. Una tabla de roles casi no cuesta más.
¿Necesito más roles que admin y miembro? Agrégalos cuando hagan falta, por ejemplo staff con acceso de solo lectura. Si tus clientes pertenecen a empresas, los roles suelen ir por empresa; mira multi-empresa con Supabase.
¿La pantalla User Management de Bolt es un panel de administración? Es para ti como dueño del proyecto, dentro de Bolt. El equipo de tu app necesita su propia área de administración dentro de la app.
¿Qué más debería tener un panel de admin? Un registro de auditoría, inicio de sesión en dos pasos para las cuentas de administrador y una lista corta de quién tiene acceso. Más en seguridad del panel de administración.
¿Quieres que alguien revise tus permisos?
Los roles son donde un error chico expone más datos. Nuestra revisión de seguridad de Supabase recorre cada tabla y política, y el servicio para arreglar apps de Bolt cubre el resto de la app. Empieza con la revisión gratuita o escríbenos.


