Files
auth-tools/tests/otp/collection.test.ts
T

58 lines
1.7 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { utf8ToBytes } from "../../src/crypto/encoding";
import {
compareCollectionSnapshots,
createCollectionSnapshot,
parseCollectionSnapshot,
} from "../../src/otp/collection";
import type { OtpProfile } from "../../src/otp/profile";
function profile(account: string, secret: string): OtpProfile {
return {
kind: "totp",
issuer: "Example",
account,
secret: utf8ToBytes(secret),
algorithm: "SHA-1",
digits: 6,
period: 30,
counter: 0n,
extensions: new Map(),
};
}
describe("OTP collection snapshots", () => {
it("exports fingerprints without secrets", async () => {
const snapshot = await createCollectionSnapshot([
profile("alice", "top-secret-value"),
]);
const serialized = JSON.stringify(snapshot);
expect(serialized).not.toContain("top-secret-value");
expect(parseCollectionSnapshot(serialized)).toEqual(snapshot);
});
it("detects renames, parameter changes and secret reuse", async () => {
const before = await createCollectionSnapshot([
profile("alice", "secret-a"),
profile("bob", "secret-b"),
]);
const bob = profile("bob", "secret-b");
bob.digits = 8;
const after = await createCollectionSnapshot([
profile("alice-renamed", "secret-a"),
bob,
profile("carol", "secret-b"),
]);
const result = compareCollectionSnapshots(before, after);
expect(result.changes.map(({ kind }) => kind)).toEqual(
expect.arrayContaining([
"renamed",
"parameters-changed",
"added",
"secret-reused",
]),
);
expect(result.rotationPlan[0]?.kind).toBe("secret-reused");
});
});