Passwoerter mit Argon2id, im Cookie steht nur ein Token und in der Datenbank dessen Hash. Das CSRF-Token wird per HMAC aus dem Sitzungstoken abgeleitet statt separat gespeichert - ohne das httpOnly-Cookie ist es nicht herstellbar. TOTP ist optional und wird erst nach einem bestaetigten Code scharf geschaltet, damit sich niemand bei der Einrichtung aussperrt.
37 lines
996 B
TypeScript
37 lines
996 B
TypeScript
import { hash, verify } from '@node-rs/argon2';
|
|
|
|
// OWASP-Empfehlung fuer Argon2id: 19 MiB, 2 Durchlaeufe, Parallelitaet 1.
|
|
const options = {
|
|
memoryCost: 19456,
|
|
timeCost: 2,
|
|
parallelism: 1,
|
|
} as const;
|
|
|
|
export function hashPassword(plain: string): Promise<string> {
|
|
return hash(plain, options);
|
|
}
|
|
|
|
export async function verifyPassword(digest: string, plain: string): Promise<boolean> {
|
|
try {
|
|
return await verify(digest, plain, options);
|
|
} catch {
|
|
// Kaputter oder fremdformatiger Hash gilt als nicht passend.
|
|
return false;
|
|
}
|
|
}
|
|
|
|
const MIN_LENGTH = 12;
|
|
|
|
export function validatePasswordStrength(plain: string): string | null {
|
|
if (plain.length < MIN_LENGTH) {
|
|
return `Passwort muss mindestens ${MIN_LENGTH} Zeichen lang sein`;
|
|
}
|
|
if (!/[a-z]/.test(plain) || !/[A-Z]/.test(plain)) {
|
|
return 'Passwort muss Gross- und Kleinbuchstaben enthalten';
|
|
}
|
|
if (!/[0-9]/.test(plain)) {
|
|
return 'Passwort muss mindestens eine Ziffer enthalten';
|
|
}
|
|
return null;
|
|
}
|