Using NextAuth with App Router in Next.js 14 (GitHub OAuth)

Set env vars.

NEXTAUTH_URL=http://localhost:3000
AUTH_SECRET=# Linux: `openssl rand -hex 32` or go to https://generate-secret.now.sh/32

# https://next-auth.js.org/providers/github
OAUTH_CLIENT_KEY=
OAUTH_CLIENT_SECRET=

Install next-auth package.

bun add next-auth@5.0.0-beta.3

Create a new file src/app/auth.tsx and add the following code.

touch src/app/auth.tsx
import NextAuth from 'next-auth';
import GitHub from 'next-auth/providers/github';

export const {
  handlers: { GET, POST },
  auth
} = NextAuth({
  providers: [
    GitHub({
      clientId: process.env.OAUTH_CLIENT_KEY as string,
      clientSecret: process.env.OAUTH_CLIENT_SECRET as string
    })
  ]
});

Create a new file src/app/api/auth/[...nextauth]/route.ts and add the following code.

mkdir -p src/app/api/auth/[...nextauth]
touch src/app/api/auth/[...nextauth]/route.ts
// src/app/api/auth/[...nextauth]/route.ts
export { GET, POST } from '@/app/auth';
export const runtime = 'edge';

Create Login component.

touch src/app/components/Login.tsx
// src/app/components/login.tsx
'use client';

import {signOut, signIn} from "next-auth/react";
export function LoginButton({ user }: { user: any }) {
  return (
    <>
      {user ? (
        <button onClick={() => signOut()}>Sign out</button>
      ) : (
        <button onClick={() => signIn('github')}>Sign in with github</button>
      )}

      <div>
        {user ? (
          <div>
            <p>Signed in as {user.email}</p>
            <img src={user.image} alt={user.name} />
          </div>
        ) : null}
      </div>
    </>
  );
}

Create Header component.

mkdir src/app/components
touch src/app/components/header.tsx
// src/app/components/header.tsx
import { auth } from '@/app/auth';
import {LoginButton} from "@/app/components/login";

export default async function Header() {
  const session = await auth();
  return (
    <header>
      <Login user={session?.user} />
    </header>
  );
}

Add Header component to layout.

// src/app/layout.tsx
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";
import Header from "@/app/components/header";

const inter = Inter({ subsets: ["latin"] });

export const metadata: Metadata = {
  title: "Next Auth Sandbox",
  description: "Generated by create next app",
};

export default function RootLayout({
                                     children,
                                   }: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="en">
    <body className={inter.className}>
    <Header />
    {children}
    </body>
    </html>
  );
}

Start the dev server and open http://localhost:3000 to see the app.

bun dev
open http://localhost:3000