Fix Missing Authorization Checks in Vibe-Coded Apps
OverMCP Team
Quick answer
Missing authorization checks are one of the most common security holes in vibe-coded apps. AI tools like Cursor, Bolt.new, and v0 often generate code that checks if a user is authenticated but forgets to verify they have permission to access specific resources. Fix this by adding explicit role or ownership checks in every API route and server action that handles user data.
Why vibe-coded apps miss authorization
When you prompt an AI to "build a todo app with Next.js and Supabase," it typically generates authentication flows (login, signup, session management) flawlessly. But authorization—the logic that ensures User A can't delete User B's todos—is often completely absent. The AI focuses on making the app functional and aesthetically pleasing, not on enforcing access control boundaries.
I've seen this pattern across dozens of apps built with Cursor, Lovable, and Replit Agent. The AI assumes a single-user demo scenario and never adds checks like "is this todo owned by the current user?" When you deploy to production, any authenticated user can manipulate any record.
What to check first
Before you launch your app, run through this checklist:
userId against the record's owner_id?/todos/[id]: Is the ID parameter validated against the user's allowed resources?request.auth.uid to restrict reads/writes?Step-by-step fix
Let's fix a typical Next.js App Router app built with Cursor that uses Supabase. Suppose you have a route to delete a todo:
// ❌ Broken: no authorization check
export async function DELETE(request: Request, { params }: { params: { id: string } }) {
const supabase = createClient();
const { error } = await supabase.from('todos').delete().eq('id', params.id);
if (error) return Response.json({ error }, { status: 500 });
return Response.json({ success: true });
}This deletes any todo by ID, regardless of ownership. Here's the fix:
// ✅ Fixed: verify ownership before delete
export async function DELETE(request: Request, { params }: { params: { id: string } }) {
const supabase = createClient();
// Get authenticated user
const { data: { user } } = await supabase.auth.getUser();
if (!user) return Response.json({ error: 'Unauthorized' }, { status: 401 });
// Fetch the todo and check ownership
const { data: todo, error: fetchError } = await supabase
.from('todos')
.select('user_id')
.eq('id', params.id)
.single();
if (fetchError || !todo) return Response.json({ error: 'Not found' }, { status: 404 });
if (todo.user_id !== user.id) return Response.json({ error: 'Forbidden' }, { status: 403 });
// Now safe to delete
const { error } = await supabase.from('todos').delete().eq('id', params.id);
if (error) return Response.json({ error }, { status: 500 });
return Response.json({ success: true });
}For Supabase RLS, enable it and write policies:
-- Enable RLS
ALTER TABLE todos ENABLE ROW LEVEL SECURITY;
-- Policy: users can only delete their own todos
CREATE POLICY "delete_own_todos" ON todos FOR DELETE
USING (auth.uid() = user_id);Then in your client code, bypass any client-side filters that might be bypassed:
// Client-side: let RLS handle authorization server-side
const { error } = await supabase.from('todos').delete().eq('id', todoId);Common mistakes
How to automate authorization checks
Manually auditing every route is tedious. Use a free AI app security scanner to automatically detect missing authorization checks across your codebase. It hooks into your GitHub repo or CI/CD pipeline and flags routes that lack ownership validation, role checks, or proper RLS policies.
For continuous protection, set up continuous security monitoring to catch new authorization gaps as you iterate with AI.
FAQ
What is the difference between authentication and authorization?
Authentication verifies who you are (login), while authorization determines what you're allowed to do (access control). Vibe-coded apps often implement authentication well but forget authorization entirely.
Can Supabase RLS alone fix missing authorization?
RLS is a powerful tool, but it only works if you enable it and write correct policies. It also doesn't cover business logic that RLS can't express (e.g., rate limits, multi-step workflows). Always combine RLS with server-side checks in API routes.
How can I find missing authorization checks in my app?
Manually review every route that accesses user-specific data. Look for patterns where you fetch a record by ID without verifying ownership. Automated tools like OverMCP can scan your codebase and highlight routes missing authorization logic.