Skip to content
C.W.K.
Stream
Lesson 04 of 09 · published

Auth.js v5 (NextAuth)

~22 min · Auth.js, OAuth, session

Level 0Curious
0 XP0/68 lessons0/11 achievements
0/120 XP to next level120 XP to go0% complete

One auth() works everywhere

Auth.js v5 (formerly NextAuth.js) ships a single auth() helper that runs in Server Components, Server Actions, Route Handlers, and the proxy. The mental model: "is there a session?" — same answer, same call site, no matter where you ask.

Setup

Install next-auth@beta, configure providers, export handlers + auth. Wire handlers as a Route Handler at app/api/auth/[...nextauth]/route.ts.

Where you'll call auth()

LocationPattern
Server Componentconst session = await auth();
Server Actionsame
Route Handlersame
Proxywrap export: export const proxy = auth(req => { … });

Centralize Auth.js configuration and use auth() at every server boundary that needs a session. Treat the session as identity evidence, then perform resource-level authorization close to the read or write. Keep provider secrets and callbacks server-only. A shared auth helper still needs failure behavior tailored to each execution surface. Navigation, API calls, and server actions cannot all return a login page without breaking their client contracts.

A session check is not an authorization policy. A signed-in user may still be unable to read another account or mutate an administrative resource. A redirect in Proxy also cannot protect a Route Handler or Server Action called directly. Exercise signed-out, ordinary-user, owner, and administrator cases against the page and the underlying action or handler. Test expired sessions and callback failures, and verify logs do not expose tokens, provider payloads, or secrets.

Code

auth.ts — single config file·ts
// auth.ts
import NextAuth from 'next-auth';
import GitHub from 'next-auth/providers/github';
import Google from 'next-auth/providers/google';

export const { handlers, auth, signIn, signOut } = NextAuth({
  providers: [
    GitHub({ clientId: process.env.GITHUB_ID!, clientSecret: process.env.GITHUB_SECRET! }),
    Google({ clientId: process.env.GOOGLE_ID!, clientSecret: process.env.GOOGLE_SECRET! }),
  ],
});
Wire handlers, then call auth() anywhere·ts
// app/api/auth/[...nextauth]/route.ts
export { GET, POST } from '@/auth';

// In a Server Component:
import { auth } from '@/auth';
export default async function Page() {
  const session = await auth();
  return session ? <p>Welcome {session.user?.name}</p> : <SignInButton />;
}

// In a Server Action:
export async function createPost() {
  'use server';
  const session = await auth();
  if (!session) throw new Error('Unauthorized');
  // …
}

// In proxy.ts:
import { auth } from '@/auth';
import { NextResponse } from 'next/server';

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

External links

Exercise

Set up Auth.js v5 with one OAuth provider. Add auth() calls in a Server Component, a Server Action, and the proxy. Confirm the same session is visible in all three.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.