Technology6 min read

Secrets and API Keys in React and Supabase Apps How to Prevent Leaks and Rotate Safely

R
RileyAuthor
Secrets and API Keys in React and Supabase Apps How to Prevent Leaks and Rotate Safely

Secrets are the easiest way to break a “working” AI-generated app

React + Supabase is a fast, friendly stack—and it’s also a stack where secret handling is easy to get subtly wrong. AI-generated code makes this more likely: the app compiles, auth works, and then a key lands in a client bundle, a Git commit, a screenshot, or an error log. The result isn’t theoretical: leaked keys lead to database abuse, unexpected bills, and incident response work you didn’t plan for.

This playbook focuses on practical leakage prevention and safe rotation for AI-generated React + Supabase apps, including projects created with lovable.dev, where you can iterate quickly while still keeping secrets out of the frontend and out of Git.

Know what is and isn’t a secret in Supabase

Public keys you can ship

Supabase apps typically use a project URL and an anon key in the browser. That anon key is designed to be public. Treat it like a username, not a password: it’s only safe if your Row Level Security (RLS) policies are correct. If RLS is missing or permissive, a public anon key becomes a data-exfil path.

Keys that must never reach the browser

  • Service role key (bypasses RLS): keep server-side only.
  • Database password / direct Postgres credentials: never embed in apps, never log.
  • Third-party secrets (Stripe secret key, webhook signing secret, OAuth client secret): server-only.

If an AI tool generates a “quick fix” that puts the service role key into a React env var, treat it as an incident, not a refactor task.

The two rules that prevent most secret leaks

Rule 1: Anything in a React build is public

Client environment variables are not secret. Even if hidden behind a build step, anything bundled into the frontend can be read by users, proxies, browser devtools, or crawlers. Many leaks happen because developers confuse “env var” with “secret.” In the browser, an env var is just a convenient string substitution.

Rule 2: Data access must be constrained by policy, not key privacy

Assume your anon key is public and design around it. The real protection is:

  • RLS enabled on every table that can be reached from the client
  • Policies scoped to auth.uid() and explicit roles
  • Least-privilege columns and views (avoid select * patterns for sensitive tables)

Common AI-generated mistakes and how to catch them fast

Mistake: Service role key in the frontend

Symptom: code initializes Supabase with a service role key or uses server-only admin operations from React. Fix: move that logic to a server endpoint (serverless function, edge function, or backend) and only call it from the client.

Mistake: “Temporary debugging” that prints secrets

AI-generated troubleshooting often adds console.log(process.env…) or dumps config objects. These logs end up in hosted logs, error reporting, screen recordings, and shared tickets. Fix: remove logs, rotate exposed keys, and add a lint rule or pre-commit check to block process.env logging.

Mistake: Copy/paste from a dashboard screenshot

When building quickly, it’s easy to paste a key that was visible in a screenshot or a doc. Fix: treat any key that appeared in a shared artifact as compromised and rotate it.

A practical environment variable layout for React + Supabase

Client (React) variables

Keep only what you are comfortable publishing:

  • VITE_SUPABASE_URL (or framework equivalent)
  • VITE_SUPABASE_ANON_KEY

In code review, if you see anything like SERVICE_ROLE, POSTGRES, STRIPE_SECRET, or WEBHOOK_SECRET in frontend env vars, stop and redesign.

Server variables

These live in your server runtime (functions/edge/backend), not in the React build:

  • SUPABASE_SERVICE_ROLE_KEY
  • DATABASE_URL (if used)
  • STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET

Server code is where you should perform privileged writes, admin reads, and third-party API calls that require secrets.

Rotation playbook that doesn’t break production

1) Decide what triggered rotation

Rotation isn’t only for “confirmed leaks.” Rotate when:

  • a secret was committed to Git, even briefly
  • a secret appeared in logs, screenshots, support tickets, or issue trackers
  • a contractor or former teammate had access you can’t fully audit
  • you changed a hosting provider or CI system permissions

2) Inventory dependencies before changing anything

List every place the secret is used: production functions, preview deployments, local dev, CI, cron jobs, webhook endpoints. AI-generated apps often have “hidden” usage (a quick script, a one-off function, or an integration step) that will fail silently after rotation.

3) Perform a two-phase cutover when possible

For secrets that support multiple active versions (for example, webhook signing secrets or API keys in some providers), add the new secret first, deploy code that accepts both, then remove the old secret. Where dual-running isn’t possible, schedule a short maintenance window and prepare a rollback plan.

4) Rotate, redeploy, verify, then revoke

  • Rotate the credential at the provider
  • Update server env vars
  • Redeploy functions/backends
  • Run a verification checklist (auth, writes, webhooks, background jobs)
  • Revoke the old credential

Verification should include “negative” tests too: confirm the old key truly fails, not just that the new key works.

Leakage prevention that fits fast iteration

Block secrets before they land in Git

Use pre-commit scanning and CI checks to detect common token formats. Even lightweight scanning catches the majority of accidental commits. Also ensure .env files are ignored and avoid sharing them in chat tools.

Normalize tool inputs when using MCP or automations

Secrets leak when toolchains accept untrusted inputs and pass them through to logs, prompts, or API calls. If you’re adding custom connectors via MCP servers, adopt canonical schemas and input normalization to prevent “tool confusion” classes of issues. The same discipline that protects secrets also improves reliability in multi-step automation. For a deeper look at this defensive pattern, see Mitigating MCP Tool Confusion Attacks with Canonical Schemas and Input Normalization.

Keep production keys out of demos and support flows

AI builders encourage sharing context—screenshots, console outputs, export zips. Build a habit: use separate projects for demos, use scoped keys, and strip logs before sharing. If you need verifiable claims without exposing internals, structured “snippet packs” can help teams share evidence safely. Related: Ground-Truth Snippet Packs for Verifiable AI Claims.

RLS and backend boundaries that make public keys safe

A good target architecture is simple:

  • Frontend uses anon key and only accesses RLS-protected tables
  • Privileged operations run in server functions using service role
  • All third-party secret integrations happen server-side

When you generate apps quickly—especially in tools like Lovable that produce React + Supabase projects and can sync to GitHub early—the best security outcome is achieved by establishing these boundaries immediately and keeping the iteration loop inside them.

FAQ
How should I store Supabase keys in a lovable.dev React project?

If my Supabase anon key is public, what actually protects my data in lovable.dev apps?

What should I do if a service role key accidentally appears in the frontend of my lovable.dev app?

How often should I rotate secrets for a lovable.dev React + Supabase deployment?

Can I use Stripe with lovable.dev without exposing my secret key?