Bolt, v0, and Replit Apps: The Auth and RLS Mistakes We Keep Seeing
In apps generated by Bolt.new, v0.dev, and Replit Agent, authentication often creates a false sense of security while leaving database tables exposed.

In AI-generated applications built with Bolt.new, v0.dev, and Replit Agent, authentication mechanisms frequently suffer from a fatal disconnect: the UI reflects a logged-in user while the database is completely open or improperly restricted. AI coding assistants frequently introduce four critical authorization flaws: permissive USING (true) RLS policies generated to bypass errors during prototyping; relying on insecure client-side supabase.auth.getSession() without server verification via supabase.auth.getUser(); database views that silently bypass RLS because they lack security_invoker = true; and Route Handlers lacking tenant organization scope.
When an AI model generates database schemas and authentication hooks, it prioritizes error-free execution over defensive authorization boundaries. If a query fails during prototyping because a policy blocks access, the AI often responds by widening permissions or removing filters altogether.
The short version
Securing authentication in an AI-generated app requires moving authorization enforcement from client components into PostgreSQL Row-Level Security and server-verified identity checks:
- Never deploy
USING (true)policies on tables containing customer records; enforce explicit tenant predicates such asauth.uid() = user_id. - Do not trust client-read sessions via
getSession(); verify caller identity on the server usingsupabase.auth.getUser(). - Configure all PostgreSQL views with
WITH (security_invoker = true)so they inherit the querying user's RLS rules rather than the view creator's permissions. - Scope API Route Handlers and server mutations to explicit organization or tenant identifiers extracted directly from authenticated session claims.
Review our AI-generated code audit checklist and sample AI code rescue audit report to benchmark your database authorization posture.
1. The Permissive USING (true) RLS Antipattern
When AI coding assistants generate database initialization scripts, they frequently enable Row-Level Security but immediately neutralize it with permissive catch-all policies.
The vulnerability
During rapid prototyping, AI assistants often write policies like this:
-- DANGEROUS: Permissive policy allows any authenticated user to view all rows
CREATE POLICY "Allow authenticated users full access"
ON public.organizations
FOR ALL
TO authenticated
USING (true)
WITH CHECK (true);
While this eliminates permission errors during initial user testing, USING (true) means that any user with a valid login token can read, modify, or delete every other user's organization data simply by requesting it via the client SDK or direct API call.
How to audit and fix
Run this diagnostic query in your PostgreSQL database to find all public tables with overly permissive policies:
SELECT
tablename,
policyname,
cmd,
qual,
with_check
FROM pg_policies
WHERE schemaname = 'public'
AND (qual = 'true' OR with_check = 'true');
Replace permissive rules with explicit tenant-scoped policies. For multi-tenant tables, ensure operations check that the authenticated user owns or belongs to the target row:
-- SECURE: Restrict row access to the authenticated user
ALTER TABLE public.user_profiles ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Users can manage their own profile"
ON public.user_profiles
FOR ALL
TO authenticated
USING (auth.uid() = user_id)
WITH CHECK (auth.uid() = user_id);
For detailed policy syntax, refer to the PostgreSQL Row Security Policies Documentation.
2. Insecure Client Sessions vs Server getUser() Verification
A frequent architectural bug in AI-generated Next.js and Remix applications is trusting client-supplied session tokens in server-rendered components.
The vulnerability
AI code generators frequently write server handlers that read sessions directly:
// INSECURE: getSession() does not revalidate the JWT against the auth server
const { data: { session } } = await supabase.auth.getSession();
const userId = session?.user?.id; // Insecure: token signature may be expired or revoked
In serverless environments, getSession() simply decodes the JWT from local storage or cookies without validating whether the token has been revoked, altered, or issued to a banned account.
How to audit and fix
Always authenticate requests using supabase.auth.getUser(), which makes a server-side call to verify the token signature and account validity against the auth backend:
import { createServerClient } from "@supabase/ssr";
import { cookies } from "next/headers";
export async function POST(request: Request) {
const cookieStore = await cookies();
const supabase = createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
cookies: {
getAll() {
return cookieStore.getAll();
},
setAll(cookiesToSet) {
try {
cookiesToSet.forEach(({ name, value, options }) =>
cookieStore.set(name, value, options)
);
} catch {
// Middleware handles session refresh in server components
}
},
},
}
);
// SECURE: getUser() verifies token integrity with the auth service
const { data: { user }, error: authError } = await supabase.auth.getUser();
if (authError || !user) {
return Response.json({ error: "Unauthorized" }, { status: 401 });
}
// Safe to proceed with authenticated user ID
return Response.json({ success: true, userId: user.id });
}
Consult the Supabase Server-Side Auth Guide for comprehensive implementation details across App Router layouts and route handlers.
3. Database Views Silently Bypassing Row-Level Security
AI tools often generate PostgreSQL views to join multiple tables for dashboard reporting or search interfaces.
The vulnerability
By default in PostgreSQL (prior to version 15, or without explicit flags), views execute with the permissions of the view creator (security_definer behavior). When a client queries a view that joins tables with RLS enabled, PostgreSQL checks permissions against the creator, completely bypassing the Row-Level Security policies intended to protect the underlying tables.
How to audit and fix
In PostgreSQL 15 and above, declare all client-facing views with WITH (security_invoker = true). This forces PostgreSQL to evaluate the querying user's active RLS policies when evaluating the view:
-- SECURE: Explicitly enforce querying user's RLS policies on the view
CREATE OR REPLACE VIEW public.customer_dashboard_overview
WITH (security_invoker = true)
AS
SELECT
c.id AS customer_id,
c.name,
c.user_id,
o.id AS order_id,
o.total_amount
FROM public.customers c
JOIN public.orders o ON o.customer_id = c.id;
To find existing views that lack security_invoker, query the PostgreSQL catalog:
SELECT
c.relname AS view_name,
c.reloptions
FROM pg_class c
JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE c.relkind = 'v'
AND n.nspname = 'public';
Ensure security_invoker=true is present in reloptions for all user-accessible views.
4. Multi-Tenant Tenancy Scope Omission in Server Mutations
When building B2B SaaS features, AI code generators routinely miss organization-level authorization checks in API endpoints.
The vulnerability
An AI generator creates an update endpoint for updating project settings. It accepts project_id in the URL or payload, verifies that the caller is logged in, but fails to check whether the authenticated user actually belongs to the organization that owns that project.
// INSECURE: User is authenticated, but tenancy is never checked
export async function PATCH(request: Request) {
const { user } = await getAuthenticatedUser();
const { projectId, newTitle } = await request.json();
// Flaw: Updates project without verifying project belongs to user's tenant!
await db.update(projects).set({ title: newTitle }).where(eq(projects.id, projectId));
return Response.json({ success: true });
}
This vulnerability is classified as Broken Object Level Authorization (BOLA), the number one API vulnerability in the OWASP API Security Top 10.
How to audit and fix
Always scope update and delete mutations to the authenticated tenant. In PostgreSQL, enforce this with compound RLS checks:
-- Restrict updates to projects within organizations the user belongs to
CREATE POLICY "Users can update projects in their organization"
ON public.projects
FOR UPDATE
TO authenticated
USING (
organization_id IN (
SELECT organization_id
FROM public.organization_members
WHERE user_id = auth.uid()
)
);
In server handlers, always validate that the resource belongs to the active tenant before executing mutations.
10-Minute PostgreSQL Security Audit Script
Before launching any AI-generated prototype to production, run this audit script against your database to verify foundational security invariants:
-- Check 1: Tables lacking Row-Level Security
SELECT tablename
FROM pg_tables
WHERE schemaname = 'public'
AND rowsecurity = false;
-- Check 2: Policies with trivial pass-through rules
SELECT tablename, policyname, qual
FROM pg_policies
WHERE schemaname = 'public'
AND qual = 'true';
-- Check 3: Public views lacking security_invoker
SELECT c.relname AS view_name
FROM pg_class c
JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE c.relkind = 'v'
AND n.nspname = 'public'
AND (c.reloptions IS NULL OR NOT ('security_invoker=true' = ANY(c.reloptions)));
If any table or view returns results in checks 1 through 3, remediate the policies before deploying to production.
Where to go next
Securing authentication and authorization is the foundational step in preparing AI-generated applications for real users. For a broader overview of technical risks, read our guide on 12 critical security flaws in vibe-coded apps, or explore our runbook for fixing a broken vibe-coded app. If you are preparing to transition prototypes from tools like Lovable, review our checklist on Lovable apps moving to production, or inspect our security auditing guide for Cursor-built codebase security reviews.
When complex tenancy models or production launch deadlines require specialized hands-on engineering, Aatvi's AI Code Rescue engagement audits your database security, closes authorization bypasses, and establishes reliable CI verification gates.
Source notes
- The PostgreSQL Row Security Policies Documentation explains RLS syntax, policy evaluation, and security invoker views.
- The Supabase Server-Side Auth Guide outlines server client creation and the distinction between
getUser()andgetSession(). - The OWASP API Security Top 10 details Broken Object Level Authorization (API1:2023) and authorization bypass mechanics.
- The Carnegie Mellon University Software Engineering Institute provides guidance on authorization boundary enforcement and secure system design.
Start here: AI Code Rescue audit and stabilization.
