refactor(auth): single digest registry, extracted 2FA and login-log, dep bumps
- password.ts: derive plain and salted digest detection from one DIGEST_SCHEMES table instead of parallel hardcoded lists, so adding a family is one row. - auth.ts: move 2FA challenge verification into twofactor-verification.ts and the website login-log insert into website-login-log.ts, slimming the NextAuth provider to orchestration only. - deps: bump @formatjs/icu-messageformat-parser, @tanstack/react-query, jszip, lucide-react, motion (patch/minor only). @types/react stay pinned per pnpm-workspace.yaml; next-auth is already at the newest available (v5 beta).
This commit is contained in:
+18
-13
@@ -44,6 +44,21 @@ async function isHexDigestOf(
|
||||
return (await hashFn(password)) === stored.toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* Single source of truth for the supported unsalted digest families. Both the
|
||||
* plain single-digest checks and the salted detection below derive from here,
|
||||
* so adding a family (e.g. sha384) means adding a single row.
|
||||
*/
|
||||
const DIGEST_SCHEMES: ReadonlyArray<{
|
||||
length: number;
|
||||
hash: (input: string) => Promise<string>;
|
||||
}> = [
|
||||
{ length: 32, hash: md5Hex },
|
||||
{ length: 40, hash: sha1Hex },
|
||||
{ length: 64, hash: sha256Hex },
|
||||
{ length: 128, hash: sha512Hex },
|
||||
];
|
||||
|
||||
export async function isMd5Of(
|
||||
password: string,
|
||||
stored: string,
|
||||
@@ -72,16 +87,6 @@ export async function isSha512Of(
|
||||
return isHexDigestOf(password, stored, 128, sha512Hex);
|
||||
}
|
||||
|
||||
const DIGEST_HEX_LENGTHS: ReadonlyArray<{
|
||||
length: number;
|
||||
hash: (input: string) => Promise<string>;
|
||||
}> = [
|
||||
{ length: 32, hash: md5Hex },
|
||||
{ length: 40, hash: sha1Hex },
|
||||
{ length: 64, hash: sha256Hex },
|
||||
{ length: 128, hash: sha512Hex },
|
||||
];
|
||||
|
||||
/**
|
||||
* Combined/double digest conventions used by legacy CMSes and forums, e.g.
|
||||
* md5(md5(pass)) (UberCMS/Butterfly), md5(sha1(pass)), sha1(md5(pass)),
|
||||
@@ -133,7 +138,7 @@ export async function isSaltedDigestOf(
|
||||
): Promise<boolean> {
|
||||
if (!SALTED_HASH_DELIMITER_RE.test(stored)) return false;
|
||||
|
||||
for (const { length, hash: hashFn } of DIGEST_HEX_LENGTHS) {
|
||||
for (const { length, hash: hashFn } of DIGEST_SCHEMES) {
|
||||
const hashFirst = stored.match(
|
||||
new RegExp(`^([a-f0-9]{${length}})[:\$@_](.{1,64})$`, "i"),
|
||||
);
|
||||
@@ -225,8 +230,8 @@ export async function checkLogin(
|
||||
}
|
||||
|
||||
// Legacy digest formats (hex) — verify + auto-upgrade to bcrypt.
|
||||
for (const check of [isMd5Of, isSha1Of, isSha256Of, isSha512Of]) {
|
||||
if (await check(password, stored)) {
|
||||
for (const { length, hash: hashFn } of DIGEST_SCHEMES) {
|
||||
if (await isHexDigestOf(password, stored, length, hashFn)) {
|
||||
return { valid: true, upgradedHash: await hashPassword(password) };
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user