diff --git a/server/utils/challenge-token.ts b/server/utils/challenge-token.ts new file mode 100644 index 0000000..eef222d --- /dev/null +++ b/server/utils/challenge-token.ts @@ -0,0 +1,9 @@ +import { createHash, randomBytes } from "node:crypto"; + +export function randomUrlToken(): string { + return randomBytes(32).toString("base64url"); +} + +export function hashChallengeToken(token: string): string { + return createHash("sha256").update(token, "utf8").digest("hex"); +} diff --git a/server/utils/password.ts b/server/utils/password.ts new file mode 100644 index 0000000..6a404c5 --- /dev/null +++ b/server/utils/password.ts @@ -0,0 +1,15 @@ +import bcrypt from "bcryptjs"; + +const ROUNDS = 10; + +export async function hashPassword(plain: string): Promise { + const salt = await bcrypt.genSalt(ROUNDS); + return bcrypt.hash(plain, salt); +} + +export async function verifyPassword( + plain: string, + hash: string, +): Promise { + return bcrypt.compare(plain, hash); +} diff --git a/test/unit/challenge-token.test.ts b/test/unit/challenge-token.test.ts new file mode 100644 index 0000000..a04a023 --- /dev/null +++ b/test/unit/challenge-token.test.ts @@ -0,0 +1,15 @@ +import { describe, expect, it } from "bun:test"; +import { + hashChallengeToken, + randomUrlToken, +} from "../../server/utils/challenge-token"; + +describe("challenge-token", () => { + it("hash is stable", () => { + expect(hashChallengeToken("abc")).toBe(hashChallengeToken("abc")); + }); + + it("random has reasonable length", () => { + expect(randomUrlToken().length).toBeGreaterThan(20); + }); +}); diff --git a/test/unit/password.test.ts b/test/unit/password.test.ts new file mode 100644 index 0000000..6da7425 --- /dev/null +++ b/test/unit/password.test.ts @@ -0,0 +1,10 @@ +import { describe, expect, it } from "bun:test"; +import { hashPassword, verifyPassword } from "../../server/utils/password"; + +describe("password", () => { + it("hashes and verifies", async () => { + const h = await hashPassword("hunter2"); + expect(await verifyPassword("hunter2", h)).toBe(true); + expect(await verifyPassword("wrong", h)).toBe(false); + }); +});