| import { NextAuthOptions } from "next-auth"; |
| import CredentialsProvider from "next-auth/providers/credentials"; |
| import { prisma } from "@/lib/prisma"; |
| import bcrypt from "bcryptjs"; |
|
|
| const SINGLE_USER_MODE = process.env.NEXT_PUBLIC_SINGLE_USER_MODE === "true"; |
| const ALLOWED_EMAIL = process.env.SINGLE_USER_EMAIL || "admin@tinker.local"; |
| const SINGLE_USER_PASSWORD = process.env.SINGLE_USER_PASSWORD || "tinker123!"; |
|
|
| export const authOptions: NextAuthOptions = { |
| providers: [ |
| CredentialsProvider({ |
| name: "credentials", |
| credentials: { |
| email: { label: "Email", type: "email" }, |
| password: { label: "Password", type: "password" }, |
| }, |
| async authorize(credentials) { |
| if (!credentials?.email || !credentials?.password) { |
| throw new Error("Email and password are required"); |
| } |
|
|
| |
| if (SINGLE_USER_MODE) { |
| if (credentials.email !== ALLOWED_EMAIL) { |
| throw new Error("Access denied: this instance is limited to a single user."); |
| } |
| |
| let user = await prisma.user.findUnique({ |
| where: { email: ALLOWED_EMAIL }, |
| }); |
| if (!user) { |
| const hashedPassword = await bcrypt.hash(SINGLE_USER_PASSWORD, 10); |
| user = await prisma.user.create({ |
| data: { |
| email: ALLOWED_EMAIL, |
| name: "Tinker Admin", |
| password: hashedPassword, |
| }, |
| }); |
| } |
| return { |
| id: user.id, |
| name: user.name, |
| email: user.email, |
| image: user.image, |
| }; |
| } |
|
|
| |
| const user = await prisma.user.findUnique({ |
| where: { email: credentials.email }, |
| }); |
|
|
| if (!user) { |
| throw new Error("No user found with this email"); |
| } |
|
|
| if (!user.password) { |
| throw new Error("This account does not have a password set."); |
| } |
|
|
| const isPasswordValid = await bcrypt.compare( |
| credentials.password, |
| user.password |
| ); |
|
|
| if (!isPasswordValid) { |
| throw new Error("Invalid password"); |
| } |
|
|
| return { |
| id: user.id, |
| name: user.name, |
| email: user.email, |
| image: user.image, |
| }; |
| }, |
| }), |
| ], |
| session: { |
| strategy: "jwt", |
| maxAge: 30 * 24 * 60 * 60, |
| }, |
| pages: { |
| signIn: "/login", |
| error: "/login", |
| }, |
| callbacks: { |
| async jwt({ token, user }) { |
| if (user) { |
| token.id = user.id; |
| } |
| return token; |
| }, |
| async session({ session, token }) { |
| if (session.user) { |
| session.user.id = token.id as string; |
| } |
| return session; |
| }, |
| }, |
| secret: process.env.NEXTAUTH_SECRET, |
| }; |
|
|