Files
xiaomai 377a9617be feat: implement auth system, passkeys, and user management
Add PostgreSQL and Redis integration for users and sessions
Implement password and WebAuthn passkey login flows
Add Docker stack, super-admin seeding, and protected routes
2026-04-12 20:16:43 +08:00

53 lines
1.3 KiB
TypeScript

import { randomBytes, scrypt as scryptCallback, timingSafeEqual } from 'node:crypto'
import { promisify } from 'node:util'
import { decodeBase64Url, encodeBase64Url } from './base64url'
const scrypt = promisify(scryptCallback)
const SCRYPT_COST = 16_384
const SCRYPT_BLOCK_SIZE = 8
const SCRYPT_PARALLELIZATION = 1
const KEY_LENGTH = 64
export async function hashPassword(password: string): Promise<string> {
const salt = encodeBase64Url(randomBytes(16))
const derivedKey = await scrypt(password, salt, KEY_LENGTH, {
N: SCRYPT_COST,
r: SCRYPT_BLOCK_SIZE,
p: SCRYPT_PARALLELIZATION
}) as Buffer
return [
'scrypt',
SCRYPT_COST,
SCRYPT_BLOCK_SIZE,
SCRYPT_PARALLELIZATION,
salt,
encodeBase64Url(derivedKey)
].join('$')
}
export async function verifyPassword(password: string, storedHash: string): Promise<boolean> {
const [algorithm, cost, blockSize, parallelization, salt, key] = storedHash.split('$')
if (
algorithm !== 'scrypt'
|| !cost
|| !blockSize
|| !parallelization
|| !salt
|| !key
) {
return false
}
const expectedKey = decodeBase64Url(key)
const derivedKey = await scrypt(password, salt, expectedKey.length, {
N: Number(cost),
r: Number(blockSize),
p: Number(parallelization)
}) as Buffer
return timingSafeEqual(expectedKey, derivedKey)
}