Een dating-app op basis van brieven, niet van gezichten!
Deze repository wordt als momentopname gepubliceerd: een clone bevat de volledige inhoud, maar geen commitgeschiedenis.
import { randomBytes, scrypt as scryptCb, timingSafeEqual, createHash } from "node:crypto";
import { promisify } from "node:util";
const scrypt = promisify(scryptCb);
/*
* Wachtwoorden: scrypt (in Node ingebouwd, geen extra afhankelijkheid).
* Opslagformaat: "scrypt$N$r$p$salt$hash" (salt en hash base64url), zodat de
* parameters later verhoogd kunnen worden zonder bestaande hashes te breken.
*/
const SCRYPT = { N: 16384, r: 8, p: 1, keylen: 32 };
const b64 = (buf) => buf.toString("base64url");
const unb64 = (s) => Buffer.from(s, "base64url");
export async function hashPassword(password, salt = randomBytes(16)) {
const { N, r, p, keylen } = SCRYPT;
const hash = await scrypt(password.normalize("NFKC"), salt, keylen, { N, r, p });
return `scrypt$${N}$${r}$${p}$${b64(salt)}$${b64(hash)}`;
}
export async function verifyPassword(password, stored) {
const [alg, N, r, p, salt, hash] = (stored ?? "").split("$");
if (alg !== "scrypt") return false;
const expected = unb64(hash);
const actual = await scrypt(password.normalize("NFKC"), unb64(salt), expected.length, { N: +N, r: +r, p: +p });
return timingSafeEqual(actual, expected);
}
/*
* Sessies: de client krijgt een willekeurig token; in de database staat alleen
* de sha256 ervan, zodat een gelekte tabel geen bruikbare tokens oplevert.
*/
export const newToken = () => b64(randomBytes(32));
export const hashToken = (token) => createHash("sha256").update(token).digest("hex");