Fix guide · high · jwt_admin_in_client

Hardcoded JWT with privileged claims in client bundle

What this rule means

A JWT containing privileged claims (role: "admin", is_admin: true, service_role, etc.) is hardcoded in your client JavaScript. Every visitor can extract this token and present it to your backend to impersonate an admin or service identity.

Why it matters

The pattern shows up consistently in vibe-coded apps: a developer needs an admin-shaped token to test a feature, generates one with a long expiration, drops it into the client code as a temporary fixture, and forgets to remove it before launch. The token survives the build process and ships. Until the signing secret rotates, every visitor has admin capability.

This is the JWT analogue of the Supabase service_role key leak: the credential is supposed to be server-only, ended up in the client bundle, and bypasses your authorization model.

How to fix it

Treat this as an active compromise.

  1. Rotate the signing secret immediately. Console for your auth provider, or your environment variables for self-rolled JWT auth. This invalidates the leaked token (and every other token signed with the old secret — accept the user-facing logout).
  1. Remove the hardcoded token from source. git log -S "<first-12-chars-of-token>" finds the commit. Delete the line. Push.
  1. Audit access logs for usage of the leaked token. Check your auth provider's audit log, or your backend's request logs, for requests carrying the token. Any action taken with admin claims that wasn't yours should be reviewed.
  1. Stop generating long-lived admin tokens for testing. For local dev, mint fresh short-lived tokens through the same flow real admins use. For CI tests, use a per-test fixture user with a per-test token minted at test setup.
  1. Add a build-time guard. Reject any JWT pattern in the client bundle that decodes to a privileged role. grep -E "role.*admin" dist/**/*.js or a custom rollup/vite plugin.

Full guide: /blog/jwt-mistakes-vibe-coded.

Did vibecheck flag this on your app?

If you reached this page from a vibecheck inspection report, the redacted match in your scan output is the exact string we found in your bundle. After applying the fix above, run the inspection again — the finding should clear.

Run another inspection