Aura Auth
Integrations

Next.js (App Router)

Integrate Aura Auth and Next.js App Router

This guide walks you through to implement Aura Auth in a Next.js application using the App Router with a complete support for Route Handlers, Server Components, and Server Actions. If you haven't configured Aura Auth yet, start with the Installation Guide and Quick Start Guide to set up your Auth instance and environment variables. Then follow the steps in this guide to integrate Aura Auth with your Next.js App Router application.

Aura Auth is working on developing a Next.js-specific package that will provide additional utilities and hooks for Next.js applications. For now, the core package can be used to implement authentication in Next.js with the patterns outlined in this guide. Stay tuned for updates on the Next.js package!


Setup Aura Auth

Create an Auth Instance

Create an auth.ts file in src/lib directory to configure your Aura Auth instance.

src/lib/auth.ts
import { createAuth } from "@aura-stack/auth"

export const auth = createAuth({
  oauth: ["github"],
  basePath: "/api/auth",
  baseURL: "http://localhost:3000",
})

export const { handlers, jose, api } = auth

The basePath should match the path where your auth route handlers are mounted and baseURL should point to your local development server or deployed application URL.

Create an Auth Client Instance

Create an auth-client.ts file in src/lib directory to enable client-side features.

src/lib/auth-client.ts
import { createAuthClient } from "@aura-stack/auth/client"

export const authClient = createAuthClient({
  basePath: "/api/auth",
  baseURL: "http://localhost:3000",
})

The basePath should match the path where your auth route handlers are mounted and baseURL should point to your local development server or deployed application URL. Both options should be the same as the ones used in your auth.ts configuration to ensure client and server instances are aligned.

Mount HTTP Handlers

Create a catch-all route in app/api/auth/[...aura]/route.ts to handle all authentication endpoints dynamically. The /api/auth path should match with the App Router Structure and basePath defined in your Auth configuration.

src/app/api/auth/[...aura]/route.ts
import { handlers } from "@/lib/auth"

export const { GET, POST, PATCH } = handlers

RSC and Server Actions

The api object is designed for server environments such as Server Components and Server Actions.

When you need the active session on the server, pass the current request headers using headers() from next/headers. That gives Aura Auth access to the cookies and headers required to identify the session.

Sign-In

src/app/login/page.tsx
import { api } from "@/lib/auth"
import { redirect } from "next/navigation"

export default function LoginPage() {
  const signInAction = async () => {
    "use server"
    const signIn = await api.signIn("github", {
      redirect: false,
      redirectTo: "/dashboard",
    })
    redirect(signIn.signInURL)
  }

  return (
    <form action={signInAction}>
      <button type="submit">Sign in with GitHub</button>
    </form>
  )
}

Use this pattern when you want the server action to own the redirect destination while Aura Auth handles the provider flow.

Get Session

src/app/dashboard/page.tsx
import { api } from "@/lib/auth"
import { headers } from "next/headers"
import { redirect } from "next/navigation"

export default async function DashboardPage() {
  const headersStore = await headers()

  const { session, authenticated } = await api.getSession({ headers: headersStore })
  if (!authenticated) {
    redirect("/login")
  }

  return (
    <div>
      <h1>Welcome back, {session.user?.name}</h1>
      <img src={session.user?.image} alt="Profile Avatar" />
    </div>
  )
}

This is the recommended pattern for protected App Router pages: check the session before rendering any private UI.

Sign Out

src/app/dashboard/page.tsx
import { api } from "@/lib/auth"
import { headers } from "next/headers"
import { redirect } from "next/navigation"

export default async function DashboardPage() {
  const signOutAction = async () => {
    "use server"
    const headerStore = await headers()
    const response = await api.signOut({
      headers: headerStore,
    })
    if (response.ok) {
      redirect("/home")
    }
  }
  return (
    <form action={signOutAction}>
      <button type="submit">Sign Out</button>
    </form>
  )
}

If you prefer a client-only interaction, you can call authClient.signOut() from a client component instead.

Client Side Rendering (CSR)

Sign In

src/app/components/sign-in-button.tsx
"use client"
import { authClient } from "@/lib/auth-client"

export const SignInButton = () => {
  const handleSignIn = async () => {
    await authClient.signIn("github", {
      redirectTo: "/dashboard",
    })
  }

  return <button onClick={handleSignIn}>Sign in with GitHub</button>
}

Use a client component when you need a clickable auth button, custom modal, or other interactive sign-in entry point.

Get Session

src/app/components/user-profile.tsx
"use client"
import { useEffect, useState } from "react"
import { authClient } from "@/lib/auth-client"

export const UserProfile = () => {
  const [session, setSession] = useState(null)
  useEffect(() => {
    const fetchSession = async () => {
      const { session } = await authClient.getSession()
      setSession(session)
    }
    fetchSession()
  }, [])
  if (!session) return <div>Loading...</div>

  return (
    <div>
      <h1>Welcome back, {session.user?.name}</h1>
      <img src={session.user?.image} alt="Profile Avatar" />
    </div>
  )
}

This is useful for session-aware UI that updates after hydration or after a client-side navigation.

Sign Out

To easily check sessions or log out from client components, use createAuthClient.

src/app/components/sign-out-button.tsx
"use client"
import { authClient } from "@/lib/auth-client"

export const SignOutButton = () => {
  const handleSignOut = async () => {
    await authClient.signOut({
      redirectTo: "/login",
    })
  }

  return <button onClick={handleSignOut}>Sign out</button>
}

Client-side sign-out is a better fit for navigation menus, profile popovers, and other interactive UI.


Common Pitfalls

  • Mismatched Paths: Ensure the basePath in your auth configuration matches the path where your route handlers are mounted. For example, if your handlers are in app/api/auth/[...aura]/route.ts, then basePath should be /api/auth.
  • Incorrect Headers: When calling api.getSession or api.signOut from server components or actions, always pass the current request headers using Next.js's headers() function. This allows Aura Auth to read cookies and manage sessions correctly.
  • Don't update cookies in signOut: When signing out is called, Aura Auth will return a response with Set-Cookie headers and it's required to set the cookies using cookies function.

Resources

On this page