The admin button was hidden. The admin data wasn't.

In this controlled demo, regular users never see the admin button, but the admin endpoint only checks that someone is logged in. A regular user can still call it.

Technical name: Missing role check on the serverControlled demo · fake data

Is hiding the admin button enough to protect admin pages?

No. Hiding a button only changes what people see. If the admin endpoint behind it only checks that someone is logged in, any logged-in user can call it directly and get the admin data. The server has to check the user's role on every protected request.

How to tell if your app has this

No code needed.

  1. Log in as a normal test user, open your browser's developer tools (Network tab) and note the addresses your admin pages call when an admin uses them.
  2. As the normal user, open one of those addresses directly. If data comes back instead of an error, the role is not checked.
  3. Ask your AI tool: "For every admin API route, show me where the server checks the user's role."

Before and after

Before
Signed in as: member
Admin report · hidden
GET /api/admin/report
200 OK

A regular member got the admin report: 200 OK.

After
Signed in as: member
Admin report · hidden
GET /api/admin/report
403

The server checks the role on every protected request.

Why AI-built apps end up with this

When you ask for an admin panel, AI tools usually build the screen and hide it from non-admins, because that is what you can see. The API route behind it gets a login check so it works, and the role check is easy to miss because nothing looks wrong in the app.

Fix it yourself

How do I fix it? Paste this into your AI tool

My app hides the admin button for regular users, but the admin API endpoints only check that someone is logged in. Add a server-side role check to every admin endpoint: read the user's role from the server session or database (never from the request body, client headers or localStorage), return 401 for signed-out users and 403 for non-admins.
Works with Lovable, Bolt, Cursor, Replit and similar tools. Then run the check-up below on your own app.

How we checked the fix

Same request before and after the fix
Same requestBeforeAfter
Anonymous401401
Regular member200 (admin data)403
Real admin200200
Member sending a fake admin role200403

Results from our demo test, run against the same demo before and after the fix.

Common mistakes when fixing this

  • Hiding a button changes what people see, not what the server allows.

Questions people ask

Can I check the role in the browser instead?

No. Anything in the browser, including localStorage, cookies you set yourself and request bodies, can be changed by the user. Read the role from your server session or database.

What status should a non-admin get?

401 when nobody is logged in, 403 when a logged-in user lacks permission. In our demo test the fixed endpoint returned 401, 403 and 200 for anonymous, member and admin.

Does this apply to every protected action, not just pages?

Yes. Every endpoint that reads or changes admin-only data needs its own server-side role check, including exports, deletes and settings.

For developers: the cause and the fix in code
− if not user: return 401
+ if not user: return 401
+ if session_role(user) != "admin":
+     return 403

Simplified. Your stack will look different; the principle is the same.

Learn more