first commit
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
# 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
|
||||
|
||||
```ts
|
||||
import { mapDomException } from "@suaveplan/webauthn/adapter";
|
||||
|
||||
try {
|
||||
await navigator.credentials.create({ publicKey });
|
||||
} catch (err) {
|
||||
throw mapDomException(err);
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
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.
|
||||
Reference in New Issue
Block a user