Skip to main content
Prisma Documentation Docs

Search documentation

Type to search this documentation.

On this pageOverview

Auth.js (with Next.js)

Auth.js is a flexible, open-source authentication library designed to simplify adding authentication to your Next.js applications.

In this guide, you'll wire Auth.js into a brand-new Next.js app and persist users in a Prisma Postgres database. You can find a complete example of this guide on GitHub.

  • Node.js 20+
  • Basic familiarity with Next.js App Router and Prisma

Create a new Next.js application:

title="bun"
bunx create-next-app@latest authjs-prisma
pnpm
pnpm dlx create-next-app@latest authjs-prisma
yarn
yarn dlx create-next-app@latest authjs-prisma
npm
npx create-next-app@latest authjs-prisma

It will prompt you to customize your setup. Choose the defaults:

Navigate to the project directory:

cd authjs-prisma

To get started with Prisma, you'll need to install a few dependencies:

title="bun"
bun add prisma@prev tsx @types/pg --dev
pnpm
pnpm add prisma@prev tsx @types/pg --save-dev
yarn
yarn add prisma@prev tsx @types/pg --dev
npm
npm install prisma@prev tsx @types/pg --save-dev
bun add @prisma/client@7 @prisma/adapter-pg dotenv pg
Bash
pnpm add @prisma/client@7 @prisma/adapter-pg dotenv pg
Bash
yarn add @prisma/client@7 @prisma/adapter-pg dotenv pg
Bash
npm install @prisma/client@7 @prisma/adapter-pg dotenv pg

[!NOTE] If you are using a different database provider (MySQL, SQL Server, SQLite), install the corresponding driver adapter package instead of @prisma/adapter-pg. For more information, see Database drivers.

Once installed, initialize Prisma in your project:

Once installed, initialize Prisma in your project:

bunx --bun prisma init --output ../app/generated/prisma
Bash
pnpm prisma init --output ../app/generated/prisma
Bash
yarn prisma init --output ../app/generated/prisma
Bash
npx prisma init --output ../app/generated/prisma

[!NOTE] prisma init creates the Prisma scaffolding and a local DATABASE_URL. In the next step, you will create a Prisma Postgres database and replace that value with a direct postgres://... connection string.

This will create:

  • A prisma directory with a schema.prisma file.
  • A prisma.config.ts file for configuring Prisma
  • A .env file containing a local DATABASE_URL at the project root.
  • A schema configuration that specifies where the Prisma Client will be generated (../app/generated/prisma).

Create a Prisma Postgres database and replace the generated DATABASE_URL in your .env file with the postgres://... connection string from the CLI output:

This will create:

  • A prisma directory with a schema.prisma file.
  • A prisma.config.ts file for configuring Prisma
  • A .env file containing a local DATABASE_URL at the project root.
  • A schema configuration that specifies where the Prisma Client will be generated (../app/generated/prisma).

Create a Prisma Postgres database and replace the generated DATABASE_URL in your .env file with the postgres://... connection string from the CLI output:

title="bun"
bunx create-db
pnpm
pnpm dlx create-db
yarn
yarn dlx create-db
npm
npx create-db

In the prisma/schema.prisma file, swap the provider to prisma-client and add the runtime vercel-edge to the generator:

title="prisma/schema.prisma"
generator client {

  provider = "prisma-client"

  output   = "../app/generated/prisma"

  runtime  = "vercel-edge"

}

datasource db {

  provider = "postgresql"

}

Add the following models to the schema.prisma file, these models are provided by Auth.js:

title="prisma/schema.prisma"
model Account { 

  id                String  @id @default(cuid()) 

  userId            String  @map("user_id") 

  type              String

  provider          String

  providerAccountId String  @map("provider_account_id") 

  refresh_token     String? @db.Text

  access_token      String? @db.Text

  expires_at        Int?

  token_type        String?

  scope             String?

  id_token          String? @db.Text

  session_state     String?

  user User @relation(fields: [userId], references: [id], onDelete: Cascade) 

  @@unique([provider, providerAccountId]) 

  @@map("accounts") 

} 

model Session { 

  id           String   @id @default(cuid()) 

  sessionToken String   @unique @map("session_token") 

  userId       String   @map("user_id") 

  expires      DateTime

  user         User     @relation(fields: [userId], references: [id], onDelete: Cascade) 

  @@map("sessions") 

} 

