import { TOTP, URI } from "otpauth"; export type TOTPAlgorithm = "SHA1" | "SHA256" | "SHA512"; export interface TOTPParams { secret: string; algorithm?: TOTPAlgorithm; digits?: number; period?: number; } export function generateTOTP( base32Secret: string, algorithm: TOTPAlgorithm = "SHA1", digits: number = 6, period: number = 30 ): { code: string; remaining: number } { // PCH-01-019: honour the entry's stored algorithm/digits/period; absent // params (undefined) fall through to the RFC defaults (SHA-1 / 6 / 30 s). const totp = new TOTP({ secret: base32Secret, digits, period, algorithm }); const code = totp.generate(); const remaining = period - (Math.floor(Date.now() / 1000) % period); return { code, remaining }; } export function parseOtpauthURI(uri: string): TOTPParams | null { try { const parsed = URI.parse(uri); if (parsed instanceof TOTP) { return { secret: parsed.secret.base32, algorithm: parsed.algorithm as TOTPAlgorithm, digits: parsed.digits, period: parsed.period, }; } return null; } catch { return null; } }