aboutsummaryrefslogtreecommitdiff
path: root/src/lib/auth.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/auth.ts')
-rw-r--r--src/lib/auth.ts21
1 files changed, 19 insertions, 2 deletions
diff --git a/src/lib/auth.ts b/src/lib/auth.ts
index 0ed9d12..ad47d5f 100644
--- a/src/lib/auth.ts
+++ b/src/lib/auth.ts
@@ -1,6 +1,7 @@
import { type NextAuthOptions } from "next-auth"
import CredentialsProvider from "next-auth/providers/credentials"
import bcrypt from "bcryptjs"
+import { authenticator } from "otplib"
import dbConnect from "./mongodb"
import User from "@/model/User"
import { loginSchema } from "./validation"
@@ -11,7 +12,8 @@ export const authOptions: NextAuthOptions = {
name: "credentials",
credentials: {
email: { label: "Email", type: "email" },
- password: { label: "Password", type: "password" }
+ password: { label: "Password", type: "password" },
+ twoFactorCode: { label: "2FA Code", type: "text" }
},
async authorize(credentials) {
if (!credentials?.email || !credentials?.password) return null
@@ -19,7 +21,7 @@ export const authOptions: NextAuthOptions = {
const result = loginSchema.safeParse(credentials)
if (!result.success) return null
- const { email, password } = result.data
+ const { email, password, twoFactorCode } = result.data
try {
await dbConnect()
@@ -30,6 +32,17 @@ export const authOptions: NextAuthOptions = {
const isPasswordValid = await bcrypt.compare(password, user.password)
if (!isPasswordValid) return null
+ if (user.twoFactorEnabled) {
+ if (!twoFactorCode) {
+ throw new Error("2FA_REQUIRED")
+ }
+
+ const isValid = authenticator.check(twoFactorCode, user.twoFactorSecret)
+ if (!isValid) {
+ throw new Error("Invalid 2FA Code")
+ }
+ }
+
return {
id: user._id.toString(),
email: user.email,
@@ -38,6 +51,10 @@ export const authOptions: NextAuthOptions = {
}
} catch (error) {
console.error("Auth error:", error)
+ // Rethrow specific 2FA errors so they reach the client
+ if (error instanceof Error && (error.message === "2FA_REQUIRED" || error.message === "Invalid 2FA Code")) {
+ throw error
+ }
return null
}
}