refactor: extract shared login credential verification into auth/login-core
The username normalization, dummy-hash constant, password check and email-verification gate were duplicated between precheckLogin and the NextAuth credentials authorize handler. Move them into a single login-core module so both paths share one source of truth and stay consistent.
This commit is contained in:
1 parent
e3ed37d170
commit
89c1751e79
4 files changed
+192
-179
No files matched your search
File diff suppressed because it is too large.
Load diff
@@ -1,11 +1,14 @@
|
|||||||
"use server";
|
"use server";
|
||||||
|
|
||||||
import { env } from "@/env";
|
import {
|
||||||
import { checkLogin } from "@/lib/auth/password";
|
getLoginUser,
|
||||||
import { queryPreparedOne } from "@/lib/db";
|
isEmailUnverified,
|
||||||
|
normalizeLoginInput,
|
||||||
|
runDummyHashCheck,
|
||||||
|
verifyLoginPassword,
|
||||||
|
} from "@/lib/auth/login-core";
|
||||||
import { clientIp, rateLimit } from "@/lib/rate-limit";
|
import { clientIp, rateLimit } from "@/lib/rate-limit";
|
||||||
import { captchaConfig, verifyCaptcha } from "@/lib/services/captcha";
|
import { captchaConfig, verifyCaptcha } from "@/lib/services/captcha";
|
||||||
import { siteSettings } from "@/lib/services/site-settings";
|
|
||||||
|
|
||||||
export type PrecheckResult =
|
export type PrecheckResult =
|
||||||
| "ok"
|
| "ok"
|
||||||
@@ -24,10 +27,7 @@ export async function precheckLogin(
|
|||||||
password: string,
|
password: string,
|
||||||
captchaToken?: string | null,
|
captchaToken?: string | null,
|
||||||
): Promise<PrecheckResult> {
|
): Promise<PrecheckResult> {
|
||||||
const u = String(username ?? "")
|
const { username: u, password: p } = normalizeLoginInput(username, password);
|
||||||
.normalize("NFC")
|
|
||||||
.trim();
|
|
||||||
const p = String(password ?? "").normalize("NFC");
|
|
||||||
if (!u || !p) return "invalid";
|
if (!u || !p) return "invalid";
|
||||||
|
|
||||||
const ip = await clientIp();
|
const ip = await clientIp();
|
||||||
@@ -38,49 +38,17 @@ export async function precheckLogin(
|
|||||||
if (!(await verifyCaptcha(captchaToken ?? null, ip))) return "captcha";
|
if (!(await verifyCaptcha(captchaToken ?? null, ip))) return "captcha";
|
||||||
}
|
}
|
||||||
|
|
||||||
let user: {
|
const user = await getLoginUser(u);
|
||||||
password: string;
|
|
||||||
twoFactorConfirmedAt: Date | null;
|
|
||||||
mail: string | null;
|
|
||||||
mailVerified: string;
|
|
||||||
} | null;
|
|
||||||
try {
|
|
||||||
user = await queryPreparedOne<{
|
|
||||||
password: string;
|
|
||||||
twoFactorConfirmedAt: Date | null;
|
|
||||||
mail: string | null;
|
|
||||||
mailVerified: string;
|
|
||||||
}>(
|
|
||||||
`SELECT password, two_factor_confirmed_at AS twoFactorConfirmedAt,
|
|
||||||
mail, mail_verified AS mailVerified
|
|
||||||
FROM users WHERE username = ? LIMIT 1`,
|
|
||||||
[u],
|
|
||||||
);
|
|
||||||
} catch {
|
|
||||||
return "invalid";
|
|
||||||
}
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
// Prevent timing-based enumeration: always run a dummy hash check.
|
// Prevent timing-based enumeration: always run a dummy hash check.
|
||||||
await checkLogin(
|
await runDummyHashCheck(p);
|
||||||
p,
|
|
||||||
"$2y$12$abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcd",
|
|
||||||
{
|
|
||||||
convertPasswords: false,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
return "invalid";
|
return "invalid";
|
||||||
}
|
}
|
||||||
|
|
||||||
const res = await checkLogin(p, user.password, {
|
const res = await verifyLoginPassword(user, p);
|
||||||
convertPasswords: env.CONVERT_PASSWORDS,
|
|
||||||
});
|
|
||||||
if (!res.valid) return "invalid";
|
if (!res.valid) return "invalid";
|
||||||
|
|
||||||
if (
|
if (await isEmailUnverified(user)) {
|
||||||
(await siteSettings.getBool("require_email_verification", false)) &&
|
|
||||||
user.mail &&
|
|
||||||
user.mailVerified !== "1"
|
|
||||||
) {
|
|
||||||
return "unverified";
|
return "unverified";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+19
-94
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user