166 lines
4.7 KiB
TypeScript
166 lines
4.7 KiB
TypeScript
import { compare } from "bcrypt-ts";
|
|
import NextAuth, { type DefaultSession } from "next-auth";
|
|
import type { DefaultJWT } from "next-auth/jwt";
|
|
import Credentials from "next-auth/providers/credentials";
|
|
import { DUMMY_PASSWORD } from "@/lib/constants";
|
|
import { createGuestUser, getUser } from "@/lib/db/queries";
|
|
import { authConfig } from "./auth.config";
|
|
import { db } from "@/lib/db";
|
|
import { loginToken } from "@/lib/db/schema";
|
|
import { eq, and, gt } from "drizzle-orm";
|
|
|
|
export type UserType = "guest" | "regular" | "admin";
|
|
|
|
declare module "next-auth" {
|
|
interface Session extends DefaultSession {
|
|
user: {
|
|
id: string;
|
|
type: UserType;
|
|
name?: string | null;
|
|
} & DefaultSession["user"];
|
|
}
|
|
|
|
// biome-ignore lint/nursery/useConsistentTypeDefinitions: "Required"
|
|
interface User {
|
|
id?: string;
|
|
email?: string | null;
|
|
name?: string | null;
|
|
type: UserType;
|
|
}
|
|
}
|
|
|
|
declare module "next-auth/jwt" {
|
|
interface JWT extends DefaultJWT {
|
|
id: string;
|
|
type: UserType;
|
|
name?: string | null;
|
|
}
|
|
}
|
|
|
|
export const {
|
|
handlers: { GET, POST },
|
|
auth,
|
|
signIn,
|
|
signOut,
|
|
} = NextAuth({
|
|
...authConfig,
|
|
trustHost: true,
|
|
providers: [
|
|
Credentials({
|
|
credentials: {},
|
|
async authorize(credentials: any) {
|
|
console.log("[Auth] Authorize called with credentials:", Object.keys(credentials));
|
|
|
|
// 1. DingTalk Token 登录逻辑
|
|
if (credentials.token) {
|
|
const { token } = credentials;
|
|
console.log("[Auth] DingTalk logic triggered with token:", token ? token.substring(0, 8) + "..." : "null");
|
|
|
|
const now = new Date();
|
|
const [validToken] = await db
|
|
.select()
|
|
.from(loginToken)
|
|
.where(and(eq(loginToken.token, token), gt(loginToken.expiresAt, now)))
|
|
.limit(1);
|
|
|
|
console.log("[Auth] Token lookup result:", validToken ? {
|
|
email: validToken.email,
|
|
expiresAt: validToken.expiresAt,
|
|
isExpired: validToken.expiresAt <= now
|
|
} : "not found");
|
|
|
|
if (!validToken) {
|
|
console.log("[Auth] Token not found or expired, returning null");
|
|
return null;
|
|
}
|
|
|
|
const users = await getUser(validToken.email);
|
|
console.log("[Auth] User lookup for", validToken.email, ":", users.length, "users found");
|
|
|
|
if (users.length === 0) {
|
|
console.log("[Auth] No user found for email, returning null");
|
|
return null;
|
|
}
|
|
|
|
const user = users[0];
|
|
console.log("[Auth] User found:", { id: user.id, email: user.email, isActive: user.isActive, role: user.role });
|
|
|
|
// 检查用户是否被激活
|
|
if (!user.isActive) {
|
|
console.log("[Auth] User is not active, returning null");
|
|
return null;
|
|
}
|
|
|
|
await db.delete(loginToken).where(eq(loginToken.id, validToken.id));
|
|
console.log("[Auth] Login token deleted");
|
|
|
|
const userType: UserType = (user.role as UserType) || "regular";
|
|
const returnUser = { id: user.id, email: user.email, name: user.name, type: userType };
|
|
console.log("[Auth] Returning user object:", returnUser);
|
|
return returnUser;
|
|
}
|
|
|
|
// 2. 邮箱密码登录逻辑
|
|
const { email, password } = credentials;
|
|
const users = await getUser(email);
|
|
|
|
if (users.length === 0) {
|
|
await compare(password, DUMMY_PASSWORD);
|
|
return null;
|
|
}
|
|
|
|
const [user] = users;
|
|
|
|
if (!user.password) {
|
|
await compare(password, DUMMY_PASSWORD);
|
|
return null;
|
|
}
|
|
|
|
const passwordsMatch = await compare(password, user.password);
|
|
|
|
if (!passwordsMatch) {
|
|
return null;
|
|
}
|
|
|
|
// 检查用户是否被激活
|
|
if (!user.isActive) {
|
|
return null;
|
|
}
|
|
|
|
// 从数据库读取用户角色,默认为 regular
|
|
const userType: UserType = (user.role as UserType) || "regular";
|
|
|
|
return { id: user.id, email: user.email, name: user.name, type: userType };
|
|
},
|
|
}),
|
|
Credentials({
|
|
id: "guest",
|
|
credentials: {},
|
|
async authorize() {
|
|
const [guestUser] = await createGuestUser();
|
|
return { ...guestUser, type: "guest" };
|
|
},
|
|
}),
|
|
],
|
|
callbacks: {
|
|
jwt({ token, user }) {
|
|
if (user) {
|
|
token.id = user.id as string;
|
|
token.type = user.type;
|
|
token.name = user.name;
|
|
}
|
|
|
|
return token;
|
|
},
|
|
session({ session, token }) {
|
|
if (session.user) {
|
|
session.user.id = token.id;
|
|
session.user.type = token.type;
|
|
session.user.name = token.name as string;
|
|
}
|
|
|
|
return session;
|
|
},
|
|
},
|
|
});
|