model User { 

  id            String    @id @default(cuid()) 

  name          String?

  email         String?   @unique

  emailVerified DateTime? @map("email_verified") 

  image         String?

  accounts      Account[]

  sessions      Session[]

  @@map("users") 

} 

model VerificationToken { 

  identifier String

  token      String

  expires    DateTime

  @@unique([identifier, token]) 

  @@map("verification_tokens") 

} 

This creates the following models:

  • Account: Stores OAuth provider information (access tokens, refresh tokens, provider account IDs) and enables users to sign in with multiple providers while maintaining a single user record.
  • Session: Tracks authenticated user sessions with a unique session token, user ID, and expiration time to maintain authentication state across requests.
  • User: The core model storing user information (name, email, profile image). Users can have multiple accounts from different providers and multiple active sessions.
  • VerificationToken: Stores temporary tokens for email verification, password reset, and other security operations with expiration times.

To get access to the variables in the .env file, they can either be loaded by your runtime, or by using dotenv. Include an import for dotenv at the top of the prisma.config.ts

import "dotenv/config"; 

import { defineConfig, env } from "prisma/config";

export default defineConfig({

  schema: "prisma/schema.prisma",

  migrations: {

    path: "prisma/migrations",

  },

  datasource: {

    url: env("DATABASE_URL"),

  },

});

Now, run the following command to create the database tables and generate the Prisma Client:

title="bun"
bunx prisma migrate dev --name init
pnpm
pnpm prisma migrate dev --name init
yarn
yarn prisma migrate dev --name init
npm
npx prisma migrate dev --name init
title="bun"
bunx prisma generate
pnpm
pnpm prisma generate
yarn
yarn prisma generate
npm
npx prisma generate

Create a new folder in the root called lib and create a new file called prisma.ts in it. This file will contain the Prisma Client:

title="lib/prisma.ts"
import { PrismaClient } from "../app/generated/prisma/client"; 

import { PrismaPg } from "@prisma/adapter-pg"; 

const adapter = new PrismaPg({

  connectionString: process.env.DATABASE_URL!, 

}); 

const globalForPrisma = global as unknown as {

  prisma: PrismaClient; 

}; 

const prisma =

  globalForPrisma.prisma ||

  new PrismaClient({

    adapter, 

  }); 

if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = prisma; 

export default prisma; 

Install the Auth.js dependencies:

title="bun"
bun add @auth/prisma-adapter next-auth@beta
pnpm
pnpm add @auth/prisma-adapter next-auth@beta
yarn
yarn add @auth/prisma-adapter next-auth@beta
npm
npm install @auth/prisma-adapter next-auth@beta

For this guide, you'll be setting up OAuth with Github. For this, you'll need 3 environment variables:

  • AUTH_SECRET - Provided by Auth.js
  • CLIENT_ID - Provided by Github
  • CLIENT_SECRET - Provided by Github

To get the AUTH_SECRET, you can run the following command:

bunx auth secret --copy
Bash
pnpm dlx auth secret --copy
Bash
yarn dlx auth secret --copy
Bash
npx auth secret --copy
  • --copy` will copy the secret to your clipboard. *(Normally, running npx auth secretadds the secret to your.env.localfile. To keep it tidy, you can use--copyand add it to the.env` file that Prisma created earlier.)*

Add the following to the .env file:

.env
DATABASE_URL=<YOUR_DATABASE_URL>AUTH_SECRET=<YOUR_AUTH_SECRET>

To get the CLIENT_ID and CLIENT_SECRET, you can create a new OAuth application on Github.

  1. Navigate to Github Developer Settings
  2. Click on New OAuth App
  3. Enter a name for your app, a home page URL, and a callback URL
  • Name: Auth.js + Prisma (Or anything you want)
  • Homepage URL: http://localhost:3000
  • Callback URL: http://localhost:3000/api/auth/callback/github
  1. Click Register application
  2. Click Generate new client secret and copy the Client ID and Client Secret.
  3. Add the Client ID and Client Secret to the .env file:
.env
DATABASE_URL=<YOUR_DATABASE_URL>AUTH_SECRET=<YOUR_AUTH_SECRET>AUTH_GITHUB_ID=<YOUR_GITHUB_CLIENT_ID>AUTH_GITHUB_SECRET=<YOUR_GITHUB_CLIENT_SECRET>
  • --copy will copy the secret to your clipboard. (Normally, running npx auth secret adds the secret to your .env.local file. To keep it tidy, you can use --copy and add it to the .env file that Prisma created earlier.)

Add the following to the .env file:

