Create Your First Authentication System with Prisma, MongoDB, and NextAuth

Creating a login system can be complex and take a lot of time. But NextAuth makes it much easier!

Getting Started

Initializing the Project

  1. Initialize your project with create-next-app:
yarn create next-app website --eslint --tailwind --app --use-yarn --ts website

This command sets up a new Next.js project with all the essential tools for building a modern web app, including ESLint for code quality, Tailwind CSS for styling, and TypeScript for type safety.

Installing Dependencies

  1. Install the core packages for authentication:
yarn add next-auth @auth/prisma-adapter @prisma/client
  • next-auth provides the core authentication functionality.
  • @auth/prisma-adapter bridges the gap between NextAuth and Prisma for seamless database interactions.
  • @prisma/client enables you to interact with your MongoDB database using Prisma’s powerful ORM.
  1. Install Prisma as a development dependency:
yarn add -D prisma

Initalizing Prisma

  • Initialize Prisma
yarn prisma init --datasource-provider mongodb
  • Add your data to the following config to .env
DATABASE_URL="mongodb://localhost:27017/yourdb"


NEXT_PUBLIC_GOOGLE_CLIENT_ID="...."
NEXT_PUBLIC_GOOGLE_CLIENT_SECRET="...."
  • Create Model Schema
prisma/schema.prisma
generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "mongodb"
  url      = env("DATABASE_URL")
}

model Account {
  id                String  @id @default(auto()) @map("_id") @db.ObjectId
  userId            String  @db.ObjectId
  type              String
  provider          String
  providerAccountId String
  refresh_token     String? @db.String
  access_token      String? @db.String
  expires_at        Int?
  token_type        String?
  scope             String?
  id_token          String? @db.String
  session_state     String?

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

  @@unique([provider, providerAccountId])
}

model Session {
  id           String   @id @default(auto()) @map("_id") @db.ObjectId
  sessionToken String   @unique
  userId       String   @db.ObjectId
  expires      DateTime
  user         User     @relation(fields: [userId], references: [id], onDelete: Cascade)
}

model User {
  id            String    @id @default(auto()) @map("_id") @db.ObjectId
  name          String?
  email         String?   @unique
  emailVerified DateTime?
  image         String?
  accounts      Account[]
  sessions      Session[]
}

model VerificationToken {
  id         String   @id @default(auto()) @map("_id") @db.ObjectId
  identifier String
  token      String   @unique
  expires    DateTime

  @@unique([identifier, token])
}
  • Push to Database
yarn prisma db push
  • Create prisma.ts in lib in the root of the project
lib/prisma.ts
import { PrismaClient } from '@prisma/client'

const prisma = new PrismaClient()

export default prisma

Coding

  1. This component utilizes NextAuth’s SessionProvider to make the session object accessible throughout your app. Here’s an example
components/Providers.tsx
"use client"

import { type Session, SessionProvider } from 'next-auth/react';
import * as React from 'react';

export default function Providers({ children, session } : { children : React.ReactNode, session: Session | null }) {
    return (
        <SessionProvider>
            {children}
        </SessionProvider>
    )
}
  1. Add Provider to the layout.
app/layout.tsx
import type { Metadata } from 'next'
import { Inter } from 'next/font/google'
import './globals.css'
import Providers from '@/components/Providers'
import { getServerSession } from "next-auth"

const inter = Inter({ subsets: ['latin'] })

export const metadata: Metadata = {
  title: 'Create Next App',
  description: 'Generated by create next app',
}

export default async function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
    const session = await getServerSession()

    return (
        <html lang="en">
            <body className={inter.className}>
                <Providers session={session}>
                    {children}
                </Providers>
            </body>
        </html>
    )
}

Creating Authentication System

  • Create authOptions.ts in app/api/auth/[...nextauth]/authOptions.ts

This file configures NextAuth. Here’s an example

app/api/auth/[...nextauth]/authOptions.ts
import { type AuthOptions } from "next-auth";
import GoogleProvider from "next-auth/providers/google";
import { PrismaAdapter } from '@auth/prisma-adapter';
import prisma from "@/lib/prisma";

const authOptions: AuthOptions = {
    adapter: PrismaAdapter(prisma),
    providers: [
        GoogleProvider({
            clientId: process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID,
            clientSecret: process.env.NEXT_PUBLIC_GOOGLE_CLIENT_SECRET
        })
    ],
    debug: process.env.NODE_ENV == "development" ? true : false
}

export default authOptions;
  • Create authOptions.ts in app/api/auth/[...nextauth]/

This file defines the NextAuth API route handler. Here’s an example

app/api/auth/[...nextauth]/route.ts
import NextAuth from "next-auth/next";
import authOptions from './authOptions.ts'

const handler = NextAuth(authOptions)

export { handler as GET, handler as POST }

Configure routes and protected content

  • Create middleware.ts in root of the project (You can use regex)
middleware.ts
export { default } from "next-auth/middleware"

export const config = { matcher: ["/protected"] }

Deploying

Deploying the project need some configuration

  • Configure Your package.json
package.json
{
  ...
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint",
    "postinstall": "prisma generate"
  },
  ...
}

Congratulations! You’ve successfully set up an authentication system using NextAuth, Prisma, and MongoDB for your Next.js project. This system provides a secure and efficient way to manage user authentication and sessions.

Happy Coding🥳