1.9 KiB
uri/format-uri
Purpose
Build a canonical otpauth:// URI from an OtpUri value object. Round-trips
with parseUri for every valid input. Percent-encodes the label so colons
and spaces survive transport; rejects literal : in the label (the colon is
reserved for the issuer-prefix convention and an unencoded : would produce
an ambiguous URI).
Features
- TOTP and HOTP variants.
- Label and issuer percent-encoded.
- Query parameters emitted in a stable order (
secret, issuer, algorithm, digits, period | counter). - Strict-mode Zod boundary at the entry point.
Usage: Basic Example
import { formatUri } from "@suaveplan/otp/uri";
const url = formatUri({
type: "totp",
label: "alice@example.com",
secret: "JBSWY3DPEHPK3PXP",
issuer: "Example",
});
Usage: Advanced Example
import { formatUri } from "@suaveplan/otp/uri";
const url = formatUri({
type: "hotp",
label: "alice",
secret: "JBSWY3DPEHPK3PXP",
counter: 17n,
algorithm: "SHA256",
digits: 8,
});
API Reference
formatUri(opts: OtpUri): string— returns the URI. RaisesOtpError(OTP.URI_MALFORMED)on Zod failure or on labels containing:.
Implementation Notes
The output is a stable canonical form so two calls with identically-typed
input produce byte-identical URLs. This matters when the URI is hashed for
QR-code de-duplication or audited via diff tooling. Field order is preserved
via a Map rather than a plain object so insertion order is stable across
JS engines.
Optional fields are omitted when they match the per-spec default — e.g.
TOTP URIs without a period parameter elide it rather than emitting
period=30. This keeps the output minimal and matches the wire format
emitted by reference Google Authenticator implementations. Round-tripping
through parseUri recovers the canonical option bag because both ends
agree on the defaults; consumers that need an explicit period=30 should
set it on the input.