Files
auth-tools/tests/webauthn/trace.test.ts
T

74 lines
2.3 KiB
TypeScript

import { describe, expect, it } from "vitest";
import {
compareTraces,
parseTrace,
redactTrace,
type AuthenticationTrace,
} from "../../src/webauthn/trace";
function trace(): AuthenticationTrace {
return {
schema: "de.add-ideas.auth-tools.webauthn-trace",
version: 1,
kind: "authentication",
recordedAt: "2026-08-19T12:00:00.000Z",
request: {
user: { id: "user-id", name: "Alice", displayName: "Alice Example" },
extensions: { prf: true },
},
expectations: {
challenge: "AQID",
origin: "https://auth.example.test",
rpId: "auth.example.test",
},
credentialPublicKey: { "1": 2, "3": -7, "-1": 1 },
response: {
id: "credential-id",
rawId: "credential-id",
clientDataJSON: "e30",
authenticatorData: "AQID",
signature: "BAUG",
userHandle: "user-handle",
clientExtensionResults: { prf: { enabled: true } },
},
privacy: [],
};
}
describe("WebAuthn ceremony traces", () => {
it("parses the bounded versioned schema", () => {
expect(parseTrace(JSON.stringify(trace()))).toMatchObject({
kind: "authentication",
version: 1,
});
expect(() => parseTrace('{"version":2}')).toThrow(/schema/u);
let nested: Record<string, unknown> = {};
for (let depth = 0; depth < 70; depth += 1) nested = { nested };
expect(() =>
parseTrace(JSON.stringify({ ...trace(), request: nested })),
).toThrow(/depth limit/u);
});
it("redacts labels and top-level identifiers while preserving evidence", async () => {
const redacted = await redactTrace(trace());
expect(redacted.response.id).toMatch(/^sha256:/u);
expect(redacted.response.clientDataJSON).toBe("e30");
expect(
redacted.kind === "authentication" && redacted.response.userHandle,
).toBe("[redacted]");
expect((redacted.request.user as Record<string, string>).displayName).toBe(
"[redacted]",
);
});
it("compares nested trace state", () => {
const right = trace();
right.expectations.rpId = "other.example.test";
right.response.clientExtensionResults = { prf: { enabled: false } };
expect(compareTraces(trace(), right).map(({ path }) => path)).toEqual([
"expectations.rpId",
"response.clientExtensionResults.prf.enabled",
]);
});
});