With the new version auth.js v5 on the way, I am going to share my knowledge about step-by-step integrating it with Prisma and Supabase in Next.js.
Version
- "next-auth": "^5.0.0-beta.20"
- "@prisma/client": "^5.19.1"
- "@auth/prisma-adapter": "^2.4.2"
- "prisma": "^5.19.1"
Setting up Prisma
- Install Prisma
npm install prisma --save-dev
- Initialize prisma
npx prisma init
- This will creates a new prisma directory with a schema.prisma file and configures PostgresSQL as your database also creating .env file with DATABASE_URL
Initialize Supabase
- Create organization in supabase (optional if you are individual developer)
- Create database
- Get connection string by clicking connect button.
Click prisma/schema.prisma and copy the code inside your prisma.
Note. Prisma doesn't work on .env.local and it only works with .env so make sure to change the correct .env file
Setting NextAuth
- Install auth.js v5
- Adding NEXTAUTH_SECRET in env file
npx auth secret
- Add auth.ts file under src folder
// auth.ts
import NextAuth from 'next-auth';
export const { handlers, auth, signIn, signOut } = NextAuth({
providers: [],
})
- Add a route handler under
app/api/auth/[...nextauth]/route.ts
// app/api/auth/[...nextauth]/route.ts
import { handlers } from "@/auth" // Referring to the auth.ts we just created
export const { GET, POST } = handlers
- Add a middleware to keep session alive.
// middleware.ts
export { auth as middleware } from "@/auth"
- Add PrismaAdapter The Prisma Adapter in NextAuth.js is a tool that allows NextAuth to store and manage authentication data (such as user accounts, sessions, verification tokens, etc.) in a Prisma-managed database.
npm install @prisma/client @auth/prisma-adapter
To improve performance using Prisma ORM, we can set up the Prisma instance to ensure only one instance is created throughout the project and then import it from any file as needed. This approach avoids recreating instances of PrismaClient every time it is used.
// prisma.ts
import { PrismaClient } from "@prisma/client"
const globalForPrisma = globalThis as unknown as { prisma: PrismaClient }
export const prisma = globalForPrisma.prisma || new PrismaClient()
if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = prisma
- Add prisma Adapter in auth.ts
// auth.ts
import NextAuth from "next-auth"
import { PrismaAdapter } from "@auth/prisma-adapter"
import { prisma } from "@/prisma"
export const { handlers, auth, signIn, signOut } = NextAuth({
adapter: PrismaAdapter(prisma),
providers: [],
})
- Define database tables for authentication by adding this in prisma/schema.prisma
// prisma/schema.prisma
model User {
id String @id @default(cuid())
firstName String?
lastName String?
email String @unique
password String? // Make sure this field exists in your schema
emailVerified DateTime?
image String?
accounts Account[]
sessions Session[]
// Optional for WebAuthn support
Authenticator Authenticator[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model Account {
userId String
type String
provider String
providerAccountId String
refresh_token String?
access_token String?
expires_at Int?
token_type String?
scope String?
id_token String?
session_state String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@id([provider, providerAccountId])
}
model Session {
sessionToken String @unique
userId String
expires DateTime
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model VerificationToken {
identifier String
token String
expires DateTime
@@id([identifier, token])
}
// Optional for WebAuthn support
model Authenticator {
credentialID String @unique
userId String
providerAccountId String
credentialPublicKey String
counter Int
credentialDeviceType String
credentialBackedUp Boolean
transports String?
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@id([userId, credentialID])
}
- Perform migration
In prisma, migrations need to perform except MongoDB.
A migration in the context of databases refers to the process of modifying the database schema in a controlled, versioned manner. This allows developers to make changes to the database structure (such as adding or removing tables, changing columns, or creating relationships) while keeping track of the changes. Migrations help ensure consistency across environments (like development, testing, and production) and make it easier to manage schema changes over time.
And in order to create SQL migration file,
npx prisma migrate dev
Note: Migration need to perform only when the schema changes
This will take a while if we are using Supabase URL and will prompt to ask migration name and after finished running this command, all the changes in schema will be updated in supabase and also creating migrations folder inside prisma/migrations
Note: In order to sync the migrations with supabase, don’t delete migrations folder.
npx prisma migrate dev
will also generate the Prisma client so the developer who is using this command may not need to run npx prisma generate.
But developers who pull from git will need to run npx prisma generate
first to generate Prisma client.
Setting SessionProvider
We need to set session provider so that the client can use useSession
hook.
Note: Need to add SessionProvider for all parent layout
// app/layout.tsx
import type { Metadata } from "next";
import localFont from "next/font/local";
import "./globals.css";
import { SessionProvider } from "next-auth/react";
import { auth } from "@/auth";
const geistSans = localFont({
src: "./fonts/GeistVF.woff",
variable: "--font-geist-sans",
weight: "100 900",
});
const geistMono = localFont({
src: "./fonts/GeistMonoVF.woff",
variable: "--font-geist-mono",
weight: "100 900",
});
export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
};
export default async function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
const session = await auth();
return (
<html lang="en">
<body
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
>
<SessionProvider session={session}>{children}</SessionProvider>
</body>
</html>
);
}
Calling session from server pages or from api
import { auth } from '@/auth';
const session = await auth();
Calling session from client components
"use client";
import { useSession } from 'next-auth/react';
const { data: session, status } = useSession();
Conclusion
By combining the power of Prisma, Auth.js v5 and Supabase (as database), we can easily manage authentication in a Next.js app with minimal configuration but I haven't added any providers yet. This is the initial setup for now and will share about others providers later.
My GitHub Repo for next-auth Repo
Integrating LinkedIn Authentication Link
Integrating GitHub Authentication Link
Happy Coding ^_^
Top comments (0)