Files
skills/docs/identity/auth/webauthn/src/adapter/dom-exception-mapper.md
2026-05-29 08:20:56 +01:00

2.6 KiB

DOMException → WebAuthnError mapper

Purpose

Converts every DOMException.name raised by navigator.credentials.create and navigator.credentials.get into a typed WebAuthnError. Raw DOMException instances never reach the caller — the mapper is invoked at every adapter boundary so consumers can rely on instanceof WebAuthnError and switch (err.code) instead of pattern-matching on browser-side string names. The original DOMException is preserved on err.cause for forensic inspection.

Features

  • Five fully-specified mappings per the W3C WebAuthn L3 §5.1.3 / §5.1.4 informative error tables:
    • NotAllowedError → WEBAUTHN.USER_CANCELLED
    • InvalidStateError → WEBAUTHN.INVALID_STATE
    • SecurityError → WEBAUTHN.SECURITY_ERROR
    • AbortError → WEBAUTHN.ABORTED
    • NotSupportedError → WEBAUTHN.NOT_SUPPORTED
  • One additional mapping for TimeoutError → WEBAUTHN.TIMEOUT, which some browsers raise instead of AbortError on the timeout field.
  • Unknown error names map to WEBAUTHN.MALFORMED_RESPONSE with cause set so callers can still drill into the original error.
  • Pass-through for an already-typed WebAuthnError: the mapper is idempotent so registration / authentication wrappers can wrap it twice without losing the original code.

Usage: Basic Example

import { mapDomException } from "@suaveplan/webauthn/adapter";

try {
    await navigator.credentials.create({ publicKey });
} catch (err) {
    throw mapDomException(err);
}

Usage: Advanced Example

import { mapDomException, WebAuthnError } from "@suaveplan/webauthn";

async function safeCreate(opts: CredentialCreationOptions) {
    try {
        return await navigator.credentials.create(opts);
    } catch (err) {
        const mapped = mapDomException(err);
        if (mapped.code === "WEBAUTHN.USER_CANCELLED") {
            // Tell the user politely, do not retry automatically.
        }
        throw mapped;
    }
}

API Reference

  • mapDomException(err: unknown): WebAuthnError — returns a WebAuthnError; preserves cause.

Implementation Notes

  • The mapper reads err.name defensively so any value that ducktypes as { name: string } is recognised. This is necessary because browsers occasionally throw Error subclasses that are not DOMException instances (older Safari).
  • WebAuthnError instances are returned unchanged — the mapper is idempotent so wrappers can compose without double-wrapping.
  • The fallback WEBAUTHN.MALFORMED_RESPONSE is intentionally distinct from NOT_SUPPORTED so the OTel errors_total{code} counter highlights authenticator misbehaviour separately from feature detection.