Vibe Code an Admin Panel (Roles, Permissions, Audit Logs)

Build an internal admin tool with AI — and get the role checks right, because an admin panel is the highest-value target in your app.

C
CodeIllusion Team
#vibe-coding #admin-panel #saas #security #tutorial
Vibe Code an Admin Panel (Roles, Permissions, Audit Logs)

An admin panel is the most privileged surface in your application. It reads everyone’s data and usually modifies it. Which makes it the one place where the standard vibe coding trade — ship fast, review later — is a bad idea.

The build is straightforward. The access control is the whole job.

The Mistake This Article Exists For

Ask an AI for an admin panel and you’ll get something like this:

{user.role === 'admin' && <AdminNav />}     // hides the link

The nav is hidden. The route still works. The API still responds. Anyone who types /admin/users gets the admin panel.

Hiding UI is not access control. It’s a cosmetic layer over an unprotected system.

Roles First

Add a role column to the users table: ‘user’ | ‘admin’ | ‘support’, defaulting to ‘user’. Roles must be readable only server-side — never expose the role table to the client via RLS.

Create a server-side requireRole(role) helper that reads the session, looks up the role from the database, and throws if it doesn’t match. Use it in every admin page and every admin API route.

Do not rely on client-side checks or JWT claims for authorization.

Reading the role from the database on each request costs a query and removes an entire class of bug. Roles cached in a token stay valid after you revoke them.

Protect Routes and Endpoints Separately

Two layers, both required:

Protect /admin/* with middleware that verifies the admin role server-side before rendering.

Separately, every /api/admin/* route must independently call requireRole(‘admin’). Middleware is not sufficient — routes must be safe if called directly.

Then verify by hand. Log in as a normal user and request an admin API route directly:

curl -i localhost:3000/api/admin/users -H "Cookie: $NORMAL_USER_SESSION"

403 or it isn’t protected.

What the Panel Does

Build /admin with: a users table (email, signup date, plan, status) with search and pagination; a user detail page showing their records; the ability to change a user’s plan and suspend an account.

All queries server-side. Paginate at 50. Search must run in the database, not by filtering fetched rows.

Note that admin queries deliberately don’t filter by user ID — that’s the point of an admin panel. Which is exactly why the role check has to be right: it’s the only thing standing between a normal user and everyone’s data.

Audit Logs

The moment an admin can change something, you need to know who changed what.

Create an audit_log table: id, actor_user_id, action, target_type, target_id, metadata jsonb, ip_address, created_at.

Log every admin write — plan changes, suspensions, deletions, impersonation. Include before and after values in metadata. The log must be append-only: no updates or deletes, enforced by RLS policy.

Append-only matters. A log an admin can edit is not evidence of anything.

Then surface it:

Add /admin/audit showing the log with filters by actor, action, and date range.

Impersonation, If You Must

Genuinely useful for support. Also the most dangerous thing in your app.

Add support impersonation. Only ‘admin’ role may impersonate, and never another admin or support user. Store the real admin ID alongside the impersonated session. Show a persistent, non-dismissible banner during impersonation with a one-click exit. Log start and end with timestamps. Impersonated sessions expire after 30 minutes. Impersonation must never be able to change passwords, email addresses, or billing.

That final restriction is the one that keeps impersonation from becoming account takeover.

Destructive Actions

Deletions must be soft deletes with a deleted_at column, never hard deletes. Require typing the user’s email to confirm. Log every deletion. Add an admin-only restore.

Hard deletes in an admin panel plus one misclick is unrecoverable data loss.

The Checklist

  • Role stored server-side, read from the database per request
  • requireRole called in every admin page and every admin API route
  • Middleware protection plus per-route checks
  • Verified: normal user gets 403 calling admin APIs directly
  • Verified: logged-out request gets 401
  • Role table not exposed to client via RLS
  • Audit log on every admin write, append-only
  • Soft deletes with typed confirmation
  • Impersonation banner, expiry, and logging (if implemented)
  • Impersonation can’t change credentials or billing
  • Admin actions rate limited

Two Habits Worth Keeping

Test as a normal user, not as yourself. Keep a permanent non-admin test account. Every time you add an admin feature, try to reach it from that account. This catches more than any amount of reading.

Assume the panel will be found. /admin is the first path anyone tries. Obscurity buys you nothing; the role check is the only thing that matters.

Next: Vibe code a SaaS dashboard for the user-facing equivalent, or vibe coding security for the full audit.

Related reading:

Frequently Asked Questions

How do I add admin roles to an AI-built app? +

Store the role in the database on the user record or a separate roles table — never in a client-readable field, a cookie, or JWT claims you can't verify server-side. Check the role server-side on every admin route and every admin API endpoint.

What's the biggest risk with an AI-generated admin panel? +

Hiding admin UI without protecting admin endpoints. AI reliably conditionally renders the admin nav for non-admins, and reliably leaves the underlying API routes callable by anyone who knows the URL.

Do I need audit logs in an admin panel? +

Yes, as soon as more than one person has access, and always if admins can modify user data. Without a log you can't answer who changed what — which matters both for debugging and for any compliance obligation.

Should admins be able to impersonate users? +

It's genuinely useful for support, and it's the single most dangerous feature you can build. If you add it, log every session, show a persistent banner during impersonation, and never allow impersonating another admin.

Tagged:

#vibe-coding #admin-panel #saas #security #tutorial

Enjoyed this article?

Get more AI tool picks, coding tutorials, and no-code automation guides every week. No spam, ever.

Found this useful? Share it:

More in AI Coding Tools