Skip to main content
Integrate Arraf Auth with your Next.js application. This guide covers both App Router and Pages Router.

Installation

npm install @arraf-auth/nextjs

Setup

1
Configure Auth Instance
2
First, create your auth configuration:
3
import { createAuth } from "@arraf-auth/core"

export const auth = createAuth({
  // Your auth configuration
})
4
Create API Route Handler
5
App Router
Create an API route at app/api/auth/[...all]/route.ts:
app/api/auth/[...all]/route.ts
import { toNextHandlers } from "@arraf-auth/nextjs"
import { auth } from "@/lib/auth"

export const { GET, POST } = toNextHandlers(auth)
The toNextHandlers function automatically remaps /api/auth paths to /auth for the handler.
Pages Router
Create an API route at pages/api/auth/[...all].ts:
pages/api/auth/[...all].ts
import type { NextApiRequest, NextApiResponse } from "next"
import { toNextHandlers } from "@arraf-auth/nextjs"
import { auth } from "@/lib/auth"

const handlers = toNextHandlers(auth)

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  const request = new Request(
    `${req.headers.host}${req.url}`,
    {
      method: req.method,
      headers: req.headers as HeadersInit,
      body: req.method !== "GET" ? JSON.stringify(req.body) : undefined,
    }
  )

  const handler = handlers[req.method as "GET" | "POST"]
  if (!handler) {
    return res.status(405).json({ error: "Method not allowed" })
  }

  const response = await handler(request)
  const data = await response.json()

  response.headers.forEach((value, key) => {
    if (key.toLowerCase() === "set-cookie") {
      res.setHeader(key, value)
    }
  })

  return res.status(response.status).json(data)
}
6
Set Up Middleware for Protected Routes
7
Create middleware.ts at the root of your project:
8
import { createAuthMiddleware, defaultMatcher } from "@arraf-auth/nextjs/middleware"
import { auth } from "@/lib/auth"

export default createAuthMiddleware({
  auth,
  protectedRoutes: ["/dashboard", "/profile", "/settings"],
  publicRoutes: ["/", "/login", "/signup"],
  redirectTo: "/login",
})

export const config = defaultMatcher
9
The middleware automatically:
  • Protects specified routes
  • Redirects unauthenticated users to login
  • Adds callback URL for post-login redirect
  • Sets user info in response headers (x-user-id, x-user-email)
10
Middleware Configuration Options
11
  • auth - Your auth instance (required)
  • protectedRoutes - Array of route patterns to protect (empty array = protect all)
  • publicRoutes - Array of routes that don’t require authentication
  • redirectTo - Where to redirect unauthenticated users (default: /login)
  • afterLoginRedirect - Where to redirect after successful login
  • Accessing Session Data

    Server-Side (App Router)

    Access session data in Server Components and Server Actions:
    app/dashboard/page.tsx
    import { getSession } from "@arraf-auth/nextjs"
    import { auth } from "@/lib/auth"
    
    export default async function DashboardPage() {
      const session = await getSession(auth)
    
      if (!session) {
        return <div>Not authenticated</div>
      }
    
      return (
        <div>
          <h1>Welcome {session.user.name}</h1>
          <p>Email: {session.user.email}</p>
        </div>
      )
    }
    
    When called without a Request object, getSession automatically retrieves cookies from Next.js headers.

    API Routes (App Router)

    Access session in API route handlers:
    app/api/profile/route.ts
    import { NextRequest } from "next/server"
    import { getSession } from "@arraf-auth/nextjs"
    import { auth } from "@/lib/auth"
    
    export async function GET(req: NextRequest) {
      const session = await getSession(auth, req)
    
      if (!session) {
        return Response.json({ error: "Unauthorized" }, { status: 401 })
      }
    
      return Response.json({
        user: session.user,
      })
    }
    

    Client-Side

    Create a client component to fetch session data:
    app/components/user-info.tsx
    "use client"
    
    import { useEffect, useState } from "react"
    
    export function UserInfo() {
      const [user, setUser] = useState(null)
    
      useEffect(() => {
        fetch("/api/auth/session")
          .then((res) => res.json())
          .then((data) => setUser(data.user))
      }, [])
    
      if (!user) return <div>Loading...</div>
    
      return <div>Logged in as {user.email}</div>
    }
    

    Route Protection Patterns

    Protect All Routes Except Public

    middleware.ts
    export default createAuthMiddleware({
      auth,
      publicRoutes: ["/", "/login", "/signup", "/about"],
      redirectTo: "/login",
    })
    

    Protect Specific Routes Only

    middleware.ts
    export default createAuthMiddleware({
      auth,
      protectedRoutes: ["/dashboard", "/admin"],
      redirectTo: "/login",
    })
    
    If protectedRoutes is an empty array, ALL routes (except publicRoutes) will be protected.

    Advanced Usage

    Custom Middleware Logic

    Extend the middleware with custom logic:
    middleware.ts
    import { NextResponse } from "next/server"
    import type { NextRequest } from "next/server"
    import { auth } from "@/lib/auth"
    
    export async function middleware(req: NextRequest) {
      const session = await auth.getSession(req)
    
      // Custom role-based protection
      if (req.nextUrl.pathname.startsWith("/admin")) {
        if (!session?.user?.role || session.user.role !== "admin") {
          return NextResponse.redirect(new URL("/unauthorized", req.url))
        }
      }
    
      return NextResponse.next()
    }
    

    Server Actions

    Access session in Server Actions:
    app/actions.ts
    "use server"
    
    import { getSession } from "@arraf-auth/nextjs"
    import { auth } from "@/lib/auth"
    
    export async function updateProfile(formData: FormData) {
      const session = await getSession(auth)
    
      if (!session) {
        throw new Error("Unauthorized")
      }
    
      // Update profile logic
    }
    

    TypeScript Types

    interface ArrafAuthConfig {
      auth: {
        handler: (req: Request) => Promise<Response>
        getSession: (req: Request) => Promise<{ user: any; session: any } | null>
      }
    }
    
    interface MiddlewareConfig {
      auth: ArrafAuthConfig["auth"]
      protectedRoutes?: string[]
      publicRoutes?: string[]
      redirectTo?: string
      afterLoginRedirect?: string
    }
    

    Troubleshooting

    Middleware Not Running

    Make sure your matcher config is set correctly:
    middleware.ts
    export const config = {
      matcher: [
        "/((?!_next/static|_next/image|favicon.ico|.*\\.png$).*)",
      ],
    }
    

    Session Not Persisting

    Ensure cookies are being set correctly. Check:
    • Your auth handler is returning proper Set-Cookie headers
    • Cookie domain matches your application domain
    • Secure flag is appropriate for your environment

    Type Errors with getSession

    If you get type errors with getSession, make sure you’re importing from the correct package:
    import { getSession } from "@arraf-auth/nextjs"
    

    Build docs developers (and LLMs) love