{
“@context”: “https://schema.org”,
“@type”: “FAQPage”,
“mainEntity”: [
{
“@type”: “Question”,
“name”: “JWT vs Database sessions — which should I use?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “JWT for simple apps where you don’t need server-side session invalidation. Database for apps where you need to revoke sessions, audit logins, or store user preferences tied to sessions.”
}
},
{
“@type”: “Question”,
“name”: “How do I add more OAuth providers?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “Import and add to the providers array: import GitHub from “next-auth/providers/github”. NextAuth supports 70+ providers with the same pattern.”
}
},
{
“@type”: “Question”,
“name”: “How do I restrict sign-in to specific email domains?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “Use the signIn callback: async signIn({ user }) { return user.email?.endsWith(“@company.com”) ?? false; }”
}
},
{
“@type”: “Question”,
“name”: “Is Auth.js (NextAuth v5) production-ready?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “As of 2026, yes — the beta has been stable for many months and is widely used in production. The API is stable and recommended for new projects.”
}
},
{
“@type”: “Question”,
“name”: “How do I access the Google access token for API calls?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “Store it in the JWT callback (token.accessToken = account.access_token) and expose it in the session callback. Access via session.accessToken.”
}
}
]
}
{
“@context”: “https://schema.org”,
“@type”: “TechArticle”,
“headline”: “How to Implement Google OAuth 2.0 in Next.js 15 with NextAuth: Complete 2026 Tutorial”,
“description”: “Add Google sign-in to your Next.js 15 app with NextAuth v5. Complete setup including callbacks, session handling, protected routes, and database integration.”,
“url”: “”,
“datePublished”: “2026-06-29 10:05:00”,
“dateModified”: “2026-06-29 10:05:00”,
“author”: {
“@type”: “Organization”,
“name”: “TechPulse Editorial Team”,
“url”: “https://techpulsesite.com”
},
“publisher”: {
“@type”: “Organization”,
“name”: “TechPulse”,
“url”: “https://techpulsesite.com”,
“logo”: {
“@type”: “ImageObject”,
“url”: “https://techpulsesite.com/wp-content/uploads/logo.png”
}
}
}
OAuth 2.0 with Google is the fastest way to add authentication to a Next.js application — users sign in with their Google account, eliminating password management and reducing friction. In 2026, NextAuth v5 (Auth.js) provides the cleanest integration for Next.js 15 App Router. This tutorial covers the complete implementation.
📋 Table of Contents
- Prerequisites
- Step 1: Set Up Google OAuth Credentials
- Step 2: Install NextAuth v5
- Step 3: Configure Auth.js
- Step 4: Create Route Handler
- Step 5: Environment Variables
- Step 6: Add Sign-In/Out to UI
- Step 7: Access Session in Server Components
- Step 8: Protect Routes with Middleware
- Step 9: Database Integration (Optional but Recommended)
- Step 10: Access Session in Client Components
- Frequently Asked Questions
- Conclusion
Prerequisites
- Next.js 15 project (App Router)
- Google Cloud Console account (free)
- PostgreSQL database (optional, for persisting sessions)
Step 1: Set Up Google OAuth Credentials
- Go to console.cloud.google.com
- Create a new project (or select existing)
- Navigate to APIs & Services → Credentials → Create Credentials → OAuth Client ID
- Application type: Web Application
- Authorized redirect URIs:
http://localhost:3000/api/auth/callback/google - Add production URI when deploying:
https://yourdomain.com/api/auth/callback/google - Save and copy your Client ID and Client Secret
Step 2: Install NextAuth v5
npm install next-auth@beta
# or
pnpm add next-auth@beta
Step 3: Configure Auth.js
Create auth.ts in your project root:
import NextAuth from "next-auth";
import Google from "next-auth/providers/google";
export const { handlers, auth, signIn, signOut } = NextAuth({
providers: [
Google({
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
}),
],
callbacks: {
async session({ session, token }) {
// Add user ID to session
if (token.sub) {
session.user.id = token.sub;
}
return session;
},
async jwt({ token, account, profile }) {
if (account) {
token.accessToken = account.access_token;
}
return token;
},
},
pages: {
signIn: "/login", // custom sign-in page
error: "/auth/error",
},
});
Step 4: Create Route Handler
Create app/api/auth/[...nextauth]/route.ts:
import { handlers } from "@/auth";
export const { GET, POST } = handlers;
Step 5: Environment Variables
# .env.local
GOOGLE_CLIENT_ID=your_client_id_here
GOOGLE_CLIENT_SECRET=your_client_secret_here
AUTH_SECRET=generate_with_openssl_rand_base64_32
# Generate AUTH_SECRET:
# openssl rand -base64 32
Step 6: Add Sign-In/Out to UI
// components/AuthButtons.tsx
import { signIn, signOut } from "@/auth";
export function SignInButton() {
return (
<form action={async () => {
"use server";
await signIn("google");
}}>
<button type="submit">Sign in with Google</button>
</form>
);
}
export function SignOutButton() {
return (
<form action={async () => {
"use server";
await signOut({ redirectTo: "/" });
}}>
<button type="submit">Sign out</button>
</form>
);
}
Step 7: Access Session in Server Components
// app/dashboard/page.tsx
import { auth } from "@/auth";
import { redirect } from "next/navigation";
export default async function Dashboard() {
const session = await auth();
if (!session?.user) {
redirect("/login");
}
return (
<main>
<h1>Welcome, {session.user.name}!</h1>
<img src={session.user.image!} alt="avatar" width={40} height={40} />
<p>Email: {session.user.email}</p>
</main>
);
}
Step 8: Protect Routes with Middleware
// middleware.ts
import { auth } from "@/auth";
import { NextResponse } from "next/server";
export default auth((req) => {
const isLoggedIn = !!req.auth;
const isProtected = req.nextUrl.pathname.startsWith("/dashboard");
if (isProtected && !isLoggedIn) {
const loginUrl = new URL("/login", req.url);
loginUrl.searchParams.set("callbackUrl", req.nextUrl.href);
return NextResponse.redirect(loginUrl);
}
return NextResponse.next();
});
export const config = {
matcher: ["/((?!api|_next/static|_next/image|favicon.ico).*)"],
};
Step 9: Database Integration (Optional but Recommended)
npm install @auth/prisma-adapter @prisma/client prisma
// auth.ts with database adapter
import { PrismaAdapter } from "@auth/prisma-adapter";
import { db } from "@/lib/db"; // your Prisma client
export const { handlers, auth, signIn, signOut } = NextAuth({
adapter: PrismaAdapter(db),
session: { strategy: "database" }, // store sessions in DB
providers: [Google(...)],
});
Database sessions allow invalidating sessions server-side (useful for banning users or forced sign-out) and persist login across server restarts — not possible with JWT-only sessions.
Step 10: Access Session in Client Components
// Wrap your app with SessionProvider in layout.tsx
// app/layout.tsx
import { SessionProvider } from "next-auth/react";
export default function RootLayout({ children }) {
return (
<html>
<body>
<SessionProvider>{children}</SessionProvider>
</body>
</html>
);
}
// Client component using session
"use client";
import { useSession } from "next-auth/react";
export function UserAvatar() {
const { data: session, status } = useSession();
if (status === "loading") return <Skeleton />;
if (!session) return <SignInButton />;
return <img src={session.user.image!} alt={session.user.name!} />;
}
Frequently Asked Questions
Q: JWT vs Database sessions — which should I use?
A: JWT for simple apps where you don’t need server-side session invalidation. Database for apps where you need to revoke sessions, audit logins, or store user preferences tied to sessions.
Q: How do I add more OAuth providers?
A: Import and add to the providers array: import GitHub from "next-auth/providers/github". NextAuth supports 70+ providers with the same pattern.
Q: How do I restrict sign-in to specific email domains?
A: Use the signIn callback: async signIn({ user }) { return user.email?.endsWith("@company.com") ?? false; }
Q: Is Auth.js (NextAuth v5) production-ready?
A: As of 2026, yes — the beta has been stable for many months and is widely used in production. The API is stable and recommended for new projects.
Q: How do I access the Google access token for API calls?
A: Store it in the JWT callback (token.accessToken = account.access_token) and expose it in the session callback. Access via session.accessToken.
Conclusion
NextAuth v5 + Google OAuth gives you production-ready authentication in Next.js 15 with about 50 lines of code. The route handler pattern, server action sign-in/out, middleware protection, and optional database adapter cover every real-world authentication requirement. Add GitHub, Discord, or email/password alongside Google using the same pattern — NextAuth handles the complexity so you don’t have to.
📚 You might also like
🔗 Share this article



✍️ Leave a Comment