53 lines
2.4 KiB
TypeScript
53 lines
2.4 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import {
|
|
createPasswordResetTokenUpdate,
|
|
createPasswordResetVerifyToken,
|
|
hashPasswordResetToken,
|
|
isPasswordResetTokenValid,
|
|
PASSWORD_RESET_TOKEN_TTL_MS,
|
|
} from "./password-reset-token.server";
|
|
|
|
describe("password reset token helpers", () => {
|
|
it("creates token update data with an issue timestamp", () => {
|
|
const issuedAt = new Date("2026-05-11T12:00:00.000Z");
|
|
|
|
expect(createPasswordResetTokenUpdate("token_1", issuedAt)).toEqual({
|
|
verifyToken: hashPasswordResetToken("token_1"),
|
|
verifyTokenCreatedAt: issuedAt,
|
|
});
|
|
});
|
|
|
|
it("creates high-entropy reset tokens as hex strings", () => {
|
|
const token = createPasswordResetVerifyToken();
|
|
|
|
expect(token).toMatch(/^[a-f0-9]{40}$/);
|
|
});
|
|
|
|
it("accepts matching tokens within the reset TTL", () => {
|
|
const issuedAt = new Date("2026-05-11T12:00:00.000Z");
|
|
const now = new Date(issuedAt.getTime() + PASSWORD_RESET_TOKEN_TTL_MS - 1);
|
|
|
|
expect(isPasswordResetTokenValid({ verifyToken: hashPasswordResetToken("token_1"), verifyTokenCreatedAt: issuedAt }, "token_1", now)).toBe(true);
|
|
});
|
|
|
|
it("accepts legacy plaintext tokens during migration", () => {
|
|
const issuedAt = new Date("2026-05-11T12:00:00.000Z");
|
|
|
|
expect(isPasswordResetTokenValid({ verifyToken: "token_1", verifyTokenCreatedAt: issuedAt }, "token_1", issuedAt)).toBe(true);
|
|
});
|
|
|
|
it("rejects missing, mismatched, or expired tokens", () => {
|
|
const issuedAt = new Date("2026-05-11T12:00:00.000Z");
|
|
const expiredNow = new Date(issuedAt.getTime() + PASSWORD_RESET_TOKEN_TTL_MS + 1);
|
|
|
|
expect(isPasswordResetTokenValid({ verifyToken: "token_1", verifyTokenCreatedAt: null }, "token_1", issuedAt)).toBe(false);
|
|
expect(isPasswordResetTokenValid({ verifyToken: hashPasswordResetToken("token_1"), verifyTokenCreatedAt: issuedAt }, "", issuedAt)).toBe(false);
|
|
expect(isPasswordResetTokenValid({ verifyToken: hashPasswordResetToken("token_1"), verifyTokenCreatedAt: issuedAt }, "wrong", issuedAt)).toBe(false);
|
|
expect(isPasswordResetTokenValid({ verifyToken: hashPasswordResetToken("token_1"), verifyTokenCreatedAt: issuedAt }, "token_1", expiredNow)).toBe(false);
|
|
});
|
|
|
|
it("rejects malformed token issue timestamps", () => {
|
|
expect(isPasswordResetTokenValid({ verifyToken: hashPasswordResetToken("token_1"), verifyTokenCreatedAt: "not-a-date" }, "token_1")).toBe(false);
|
|
});
|
|
});
|