title=".env"
DATABASE_URL=<YOUR_DATABASE_URL>

AUTH_SECRET=<YOUR_AUTH_SECRET>

To get the CLIENT_ID and CLIENT_SECRET, you can create a new OAuth application on Github.

  1. Navigate to Github Developer Settings
  2. Click on New OAuth App
  3. Enter a name for your app, a home page URL, and a callback URL
  • Name: Auth.js + Prisma (Or anything you want)
  • Homepage URL: http://localhost:3000
  • Callback URL: http://localhost:3000/api/auth/callback/github
  1. Click Register application
  2. Click Generate new client secret and copy the Client ID and Client Secret.
  3. Add the Client ID and Client Secret to the .env file:
title=".env"
DATABASE_URL=<YOUR_DATABASE_URL>

AUTH_SECRET=<YOUR_AUTH_SECRET>

AUTH_GITHUB_ID=<YOUR_GITHUB_CLIENT_ID>

AUTH_GITHUB_SECRET=<YOUR_GITHUB_CLIENT_SECRET>

In the /lib folder, create a new file called auth.ts and add the following code:

title="lib/auth.ts"
import NextAuth from "next-auth";

export const { handlers, auth, signIn, signOut } = NextAuth({

  providers: [],

});

Next, you'll need to add the Github provider to the auth.ts file:

title="lib/auth.ts"
import NextAuth from "next-auth";

import GitHub from "next-auth/providers/github"; 

export const { handlers, auth, signIn, signOut } = NextAuth({

  providers: [GitHub], 

});

Users will now be able to sign in with Github. To add them to your database, you'll need to use the Prisma Adapter:

title="lib/auth.ts"
import NextAuth from "next-auth";

import { PrismaAdapter } from "@auth/prisma-adapter"; 

import prisma from "@/lib/prisma"; 

import GitHub from "next-auth/providers/github";

export const { handlers, auth, signIn, signOut } = NextAuth({

  adapter: PrismaAdapter(prisma), 

  providers: [GitHub],

});

In the root, create a new file called middleware.ts. This will protect your routes and ensure that only authenticated users can access them:

title="middleware.ts"
export { auth as middleware } from "@/lib/auth";

The route handler is required to handle authentication requests from Auth.js. It exports the GET and POST handlers that Auth.js uses for sign-in, sign-out, and callback operations.

Create a new file at app/api/auth/[...nextauth]/route.ts:

mkdir -p app/api/auth/[...nextauth]

touch app/api/auth/[...nextauth]/route.ts

Add the following code to the file:

title="app/api/auth/[...nextauth]/route.ts"
import { handlers } from "@/lib/auth";

export const { GET, POST } = handlers;

Your routes are now protected by Auth.js. To see more configuration options, check out the Auth.js Middleware documentation.

You will be creating a Sign In and Sign Out button. Create a /components folder in the root and add a new file called auth-components.tsx in it.

Start by importing the signIn and signOut functions from the auth file:

title="components/auth-components.tsx"
import { signIn, signOut } from "@/lib/auth"; 

Next, create the SignIn and SignOut components:

title="components/auth-components.tsx"
import { signIn, signOut } from "@/lib/auth";

export function SignIn({ provider }: { provider?: string }) {

  return (

    <form>

      {" "}

      <button className="bg-neutral-700 text-white p-2 rounded-md">

        {" "}

        // [!code ++] Sign In with {provider}

      </button>{" "}

    </form> 

  ); 

} 

export function SignOut() {

  return (

    <form>

      {" "}

      <button className="bg-neutral-700 text-white p-2 rounded-md">

        {" "}

        // [!code ++] Sign Out

      </button>{" "}

    </form> 

  ); 

} 

To add functionality to both of the buttons, add an action to the form that calls the signIn and signOut functions respectively:

title="components/auth-components.tsx"
import { signIn, signOut } from "@/lib/auth";

export function SignIn({ provider }: { provider?: string }) {

  return (

    <form

      action={async () => {

        "use server"; 

        await signIn(provider); 

      }} 

    >

      <button className="bg-neutral-700 text-white p-2 rounded-md">Sign In with {provider}</button>

    </form>

  );

}

export function SignOut() {

  return (

    <form

      action={async () => {

        "use server"; 

        await signOut(); 

      }} 

      className="w-full"

    >

      <button className="bg-neutral-700 text-white p-2 rounded-md">Sign Out</button>

    </form>

  );

}

In the /app folder, replace the page.tsx file with the following code:

