{"content":"# Better Auth Protected Routes\n\nAdd server-side route protection to enforce authentication on specific pages while keeping others public.\n\n### Core Pattern: Server-Side Session Check\n\nThe standard pattern for protecting pages uses server-side session validation with redirect:\n\n```tsx\nimport { redirect } from \"next/navigation\";\nimport { headers } from \"next/headers\";\nimport { auth } from \"@/lib/auth/server\";\n\nexport default async function ProtectedPage() {\n  const session = await auth.api.getSession({\n    headers: await headers(),\n  });\n\n  if (!session) {\n    redirect(\"/sign-in\");\n  }\n\n  return <div>Welcome, {session.user.name}</div>;\n}\n```\n\nThis pattern:\n\n- Runs entirely on the server (no client-side flash)\n- Redirects unauthenticated users before rendering\n- Provides the session data for use in the page\n\n---\n\n## Example: Public Landing Page\n\nCreate a public landing page with sign-in/sign-up buttons:\n\n```tsx\n// src/app/page.tsx\nimport Link from \"next/link\";\nimport { Button } from \"@/components/ui/button\";\n\nexport default function HomePage() {\n  return (\n    <div className=\"min-h-dvh flex flex-col items-center justify-center gap-8 p-4\">\n      <div className=\"text-center space-y-4\">\n        <h1 className=\"text-4xl font-bold tracking-tight\">\n          Welcome to Your App\n        </h1>\n        <p className=\"text-xl text-muted-foreground max-w-md\">\n          Sign in to access your dashboard and start using the app.\n        </p>\n      </div>\n      <div className=\"flex gap-4\">\n        <Link href=\"/sign-in\">\n          <Button variant=\"outline\" size=\"lg\">\n            Sign in\n          </Button>\n        </Link>\n        <Link href=\"/sign-up\">\n          <Button size=\"lg\">Get started</Button>\n        </Link>\n      </div>\n    </div>\n  );\n}\n```\n\n---\n\n## Example: Protected Chat Page\n\nMove your main app functionality to a protected route:\n\n```tsx\n// src/app/chats/page.tsx\nimport type { Metadata } from \"next\";\nimport { redirect } from \"next/navigation\";\nimport { headers } from \"next/headers\";\nimport Link from \"next/link\";\nimport { ChefHat } from \"lucide-react\";\nimport { auth } from \"@/lib/auth/server\";\nimport { getUserChats } from \"@/lib/chat/queries\";\nimport { ChatList } from \"@/components/chats/chat-list\";\nimport { UserMenu } from \"@/components/auth/user-menu\";\nimport { ThemeSelector } from \"@/components/themes/selector\";\n\nexport const metadata: Metadata = {\n  title: \"Your Chats\",\n  description: \"View and manage your AI conversations.\",\n};\n\nexport default async function ChatsPage() {\n  const session = await auth.api.getSession({\n    headers: await headers(),\n  });\n\n  if (!session) {\n    redirect(\"/sign-in\");\n  }\n\n  const chats = await getUserChats(session.user.id);\n\n  return (\n    <div className=\"min-h-screen bg-gradient-to-b from-background via-background to-muted/20\">\n      <header className=\"sticky top-0 z-50 border-b border-border/50 bg-background/80 backdrop-blur-xl\">\n        <div className=\"container mx-auto px-4 h-16 flex items-center justify-between\">\n          <div className=\"flex items-center gap-4\">\n            <Link\n              href=\"/\"\n              className=\"flex items-center gap-2 hover:opacity-80 transition-opacity\"\n            >\n              <div className=\"flex h-8 w-8 items-center justify-center rounded-md bg-primary\">\n                <ChefHat className=\"h-5 w-5 text-primary-foreground\" />\n              </div>\n              <span className=\"font-mono text-lg font-semibold tracking-tight\">\n                Your App\n              </span>\n            </Link>\n          </div>\n          <div className=\"flex items-center gap-2\">\n            <ThemeSelector />\n            <UserMenu />\n          </div>\n        </div>\n      </header>\n\n      <main className=\"container mx-auto px-4 py-8\">\n        <div className=\"mb-8\">\n          <h1 className=\"text-3xl font-bold tracking-tight\">Your Chats</h1>\n          <p className=\"text-muted-foreground mt-1\">\n            View and manage your conversations\n          </p>\n        </div>\n\n        <ChatList initialChats={chats} />\n      </main>\n    </div>\n  );\n}\n```\n\n---\n\n## Auth Pages: Redirect Authenticated Users\n\nAuth pages should redirect already-authenticated users away:\n\n```tsx\n// src/app/sign-in/page.tsx\nimport { redirect } from \"next/navigation\";\nimport { headers } from \"next/headers\";\nimport { auth } from \"@/lib/auth/server\";\nimport { SignIn } from \"@/components/auth/sign-in\";\n\nexport default async function SignInPage() {\n  const session = await auth.api.getSession({\n    headers: await headers(),\n  });\n\n  if (session) {\n    redirect(\"/chats\"); // Redirect to app when already signed in\n  }\n\n  return (\n    <main className=\"min-h-dvh flex items-center justify-center p-4\">\n      <SignIn />\n    </main>\n  );\n}\n```\n\n---\n\n## Route Structure\n\nA typical app has three types of routes:\n\n| Route Type | Example | Auth Behavior |\n| **Public** | `/`, `/pricing` | Anyone can access |\n| **Auth** | `/sign-in`, `/sign-up` | Redirect to app if signed in |\n| **Protected** | `/chats`, `/profile` | Redirect to sign-in if not signed in |\n\n### Example Directory Structure\n\n```\nsrc/app/\n  page.tsx              # Public landing page\n  pricing/page.tsx      # Public pricing page\n\n  sign-in/page.tsx      # Auth: redirects if signed in\n  sign-up/page.tsx      # Auth: redirects if signed in\n  forgot-password/page.tsx\n  reset-password/page.tsx\n  verify-email/page.tsx\n\n  chats/                # Protected: requires auth\n    page.tsx            # Chat list\n    [chatId]/page.tsx   # Individual chat\n\n  profile/page.tsx      # Protected: account settings\n```\n\n---\n\n## Loading User Data\n\nFor protected pages that need user-specific data, fetch it server-side after authentication:\n\n```tsx\nexport default async function DashboardPage() {\n  const session = await auth.api.getSession({\n    headers: await headers(),\n  });\n\n  if (!session) {\n    redirect(\"/sign-in\");\n  }\n\n  // Fetch user-specific data using the session\n  const [chats, subscription] = await Promise.all([\n    getUserChats(session.user.id),\n    getUserSubscription(session.user.id),\n  ]);\n\n  return (\n    <Dashboard user={session.user} chats={chats} subscription={subscription} />\n  );\n}\n```\n\n---\n\n## Callback URLs\n\nWhen redirecting to sign-in, you may want to return users to their original destination. The auth components support callback URLs:\n\n```tsx\n// In your SignIn component\nawait signIn.email({\n  email,\n  password,\n  callbackURL: \"/chats\", // Where to go after sign in\n});\n```\n\nFor dynamic redirect-back behavior, you can use search params:\n\n```tsx\n// Protected page: redirect with return URL\nif (!session) {\n  redirect(`/sign-in?returnTo=${encodeURIComponent(\"/chats/123\")}`);\n}\n\n// Sign-in page: read the return URL\nconst searchParams = await props.searchParams;\nconst returnTo = searchParams.returnTo || \"/chats\";\n\n// After sign in success\nrouter.push(returnTo);\n```\n\n---\n\n## Summary\n\n1. **Public pages** - No session check needed\n2. **Auth pages** - Redirect away if already signed in\n3. **Protected pages** - Redirect to sign-in if not authenticated\n4. Use `auth.api.getSession()` server-side for immediate protection without flash\n5. Fetch user-specific data after validating the session"}