Skip to main content

Next.js adapter

WIP

Next.js App Router. Catch-all [...auth]/route.ts mount, plus standalone Web-Fetch route handlers.

Catch-all mount

The simplest mount is a single catch-all route handler that dispatches to the right duck-auth handler:

app/api/auth/[...auth]/route.ts
import { authMountNext } from '@gentleduck/auth/server/next'
import { auth } from '~/lib/auth'

export const { GET, POST } = authMountNext(auth)

This installs handlers for signin, signout, session, and providers/:providerId/begin under /api/auth/.... Edge runtime is supported - duck-auth is Web-Fetch native.

Individual handlers

If you need per-route control (different rate-limit middleware, custom logging), mount each handler separately:

app/api/auth/signin/route.ts
import { authNextSignIn } from '@gentleduck/auth/server/next'
import { auth } from '~/lib/auth'

export const POST = authNextSignIn(auth)
app/api/auth/signout/route.ts
import { authNextSignOut } from '@gentleduck/auth/server/next'
export const POST = authNextSignOut(auth)
app/api/auth/session/route.ts
import { authNextSession } from '@gentleduck/auth/server/next'
export const GET = authNextSession(auth)
app/api/auth/providers/[providerId]/begin/route.ts
import { authNextProviderBegin } from '@gentleduck/auth/server/next'

export async function POST(req: Request, { params }: { params: { providerId: string } }) {
  return authNextProviderBegin(auth, params.providerId)(req)
}

OAuth callbacks

Each OAuth provider redirects back to a callback URL. Mount the sign-in handler under each callback path so the redirect resolves cleanly:

app/api/auth/google/callback/route.ts
import { authNextSignIn } from '@gentleduck/auth/server/next'
export const GET = authNextSignIn(auth)

The provider extracts code, state, and the PKCE verifier from the query string and the cookie, and emits the session intent.

Server components

In server components and route handlers, resolve the session by passing the Request directly:

app/account/page.tsx
import { headers } from 'next/headers'
import { auth } from '~/lib/auth'

export default async function AccountPage() {
  const ctx = await auth.resolveSession({ headers: new Headers(headers()) })
  if (!ctx) {
    redirect('/signin')
  }
  return <h1>Hello, {ctx.identity.profile.displayName}</h1>
}

For route handlers, pass the request object straight in:

app/api/me/route.ts
import { auth } from '~/lib/auth'

export async function GET(req: Request) {
  const ctx = await auth.resolveSession(req)
  if (!ctx) return new Response('Unauthorized', { status: 401 })
  return Response.json(ctx.identity)
}

Middleware

For app-wide gates (e.g. all /dashboard routes require auth), use Next.js middleware:

middleware.ts
import { NextResponse } from 'next/server'
import { auth } from '~/lib/auth'

export async function middleware(req: NextRequest) {
  const ctx = await auth.resolveSession(req)
  if (!ctx && req.nextUrl.pathname.startsWith('/dashboard')) {
    return NextResponse.redirect(new URL('/signin', req.url))
  }
  return NextResponse.next()
}

export const config = {
  matcher: ['/dashboard/:path*'],
}

Be careful with middleware: it runs on every request and adds latency. Resolve-only-if-needed (gate by pathname first), and prefer the server-component pattern for non-blocking pages.

Client integration

Pair Next with the React client:

app/providers.tsx
'use client'

import { AuthProvider } from '@gentleduck/auth/client/react'

export function Providers({ children }: { children: React.ReactNode }) {
  return (
    <AuthProvider baseUrl="/api/auth">
      {children}
    </AuthProvider>
  )
}
components/SignInButton.tsx
'use client'

import { authUseSession, authUseSignIn } from '@gentleduck/auth/client/react'

export function SignInButton() {
  const { data, status } = authUseSession()
  const signIn = authUseSignIn()

  if (status === 'authed') return <span>Hi, {data.identity.profile.displayName}</span>
  return (
    <button onClick={() => signIn.mutate({ providerId: 'google', input: {} })}>
      Sign in with Google
    </button>
  )
}

See the React client guide.