Setup Auth.js for NextJS 15

authjs

Installing Auth.js

npm install next-auth@beta

Setup Environment

npx auth secret

Configure

// ./auth.ts

import { PrismaAdapter } from "@auth/prisma-adapter";
import NextAuth from "next-auth";
import Credentials from "next-auth/providers/credentials";
import prisma from "./prisma";
import bcrypt from "bcrypt";

export const { handlers, signIn, signOut, auth } = NextAuth({
  adapter: PrismaAdapter(prisma),
  secret: process.env.NEXTAUTH_SECRET,
  pages: {
    signIn: "/login",
  },
  session: {
    strategy: "jwt",
    maxAge: 30 * 24 * 60 * 60, // 30 days
  },
  providers: [
    Credentials({
      name: "credentials",
      credentials: {
        email: { label: "Email", type: "email" },
        password: { label: "Password", type: "password" },
      },
      authorize: async (credentials: Record<string, string>) => {
        try {
          const { userId, password } = credentials;
          const user = await prisma.user.findFirst({
            where: {
              OR: [{ email: userId }, { username: userId }],
            },
          });
          if (!user) return null;
          // Check if the password is correct
          const isValidPassword = await bcrypt.compare(
            password,
            user.hashedPassword
          );
          if (!isValidPassword) return null;
          return {
            fullname: user?.fullname ?? "",
            email: user?.email ?? "",
            role: user?.role?.toLocaleLowerCase() ?? "user",
            id: user?.id ?? "",
          };
        } catch (error) {
          console.error("Error authorizing credentials:", error);
          throw new Error("Invalid credentials");
        }
      },
    }),
  ],
  callbacks: {
    async jwt({ token, user }) {
      if (user) {
        token.fullname = user.fullname;
        token.role = user.role;
      }
      return token;
    },
    async session({ session, token }) {
      if (token?.role) {
        session.user.fullname = token.fullname as string;
        session.user.role = token.role as string;
      }
      return session;
    },
  },
});
// ./app/api/auth/[...nextauth]/route.ts

import { handlers } from "@/auth" // Referring to the auth.ts we just created
export const { GET, POST } = handlers
// ./middleware.ts

export { auth as middleware } from "@/auth"

Prisma Adapter

npm install @prisma/client @auth/prisma-adapter
npm install prisma --save-dev
DATABASE_URL= "postgresql://postgres:[email protected]:5432/shopcart?schema=public"
// prima.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
// prisma/schema-postgres.prisma

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}
 
generator client {
  provider = "prisma-client-js"
}
 
model User {
  id            String          @id @default(cuid())
  name          String?
  email         String          @unique
  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])
}
npx prisma db push

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *