Files
auth-tools/tests/otp/diagnostics.test.ts

55 lines
1.7 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { utf8ToBytes } from "../../src/crypto/encoding";
import {
credentialHealthReport,
findTotpDrift,
generateTotpTimeline,
} from "../../src/otp/diagnostics";
import type { OtpProfile } from "../../src/otp/profile";
const profile: OtpProfile = {
kind: "totp",
secret: utf8ToBytes("12345678901234567890"),
issuer: "RFC",
account: "vector",
algorithm: "SHA-1",
digits: 8,
period: 30,
epoch: 0,
counter: 0n,
extensions: new Map(),
};
describe("OTP diagnostics", () => {
it("builds an ordered RFC timeline and finds clock drift", async () => {
const timeline = await generateTotpTimeline(profile, 59, 1, 2);
expect(timeline.map((item) => item.delta)).toEqual([-1, 0, 1, 2]);
expect(timeline[1]).toMatchObject({ code: "94287082", counter: 1n });
const drift = await findTotpDrift(timeline[2]!.code, profile, 59, 5);
expect(drift).toMatchObject({ delta: 1, driftSeconds: 30 });
});
it("honors a custom T0 and bounds diagnostic work", async () => {
const shifted = { ...profile, epoch: 30 };
expect((await generateTotpTimeline(shifted, 59, 0, 0))[0]!.counter).toBe(
0n,
);
await expect(
findTotpDrift("12345678", profile, 59, 10_001),
).rejects.toThrow(/10000/u);
});
it("detects duplicate secrets without exposing their fingerprint", async () => {
const report = await credentialHealthReport([
profile,
{ ...profile, issuer: "Other", account: "second" },
]);
expect(report.findings).toEqual(
expect.arrayContaining([
expect.objectContaining({ code: "reused-secret", severity: "danger" }),
]),
);
expect(JSON.stringify(report)).not.toContain("31323334");
});
});