CriticalSecurity

Auth Check Only in Client Code

The auth gate runs only in a client component (useEffect redirect or conditional render), which an attacker bypasses by disabling JavaScript or hitting the API route directly.

Typical error

Admin route protected only by client-side redirect

What this is

A common AI-generated pattern:

'use client'
import { useEffect } from 'react'
import { useRouter } from 'next/navigation'
import { useAuth } from '@/hooks/useAuth'
 
export default function AdminPage() {
  const { user } = useAuth()
  const router = useRouter()
 
  useEffect(() => {
    if (!user || !user.isAdmin) router.push('/')
  }, [user, router])
 
  if (!user?.isAdmin) return null
 
  return <AdminDashboard />
}

The render happens on the client, which means:

  • The page HTML ships to the visitor before auth runs
  • Disabling JavaScript shows the content
  • Any API route the admin page calls is not protected by this check

Why AI tools ship this

Client-side redirects are easier to write and debug. The generated code works visually (non-admins get bounced to /), so the tool declares victory.

How to detect

Look for auth-gated pages that are 'use client' components and rely on useEffect redirects. Also check that every server route the page calls enforces the same check.

How to fix

Enforce auth at the server boundary.

  1. Server component page: check the session in the page itself.

    import { redirect } from 'next/navigation'
    import { getServerSession } from '@/lib/auth'
     
    export default async function AdminPage() {
      const session = await getServerSession()
      if (!session?.user?.isAdmin) redirect('/')
      return <AdminDashboard />
    }
  2. API routes: every admin route must call the same check, not trust a header from the client.

  3. Middleware: for uniform enforcement across /admin/*, use middleware.ts to gate the whole subtree.

Return 404 instead of 403 for admin routes so the route's existence is not leaked to non-admins.

Commonly affected tools

Glossary

Is your app affected?

FinishKit checks for this finding and 50+ more across 8 dimensions of production readiness. Free during beta.

Scan your app