Skill v1.0.1
currentAutomated scan100/1003 files
version: "1.0.1" name: nextauth description: >- Configure NextAuth.js v5 authentication with OAuth providers and Prisma adapter. Use when setting up login/logout, protecting routes, accessing sessions in components, adding OAuth providers, or troubleshooting authentication issues. license: MIT compatibility: [Claude Code] metadata: author: ftcmetrics version: "1.0.0" category: auth
NextAuth.js v5 Authentication Guide
NextAuth.js v5 (Auth.js) is the authentication library for FTC Metrics. It uses OAuth providers (Google, Discord, GitHub) with a Prisma database adapter for session persistence.
Quick Start
1. Install Dependencies
bun add next-auth@beta @auth/prisma-adapter
2. Create Auth Configuration
Create src/lib/auth.ts:
import NextAuth from "next-auth";import { PrismaAdapter } from "@auth/prisma-adapter";import Google from "next-auth/providers/google";import Discord from "next-auth/providers/discord";import GitHub from "next-auth/providers/github";import { prisma } from "@ftcmetrics/db";export const { handlers, auth, signIn, signOut } = NextAuth({adapter: PrismaAdapter(prisma),providers: [Google({clientId: process.env.GOOGLE_CLIENT_ID!,clientSecret: process.env.GOOGLE_CLIENT_SECRET!,}),Discord({clientId: process.env.DISCORD_CLIENT_ID!,clientSecret: process.env.DISCORD_CLIENT_SECRET!,}),GitHub({clientId: process.env.GITHUB_CLIENT_ID!,clientSecret: process.env.GITHUB_CLIENT_SECRET!,}),],pages: {signIn: "/login",error: "/login",},callbacks: {async session({ session, user }) {if (session.user) {session.user.id = user.id;}return session;},},session: {strategy: "database",},});
3. Create API Route Handler
Create src/app/api/auth/[...nextauth]/route.ts:
import { handlers } from "@/lib/auth";export const { GET, POST } = handlers;
4. Add Session Provider
Create src/components/providers.tsx:
"use client";import { SessionProvider } from "next-auth/react";export function Providers({ children }: { children: React.ReactNode }) {return <SessionProvider>{children}</SessionProvider>;}
Wrap your app in src/app/layout.tsx:
import { Providers } from "@/components/providers";export default function RootLayout({ children }: { children: React.ReactNode }) {return (<html lang="en"><body><Providers>{children}</Providers></body></html>);}
Prisma Schema Requirements
Required models for NextAuth with database sessions:
model User {id String @id @default(cuid())name String?email String? @uniqueemailVerified DateTime? @map("email_verified")image String?createdAt DateTime @default(now()) @map("created_at")updatedAt DateTime @updatedAt @map("updated_at")accounts Account[]sessions Session[]@@map("users")}model Account {id String @id @default(cuid())userId String @map("user_id")type Stringprovider StringproviderAccountId String @map("provider_account_id")refresh_token String? @db.Textaccess_token String? @db.Textexpires_at Int?token_type String?scope String?id_token String? @db.Textsession_state String?user User @relation(fields: [userId], references: [id], onDelete: Cascade)@@unique([provider, providerAccountId])@@map("accounts")}model Session {id String @id @default(cuid())sessionToken String @unique @map("session_token")userId String @map("user_id")expires DateTimeuser User @relation(fields: [userId], references: [id], onDelete: Cascade)@@map("sessions")}
Environment Variables
Add to .env:
# NextAuthAUTH_SECRET="generate-with-openssl-rand-base64-32"# Google OAuthGOOGLE_CLIENT_ID="your-google-client-id"GOOGLE_CLIENT_SECRET="your-google-client-secret"# Discord OAuthDISCORD_CLIENT_ID="your-discord-client-id"DISCORD_CLIENT_SECRET="your-discord-client-secret"# GitHub OAuthGITHUB_CLIENT_ID="your-github-client-id"GITHUB_CLIENT_SECRET="your-github-client-secret"
Generate AUTH_SECRET:
openssl rand -base64 32
Session Management Patterns
Server Components (Recommended)
Use the auth() function directly in Server Components:
import { auth } from "@/lib/auth";import { redirect } from "next/navigation";export default async function DashboardPage() {const session = await auth();if (!session?.user) {redirect("/login");}return <div>Welcome, {session.user.name}</div>;}
Protected Layouts
Protect entire route groups with layout-level auth:
import { auth } from "@/lib/auth";import { redirect } from "next/navigation";export default async function ProtectedLayout({children,}: {children: React.ReactNode;}) {const session = await auth();if (!session?.user) {redirect("/login");}return <>{children}</>;}
Client Components
Use the useSession hook in Client Components:
"use client";import { useSession, signOut } from "next-auth/react";export function UserMenu() {const { data: session, status } = useSession();if (status === "loading") {return <div>Loading...</div>;}if (!session?.user) {return <a href="/login">Sign in</a>;}return (<div><img src={session.user.image} alt={session.user.name} /><span>{session.user.name}</span><button onClick={() => signOut({ callbackUrl: "/" })}>Sign out</button></div>);}
Sign In with Providers
"use client";import { signIn } from "next-auth/react";export function LoginButtons() {return (<div><button onClick={() => signIn("google", { callbackUrl: "/dashboard" })}>Continue with Google</button><button onClick={() => signIn("discord", { callbackUrl: "/dashboard" })}>Continue with Discord</button><button onClick={() => signIn("github", { callbackUrl: "/dashboard" })}>Continue with GitHub</button></div>);}
Adding User ID to Session
The session callback extends the session with the database user ID:
callbacks: {async session({ session, user }) {if (session.user) {session.user.id = user.id;}return session;},},
Access the user ID in components:
// Server Componentconst session = await auth();const userId = session?.user?.id;// Client Componentconst { data: session } = useSession();const userId = session?.user?.id;
OAuth Provider Setup
- Go to Google Cloud Console
- Create OAuth 2.0 credentials
- Add authorized redirect URI:
http://localhost:3000/api/auth/callback/google
Discord
- Go to Discord Developer Portal
- Create application and OAuth2 credentials
- Add redirect URI:
http://localhost:3000/api/auth/callback/discord
GitHub
- Go to GitHub Developer Settings
- Create OAuth App
- Add callback URL:
http://localhost:3000/api/auth/callback/github
Common Pitfalls
Session Not Available
- Ensure
SessionProviderwraps your entire app in the root layout - Ensure the API route exists at
app/api/auth/[...nextauth]/route.ts
User ID Missing from Session
- Add the session callback to include
user.id - When using database strategy, user info comes from the
userparameter, nottoken
Database Session Issues
- Ensure Prisma schema matches NextAuth requirements exactly
- Use
onDelete: Cascadeon relations to handle user deletion - Run
prisma migrate devafter schema changes
OAuth Callback Errors
- Verify redirect URIs match exactly in provider console
- Include the full path:
/api/auth/callback/[provider] - For production, update URIs to use HTTPS and your domain
AUTH_SECRET Missing
- Generate with
openssl rand -base64 32 - Required in production, auto-generated in development
TypeScript Type Extensions
Extend session types in src/types/next-auth.d.ts:
import { DefaultSession } from "next-auth";declare module "next-auth" {interface Session {user: {id: string;} & DefaultSession["user"];}}
File Structure Reference
packages/web/src/lib/auth.ts # NextAuth configurationcomponents/providers.tsx # SessionProvider wrapperapp/api/auth/[...nextauth]/route.ts # API route handlerlayout.tsx # Root layout with Providerslogin/page.tsx # Login page (server)login-form.tsx # OAuth buttons (client)dashboard/layout.tsx # Protected layoutpage.tsx # Uses auth() for session