new: drive vas from your AI agent over MCP · Cursor, Claude Code, Windsurf
Claude Code
+
Supabase

Claude Code + Supabase Security

Claude Code is a capable Supabase partner — it understands RLS syntax and can write solid policies. The risks come from skipping the security prompts or trusting generated code without review.

Why Claude Code + Supabase?

Claude Code's ability to read files and execute commands makes it effective for Supabase migrations, RLS policy generation, and TypeScript client code. Developers increasingly use it end-to-end for database-backed features.

Common Vulnerabilities

These are the security issues we find most often in Claude Code apps using Supabase.

high

Context Window Contains Live Credentials

When Claude Code reads .env files to understand configuration, the service_role key enters the AI's context. If those sessions are shared or logged, credentials can leak.

high

Generated Policies Skip Ownership Checks

Claude Code may generate RLS policies that only check authentication (auth.uid() IS NOT NULL) without verifying the record belongs to the requesting user.

critical

Service Key in Server Actions

When generating Next.js Server Actions or API routes, Claude Code may use the service_role key in code that is later inadvertently included in client bundles.

medium

Missing auth_rls_initplan Optimisation

Claude Code often writes auth.uid() directly in RLS policies rather than (select auth.uid()), causing repeated auth evaluation and potential performance issues at scale.

What We Check for Claude Code + Supabase

RLS Policy Correctness

Verify every generated policy includes both an auth check and a user_id ownership check, not just one or the other.

Key Placement Audit

Confirm service_role key only appears in server-side files (API routes, server actions) and never in components or pages rendered client-side.

Migration File Review

Check Claude Code-generated migration SQL for ENABLE ROW LEVEL SECURITY on every new table before running.

Auth Helper Usage

Ensure generated code uses @supabase/ssr or @supabase/auth-helpers-nextjs for server-side auth, not the browser client.

Quick Security Wins

Apply these fixes right now to improve your security.

Prompt Claude Code explicitly: 'Write RLS policies that check both auth and ownership for each table'
Use (select auth.uid()) instead of auth.uid() in all RLS policies to prevent initplan re-evaluation
Add SUPABASE_SERVICE_ROLE_KEY only to server-side .env.local, never to NEXT_PUBLIC_ prefixed variables
Ask Claude Code to generate a migration that enables RLS and creates policies in a single step
Run the generated migration in a development Supabase project before applying to production

The Bottom Line

Claude Code writes competent Supabase code when prompted correctly. Always ask it to include RLS enablement and ownership-scoped policies — never assume it will add them without being asked.

Secure Your Claude Code + Supabase App

Find Row Level Security misconfigurations, exposed credentials, and other vulnerabilities before attackers do.

Start Security Scan

Frequently Asked Questions

Can Claude Code write correct Supabase RLS policies?

Yes, Claude Code understands Supabase RLS syntax well and can write effective policies when explicitly asked. The issue is that it often generates working queries first and adds security as a separate step — or skips it entirely if not prompted. Always include security requirements in your initial prompt.

Is it safe to let Claude Code read my .env file?

The anon key is designed to be public and is safe. The service_role key should be handled carefully — avoid pasting service_role key values into chat. Use environment variable references instead and let Claude Code read the structure, not the values.

How do I ensure Claude Code uses the right Supabase client for server vs client?

Tell Claude Code your framework (e.g. Next.js App Router) and ask it to use createServerClient from @supabase/ssr for server components and API routes, and createBrowserClient for client components. The browser client should only ever receive the anon key.

What is the auth_rls_initplan warning and does it matter?

When RLS policies call auth.uid() directly, Supabase evaluates it once per row. Using (select auth.uid()) evaluates it once per query. For tables with many rows this is a significant performance difference. Ask Claude Code to always use the (select auth.uid()) pattern.