feat(auth): add login_attempts schema, lockout config, dummy-hash helper
All checks were successful
Deploy to Homelab / deploy (push) Successful in 29s
All checks were successful
Deploy to Homelab / deploy (push) Successful in 29s
Lays the foundation for brute-force defense: per-username attempt tracking table, configurable lockout/rate-limit thresholds, and a memoized dummy bcrypt hash so unknown-user paths can be timed identically to wrong-password paths in a later step. Adds @fastify/rate-limit dependency for upcoming per-IP rate-limit on login routes.
This commit is contained in:
25
src/services/dummy-hash.ts
Normal file
25
src/services/dummy-hash.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import bcrypt from 'bcrypt';
|
||||
import { hashPassword } from './auth.ts';
|
||||
|
||||
let cachedHash: Promise<string> | null = null;
|
||||
|
||||
function getDummyHash(): Promise<string> {
|
||||
if (!cachedHash) {
|
||||
// Hash a value no caller will ever submit (cryptographically random
|
||||
// string generated once at module init). Cost factor matches real users
|
||||
// because hashPassword uses the same SALT_ROUNDS.
|
||||
const seed = `dummy:${Date.now()}:${Math.random()}:${process.pid}`;
|
||||
cachedHash = hashPassword(seed);
|
||||
}
|
||||
return cachedHash;
|
||||
}
|
||||
|
||||
export async function verifyAgainstDummy(password: string): Promise<boolean> {
|
||||
const hash = await getDummyHash();
|
||||
await bcrypt.compare(password, hash);
|
||||
return false;
|
||||
}
|
||||
|
||||
export function _resetDummyHashForTests(): void {
|
||||
cachedHash = null;
|
||||
}
|
||||
Reference in New Issue
Block a user