totp.test.ts

2.1 KB · raw

1import { describe, expect, it } from "vitest";2import { TOTP } from "otpauth";34import { generateTOTP, parseOtpauthURI } from "./totp";56// PCH-01-019: algorithm/digits/period must be threaded through, with RFC7// defaults (SHA-1 / 6 / 30) when absent so legacy bare-seed entries are stable.89const SECRET = "JBSWY3DPEHPK3PXP";1011describe("generateTOTP (PCH-01-019)", () => {12  it("honours SHA256 / 8-digit / 60s params", () => {13    const { code, remaining } = generateTOTP(SECRET, "SHA256", 8, 60);14    const expected = new TOTP({ secret: SECRET, algorithm: "SHA256", digits: 8, period: 60 }).generate();15    expect(code).toBe(expected);16    expect(code.length).toBe(8);17    expect(remaining).toBeGreaterThan(0);18    expect(remaining).toBeLessThanOrEqual(60);19  });2021  it("legacy bare seed (no params) generates the SHA-1 / 6 / 30 default", () => {22    const { code, remaining } = generateTOTP(SECRET);23    const expected = new TOTP({ secret: SECRET, algorithm: "SHA1", digits: 6, period: 30 }).generate();24    expect(code).toBe(expected);25    expect(code.length).toBe(6);26    expect(remaining).toBeLessThanOrEqual(30);27  });2829  it("undefined optional args fall through to defaults", () => {30    const a = generateTOTP(SECRET, undefined, undefined, undefined);31    const b = generateTOTP(SECRET);32    expect(a.code).toBe(b.code);33  });34});3536describe("parseOtpauthURI (PCH-01-019)", () => {37  it("extracts algorithm, digits, and period", () => {38    const parsed = parseOtpauthURI(39      `otpauth://totp/Example:alice?secret=${SECRET}&algorithm=SHA256&digits=8&period=60&issuer=Example`,40    );41    expect(parsed).toMatchObject({42      secret: SECRET,43      algorithm: "SHA256",44      digits: 8,45      period: 60,46    });47  });4849  it("defaults a bare otpauth URI to SHA1 / 6 / 30", () => {50    const parsed = parseOtpauthURI(`otpauth://totp/Example:alice?secret=${SECRET}&issuer=Example`);51    expect(parsed).toMatchObject({ secret: SECRET, algorithm: "SHA1", digits: 6, period: 30 });52  });5354  it("returns null for non-otpauth input", () => {55    expect(parseOtpauthURI("not-a-uri")).toBeNull();56  });57});