File size: 2,585 Bytes
abc1805
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
import NextAuth, { CredentialsSignin } from "next-auth";
import Credentials from "next-auth/providers/credentials";
import { compare } from "bcryptjs";
import User from "@/models/User";
import dbConnect from "@/lib/db";
import { verifyTwoFactorToken } from "@/lib/tokens";

class TwoFactorRequired extends CredentialsSignin {
    code = "2FA_REQUIRED";
}

class InvalidToken extends CredentialsSignin {
    code = "INVALID_2FA_TOKEN";
}

export const { handlers, signIn, signOut, auth } = NextAuth({
    providers: [
        Credentials({
            credentials: {
                email: { label: "Email", type: "email" },
                password: { label: "Password", type: "password" },
                token: { label: "2FA Token", type: "text" },
            },
            authorize: async (credentials) => {
                await dbConnect();

                const email = credentials.email as string;
                const password = credentials.password as string;
                const token = credentials.token as string | undefined;

                if (!email || !password) {
                    throw new Error("Missing credentials");
                }

                const user = await User.findOne({ email });

                if (!user || !user.password) {
                    throw new Error("Invalid credentials");
                }

                const isPasswordValid = await compare(password, user.password);

                if (!isPasswordValid) {
                    throw new Error("Invalid credentials");
                }

                if (user.isTwoFactorEnabled) {
                    if (!token) {
                        throw new TwoFactorRequired();
                    }

                    const isTokenValid = verifyTwoFactorToken(token, user.twoFactorSecret!);

                    if (!isTokenValid) {
                        throw new InvalidToken();
                    }
                }

                return {
                    id: user._id.toString(),
                    email: user.email,
                    image: user.image,
                };
            },
        }),
    ],
    pages: {
        signIn: "/login",
    },
    callbacks: {
        async session({ session, token }) {
            if (token.sub && session.user) {
                session.user.id = token.sub;
            }
            return session;
        },
        async jwt({ token }) {
            return token;
        },
    },
    session: { strategy: "jwt" },
});