---
name: using-authjs
description: Configures Auth.js v5 providers, protects routes with middleware, and safely accesses sessions in Server and Client Components. Use whenever writing authentication code in a Next.js App Router project.
---

# Using Auth.js (NextAuth v5)

Auth.js v5 is the standard authentication library for Next.js App Router. It
handles OAuth, magic links, and credential flows, encrypts session JWTs with
`AUTH_SECRET`, and exposes a session via both server-side `auth()` and client-side
`useSession()`.

## Initial setup

```bash
npm install next-auth@5
npx auth secret           # generates AUTH_SECRET, adds it to .env.local
```

`auth.ts` — the central Auth.js config (placed at the project root or `src/`):

```ts
// auth.ts
import NextAuth from 'next-auth'
import GitHub from 'next-auth/providers/github'
import Google from 'next-auth/providers/google'
import { DrizzleAdapter } from '@auth/drizzle-adapter'
import { db } from '@/db'

export const { handlers, signIn, signOut, auth } = NextAuth({
  adapter: DrizzleAdapter(db),    // omit if using JWT sessions only
  providers: [GitHub, Google],    // credentials inferred from AUTH_GITHUB_ID etc.
  callbacks: {
    session({ session, token }) {
      // Expose only the fields the client needs — never raw tokens
      if (token.sub) session.user.id = token.sub
      return session
    },
  },
})
```

## Environment variables

Auth.js infers OAuth credentials by convention — no manual `clientId` in code:

```bash
# .env.local
AUTH_SECRET="<generated by npx auth secret>"

AUTH_GITHUB_ID=Iv1.abc123
AUTH_GITHUB_SECRET=secret_abc

AUTH_GOOGLE_ID=123-abc.apps.googleusercontent.com
AUTH_GOOGLE_SECRET=GOCSPX-abc
```

**Never** commit real secret values. Use `.env.local` (git-ignored) for local
development; inject secrets via your hosting provider's secret manager in production.

## Route handler

```ts
// app/api/auth/[...nextauth]/route.ts
import { handlers } from '@/auth'
export const { GET, POST } = handlers
```

## Route protection with middleware

```ts
// middleware.ts (project root)
import { auth } from '@/auth'
import { NextResponse } from 'next/server'

export default auth((req) => {
  if (!req.auth && req.nextUrl.pathname.startsWith('/dashboard')) {
    return NextResponse.redirect(new URL('/login', req.url))
  }
})

export const config = {
  matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)'],
}
```

## Accessing the session

### Server Components (preferred for protected pages)

```tsx
// app/dashboard/page.tsx
import { auth } from '@/auth'
import { redirect } from 'next/navigation'

export default async function DashboardPage() {
  const session = await auth()
  if (!session) redirect('/login')

  return <h1>Hello {session.user?.name}</h1>
}
```

### Client Components

Wrap the root layout with `<SessionProvider>`:

```tsx
// app/layout.tsx
import { SessionProvider } from 'next-auth/react'

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html>
      <body>
        <SessionProvider>{children}</SessionProvider>
      </body>
    </html>
  )
}
```

Access in a Client Component:

```tsx
'use client'
import { useSession } from 'next-auth/react'

export function UserMenu() {
  const { data: session, status } = useSession()
  if (status === 'loading') return <Skeleton />
  if (!session) return <SignInButton />
  return <span>{session.user?.name}</span>
}
```

## Sign in / sign out

```tsx
// Server Action or client button
import { signIn, signOut } from '@/auth'

<form action={async () => { 'use server'; await signIn('github') }}>
  <button type="submit">Sign in with GitHub</button>
</form>

<form action={async () => { 'use server'; await signOut() }}>
  <button type="submit">Sign out</button>
</form>
```

## Drizzle adapter

When using `@auth/drizzle-adapter`, Auth.js creates its own tables (`users`,
`accounts`, `sessions`, `verificationTokens`). Add those to your schema or
use the adapter's exported schema helpers. Run `drizzle-kit generate` after
adding the adapter to capture the new tables in a migration.

## Hard rules

- **Never log** `session`, `token`, or any object that may contain `accessToken`,
  `refreshToken`, `email`, or session identifiers. Tokens in logs are a breach.
- `AUTH_SECRET` must be environment-variable only — never in source files.
- Use middleware for route protection — not page-level `if (!session) redirect()`.
- The `session()` callback must expose only the minimal fields the client needs.
  Do not propagate `token.accessToken` to the session unless the client absolutely
  requires it (and document why).
- Rotate `AUTH_SECRET` if it is ever exposed — existing sessions will be invalidated,
  requiring all users to sign in again.
