Skip to content

How Do I Stop Users From Making Themselves Admin?

6 min read

Can a regular user turn themselves into an admin by editing a request or a field? If your app decides who is an admin based on anything the browser sends, then yes, and it usually takes about thirty seconds. The fix is to make the server the only place that reads and enforces a user's role, so a request that says "I'm an admin" gets ignored unless the server's own records agree.

Why this happens

When you asked your AI agent to "add an admin dashboard," it built exactly that: a page that checks a role and shows admin tools. What you did not ask for, and so did not get, was a guarantee that the role cannot be tampered with. So the agent often takes the easy path. It stores role in a field the user can edit, or it reads isAdmin from a value the browser sends up with each request, or it trusts a flag saved in localStorage. Every one of those is under the user's control.

The demo works, because in the demo nobody is trying to cheat. The gap only shows up when a curious beta user opens their browser's dev tools, sees a role: "user" field in a request or a profile record, changes it to "admin", and sends it back. If the server saves that without checking, they are now an admin. This is called privilege escalation, and it is one of the most common holes in vibe-coded apps.

How to check

You are affected if any of these is true:

  1. Look at where the role lives. If there is a role, isAdmin, or plan column that the user's own profile-update or settings request can write to, assume it is editable. On Supabase or Firebase, check whether a user can update their own row's role field under your current rules.
  2. Watch a request. Open your app, go to any "save" action, and look in the browser Network tab at what gets sent. If you see the role or any permission flag in the request body, the browser is the one asserting it.
  3. Try it yourself. As a normal test user, use the Network tab to resend a profile-save request with "role": "admin" added. Then reload. If admin tools appear, a stranger can do the same.

The fix

  1. Move the role to a place the user cannot write. Roles belong in a server-controlled table or column that ordinary users have no permission to update. On Supabase, put roles in their own table and write a policy that lets users read their role but never update it. On Firebase, set roles as custom claims or in a document users cannot write.
  2. Check the role on the server, on every protected action. Do not gate admin features by hiding a button. Each admin route and each admin database operation must re-read the role from the server's own record and refuse the request if it is not admin. Client-side checks are for showing and hiding UI, never for security.
  3. Ignore any role the request sends. The server should read role from your database using the authenticated user's ID, and treat any role or isAdmin field in the request body as noise. Never copy it into the record you save.
  4. Grant admin deliberately. Promote users through a manual step you control: a direct database change, or an admin-only endpoint that itself checks the caller is already an admin. New sign-ups should default to the lowest role.
  5. Re-run your own test. Repeat the request-tampering test from above. It should now do nothing.

The trap to avoid

The common wrong fix is to hide the admin controls in the front end and stop there: remove the button, or check isAdmin in the React code before rendering. That changes what people see, not what the server accepts. Anyone can call your API directly, without your UI, and the server will still honour the request. Hiding a control is not the same as enforcing a rule. The enforcement has to live on the server.

Where this fits

Self-granted admin is the kind of gap that never appears in a demo and shows up the moment a real user starts poking around, which is exactly what beta is for. The free Readiness Report checks whether your roles are server-enforced or browser-trusted, against your actual project, before someone finds out for you. If you want the fixes handed to you in order, that is the Finishing Pass. It sits alongside the same class of problem in can a regular user see someone else's account and changing the user ID in an API request.