title="app/page.tsx"
const Page = async () => {  return (    <div className="min-h-screen bg-black flex items-center justify-center p-4">      {" "}      <div className="bg-neutral-800 rounded-lg p-6 max-w-xl w-full">        {" "}        <h1 className="text-white text-xl mb-4 text-center">Auth.js + Prisma</h1>      </div>{" "}    </div>   ); }; export default Page; 

Import the required components and add session checking:

title="app/page.tsx"
import { SignIn, SignOut } from "@/components/auth-components"; 

import { auth } from "@/lib/auth"; 

const Page = async () => {

  const session = await auth(); 

  return (

    <div className="min-h-screen bg-black flex items-center justify-center p-4">

      <div className="bg-neutral-800 rounded-lg p-6 max-w-xl w-full">

        <h1 className="text-white text-xl mb-4 text-center">Auth.js + Prisma</h1>

      </div>

    </div>

  );

};

export default Page;

Add the logic to show different content based on whether the user is signed in:

title="app/page.tsx"
import { SignIn, SignOut } from "@/components/auth-components";

import { auth } from "@/lib/auth";

const Page = async () => {

  const session = await auth();

  return (

    <div className="min-h-screen bg-black flex items-center justify-center p-4">

      <div className="bg-neutral-800 rounded-lg p-6 max-w-xl w-full">

        <h1 className="text-white text-xl mb-4 text-center">Auth.js + Prisma</h1>

        {!session ? (

          <div className="text-center">

            <SignIn provider="github" />

          </div>

        ) : (

          <div className="space-y-4">

            <div className="text-center">

              <p className="text-gray-300">Signed in as:</p>

              <p className="text-white">{session.user?.email}</p>

            </div>

            <div className="text-center">

              <p className="text-gray-300">Data fetched from DB with Prisma:</p>

            </div>

            <div className="text-center">

              <SignOut />

            </div>

          </div>

        )}

      </div>

    </div>

  );

};

export default Page;

If the user is signed in, you can fetch the user data from the database and display it on the page.

title="app/page.tsx"
import { SignIn, SignOut } from "@/components/auth-components";import { auth } from "@/lib/auth";import prisma from "@/lib/prisma"; const Page = async () => {  const session = await auth();  let user = null;   if (session) {    user = await prisma.user.findUnique({      where: {        id: session.user?.id,       },     });   }   return (    <div className="min-h-screen bg-black flex items-center justify-center p-4">      <div className="bg-neutral-800 rounded-lg p-6 max-w-xl w-full">        <h1 className="text-white text-xl mb-4 text-center">Auth.js + Prisma</h1>        {!session ? (          <div className="text-center">            <SignIn provider="github" />          </div>        ) : (          <div className="space-y-4">            <div className="text-center">              <p className="text-gray-300">Signed in as:</p>              <p className="text-white">{session.user?.email}</p>            </div>            <div className="text-center">              <p className="text-gray-300">Data fetched from DB with Prisma:</p>            </div>            <div className="bg-neutral-900 rounded p-3">              {" "}              <pre className="text-xs text-gray-300">                {" "}                {JSON.stringify(user, null, 2)}              </pre>{" "}            </div>{" "}            <div className="text-center">              <SignOut />            </div>          </div>        )}      </div>    </div>  );};export default Page;

Your application is now fully configured.

  1. Start the development server to test it:
bun run dev
Bash
pnpm run dev
Bash
yarn dev
Bash
npm run dev
  1. Navigate to http://localhost:3000 in your browser. You should see the home page with a "Sign In with github" button.
  2. Click on Sign In with github, authorize the app, and you should be redirected to the dashboard. You can then sign out and sign back in.
  3. To view the user data directly in your database, you can use Prisma Studio:
  1. Navigate to http://localhost:3000 in your browser. You should see the home page with a "Sign In with github" button.
  2. Click on Sign In with github, authorize the app, and you should be redirected to the dashboard. You can then sign out and sign back in.
  3. To view the user data directly in your database, you can use Prisma Studio:
bunx prisma studio
Bash
pnpm prisma studio
Bash
yarn prisma studio
Bash
npx prisma studio
  1. This will open a new tab in your browser where you can see the User, Session, and Account tables and their contents.

[!TIP] You now have a working authentication system built with Auth.js, Prisma, and Next.js.

  1. This will open a new tab in your browser where you can see the User, Session, and Account tables and their contents.
Suggest an edit

Propose a replacement for this page. The site team reviews it before applying any changes.

Export
Documentation menu