React, NextJS, Prisma, NextAuth, Authentication

Setup Authentication in NextJS App Router Using Next-Auth and Prisma

Signin with google with new NextJS App Router using Next-Auth and Prisma
A corgi smiling happily
  • Add next-auth packages
1pnpm add @auth/prisma-adapter next-auth
  • [[Setup Prisma]] and add the auth tables in the schema.prisma
1generator client {
2 provider = "prisma-client-js"
3}
4
5datasource db {
6 provider = "postgresql"
7 url = env("POSTGRES_PRISMA_URL")
8 directUrl = env("POSTGRES_URL")
9 shadowDatabaseUrl = env("POSTGRES_SHADOW_URL")
10}
11
12model Account {
13 id String @id @default(uuid())
14 userId String @unique
15 type String
16 provider String
17 providerAccountId String
18 refresh_token String? @db.Text
19 access_token String? @db.Text
20 expires_at Int?
21 token_type String?
22 scope String?
23 id_token String?
24 session_state String?
25
26 user User @relation(fields: [userId], references: [id], onDelete: Cascade)
27
28 createdAt DateTime @default(now()) @map("created_at")
29 updatedAt DateTime @updatedAt @map("updated_at")
30
31 @@unique([provider, providerAccountId])
32}
33
34model Session {
35 id String @id @default(uuid())
36 sessionToken String @unique
37 userId String
38 expires DateTime
39 user User @relation(fields: [userId], references: [id], onDelete: Cascade)
40}
41
42model User {
43 id String @id @default(uuid())
44 email String @unique
45 name String?
46 emailVerified DateTime?
47 image String?
48
49 accounts Account[]
50 sessions Session[]
51
52 createdAt DateTime @default(now()) @map("created_at")
53 updatedAt DateTime @updatedAt @map("updated_at")
54
55 @@map("users")
56}
57
58model VerificationToken {
59 identifier String
60 token String @unique
61 expires DateTime
62
63 @@unique([identifier, token])
64}
  • Create and src/lib/auth.ts file
1import type { NextAuthOptions } from "next-auth";
2import GoogleProvider from "next-auth/providers/google";
3import { PrismaAdapter } from "@auth/prisma-adapter";
4import { prisma } from "@/lib/prisma";
5
6export const authOptions: NextAuthOptions = {
7 // @ts-ignore
8 adapter: PrismaAdapter(prisma),
9 session: {
10 strategy: "jwt",
11 },
12 pages: {
13 signIn: "/",
14 signOut: "/",
15 },
16 providers: [
17 GoogleProvider({
18 clientId: "*****",
19 clientSecret: "****",
20 authorization: {
21 params: {
22 prompt: "consent",
23 access_type: "offline",
24 response_type: "code",
25 },
26 },
27 }),
28 ],
29 secret: process.env.NEXTAUTH_SECRET,
30 callbacks: {
31 async signIn({ user, account, profile, email, credentials }) {
32 console.log("fire signin Callback");
33 return true;
34 },
35 async redirect({ url, baseUrl }) {
36 console.log("fire redirect Callback");
37 return baseUrl;
38 },
39 async session({ session, user, token }) {
40 console.log("fire SESSION Callback");
41 return {
42 ...session,
43 user: {
44 ...session.user,
45 id: token.id,
46 randomKey: token.randomKey,
47 },
48 };
49 },
50 async jwt({ token, user, account, profile, isNewUser }) {
51 console.log("fire jwt Callback");
52 if (user) {
53 const u = user as unknown as any;
54 return {
55 ...token,
56 id: u.id,
57 randomKey: u.randomKey,
58 };
59 }
60 return token;
61 },
62 },
63};
  • Now create a NextAuth route in src/app/api/auth/[...nextauth]/route.ts
1import NextAuth from "next-auth";
2import { authOptions } from "@/lib/auth";
3
4const handler = NextAuth(authOptions);
5export { handler as GET, handler as POST };
  • Create src/components/button.ts
1"use client";
2
3import { signIn, signOut } from "next-auth/react";
4
5export const LoginButton = () => {
6 return <button onClick={() => signIn("google")}>Login</button>;
7};
8
9export const LogoutButton = () => {
10 return <button onClick={() => signOut()}>Logout</button>;
11};
  • Now do the following configurations in src/app/page.tsx file
1import { prisma } from "@/lib/prisma";
2import { LoginButton, LogoutButton } from "@/components/buttons";
3import { getServerSession } from "next-auth";
4import { authOptions } from "@/lib/auth";
5
6async function getPosts() {
7 return await prisma.user.findMany();
8}
9
10export default async function Home() {
11 const session = await getServerSession(authOptions);
12 const posts = await getPosts();
13 return (
14 <>
15 <code>
16 <pre className="">{JSON.stringify(posts, undefined, 2)}</pre>
17 <pre className="">{JSON.stringify(session, undefined, 2)}</pre>
18 </code>
19 <div>
20 <LoginButton />
21 <LogoutButton />
22 </div>
23 </>
24 );
25}

Do you have any questions, or simply wish to contact me privately? Don't hesitate to shoot me a DM on Twitter.

Have a wonderful day.
Abhishek 🙏

Subscribe to my newsletter

Get email from me about my ideas, full-stack development resources, tricks and tips as well as exclusive previews of upcoming articles.

No spam. Just the highest quality ideas you’ll find on the web.