@excom/web-authn
v0.1.5
Published
<web-authn> custom element
Downloads
1,145
Maintainers
Readme
web-authn
Passkey register / authenticate with HTML. Pair it with Quark to render the result.
<section>
<web-authn options-url="/api/webauthn/register/options" verify-url="/api/webauthn/register/verify"
start-method="register">
<form>
<label>
Username
<input name="username" required autocomplete="username">
</label>
<button type="submit">Register passkey</button>
</form>
<output></output>
</web-authn>
<quark-sheet>
web-authn[is-success] {
$res: prop("provision").body;
output {
content: "verified: #{$res.verified}";
}
}
web-authn[is-error] output {
content: "Ceremony failed (this mock needs an authenticator).";
}
</quark-sheet>
</section><web-authn start-method="register" options-url="/api/registration-options" verify-url="/api/users">
<form>
<button type="submit">One click sign up!</button>
</form>
</web-authn>Features
- Provides data Use Quark to render the verify response
- Full ceremony Fetches options, runs the browser's WebAuthn prompt, then verifies — one element
- Register or authenticate
start-methodpicks the ceremony - Submit command
--submitstarts the ceremony programmatically — for forms outside the DOM subtree, or buttons outside the<form> - Chainable
web-authn-successfires like any{tag}-successevent — chain a redirect or next step - Highly configurable Headers, credentials, redirect, etc
Installation
@excom/web-authn v0.1.5
pnpm add @excom/web-authnnpm install @excom/web-authnyarn add @excom/web-authnImport
import "@excom/web-authn";Depends on @simplewebauthn/browser for the actual WebAuthn calls (startRegistration / startAuthentication). You can use any server-side library to handle the WebAuthn requests, but it is recommended to use the counterpart library, @simplewebauthn/server, since they seamlessly understand the same contract.
Usage
<web-authn options-url="/api/webauthn/register/options"
verify-url="/api/webauthn/register/verify" start-method="register">
<form>
<input name="username" required>
<button type="submit">Register passkey</button>
</form>
</web-authn>On submit: options-url is fetched for ceremony options, the browser's native passkey prompt runs (@simplewebauthn/browser), and the resulting credential is posted to verify-url. Use start-method="authenticate" for sign-in instead of registration.
Hook the lifecycle state with CSS:
web-authn[is-loading] { /* show loading spinner */ }
web-authn[is-error]::before { content: "An error occurred." }Or Quark:
web-authn[is-success] {
$res: prop("provision").body;
span { content: $res.verified; }
}Chain a next step off success the same way you would for any <super-form> or <provider-fetch>:
<event-handler listen-for="web-authn-success" fire-event="onboarding-step-complete">
<web-authn options-url="/api/webauthn/register/options"
verify-url="/api/webauthn/register/verify" start-method="register">
<form><input name="username"><button type="submit">Register</button></form>
</web-authn>
</event-handler>Examples
Authenticate
<section>
<web-authn options-url="/api/webauthn/authenticate/options" verify-url="/api/webauthn/authenticate/verify"
start-method="authenticate">
<form>
<label>
Username
<input name="username" required autocomplete="username webauthn">
</label>
<button type="submit">Sign in with passkey</button>
</form>
<output></output>
</web-authn>
<quark-sheet>
web-authn[is-success] {
$res: prop("provision").body;
output {
content: "verified: #{$res.verified}";
}
}
web-authn[is-error] output {
content: "Ceremony failed (this mock needs an authenticator).";
}
</quark-sheet>
</section>API Reference
Attributes
| Name | Surface | Type | Default | Values | Description | Inherited from |
| --- | --- | --- | --- | --- | --- | --- |
| form-ref | option | string | ":scope form" | <CSS Selector> | CSS selector for the <form> to intercept. Must be a descendant to be heard directly — point elsewhere and invoke the --submit command instead. | |
| start-method | option | string | | "register" | "authenticate" | Which WebAuthn ceremony to run. | |
| verify-url | option | string | | <URL> | Endpoint that verifies the credential produced by the browser prompt (your server's verifyRegistrationResponse / verifyAuthenticationResponse). Receives the credential as the request body. | |
| has-body | option | boolean | | | Force a request body even for methods that don't imply one (GET / HEAD). Already implied for POST / PUT / PATCH. | @excom/fetchable-element |
| api-url | option | string | "" | | Endpoint URL. When the request has no body, the JSON payload (from form-ref or custom doFetch() args) is merged in as query params instead. | @excom/fetchable-element |
| api-method | option | string | "GET" | | HTTP method. Always uppercased before the request is sent. | @excom/fetchable-element |
| header-accept | option | string | "application/json" | | Accept request header. | @excom/fetchable-element |
| header-content-type | option | string | "application/json" | | Content-Type request header. Dropped entirely when the request has no body. | @excom/fetchable-element |
| header-cache-control | option | string | | | Cache-Control request header. Unset by default (browser default caching applies). | @excom/fetchable-element |
| fetch-redirect | option | string | | "follow" | "error" | "manual" | RequestInit.redirect mode. Unset defers to the browser default (follow). | @excom/fetchable-element |
| fetch-credentials | option | string | "include" | "omit" | "same-origin" | "include" | RequestInit.credentials mode. | @excom/fetchable-element |
| is-loading | state | boolean | | | A request is currently in flight. | @excom/fetchable-element |
| is-success | state | boolean | | | The most recent request resolved successfully. Mutually exclusive with is-loading and is-error. | @excom/fetchable-element |
| is-error | state | boolean | | | The most recent request failed (status 400 or above, network error, or a thrown error other than AbortError). Fires with the error event. | @excom/fetchable-element |
Provision
| Name | Type | Description | Inherited from |
| --- | --- | --- | --- |
| provision | FetchResponse ({ bodyUsed: boolean; headers: [string, string][]; ok: boolean; redirected: boolean; status: number; statusText: string; type: ResponseType; url: string; body: unknown; }) | Response payload on success, or error payload on failure. Success shape: { status, statusText, ok, headers, url, redirected, bodyUsed, type, body }. Failure shape is either that same response shape (server responded with an error status) or { message, stack } (request never completed). Not reflected as an attribute. | @excom/fetchable-element |
Fires
| Name | Type | Description | Inherited from |
| --- | --- | --- | --- |
| web-authn-submit | WebAuthnSubmitEvent (CustomEvent & { type: "web-authn-submit"; detail: [url: string, requestInit: RequestInit]; bubbles: true; cancelable: true; composed: true }) | Internal — dispatched once the browser ceremony (register or authenticate) resolves, just before the verify-url fetch runs. The credential is the request body. Default action calls doFetch(). | |
| web-authn-loading | FetchableLoadingEvent (CustomEvent & { type: "{tag}-loading"; detail: void; bubbles: true; cancelable: true; composed: true }) | Dispatched immediately before the request is sent. | @excom/fetchable-element |
| web-authn-success | FetchableSuccessEvent (CustomEvent & { type: "{tag}-success"; detail: { bodyUsed: boolean; headers: [string, string][]; ok: boolean; redirected: boolean; status: number; statusText: string; type: ResponseType; url: string; body: unknown; }; bubbles: true; cancelable: true; composed: true }) | Dispatched when the request resolves successfully. event.detail is the parsed response (see provision). | @excom/fetchable-element |
| web-authn-error | FetchableErrorEvent (CustomEvent & { type: "{tag}-error"; detail: { bodyUsed: boolean; headers: [string, string][]; ok: boolean; redirected: boolean; status: number; statusText: string; type: ResponseType; url: string; body: unknown; } \| { message: string; stack?: string }; bubbles: true; cancelable: true; composed: true }) | Dispatched when the request fails — status 400 or above, network error, or a thrown error. event.detail is the error payload (see provision). Not dispatched for aborted requests. | @excom/fetchable-element |
Listens for
| Name | Type | Description |
| --- | --- | --- |
| submit | WebAuthnNativeSubmitEvent (SubmitEvent & { type: "submit"; bubbles: true; cancelable: true; composed: false; }) | The default action of the <form> matched by form-ref; prevented, then starts the ceremony. |
Commands
| Command | Action |
| --- | --- |
| --submit | Starts the ceremony programmatically (<button command="--submit" commandfor="…">) — the only option when form-ref points to a form that isn't a descendant. |
Default actions
| Event | Default behavior (unless preventDefault() is called) |
| --- | --- |
| web-authn-submit | Calls doFetch(url, requestInit) with the event's detail (the verify-url request). |
Release notes
0.1.4 (2026-10-01)
- A failed ceremony or options request now clears is-loading and sets is-error with the message in provision
0.1.1 (2026-09-23)
- Ship only dist (and declared extras) in the npm tarball; drop build logs, tests and sources
Full documentation: https://nucleus.excom.dev/packages/web-authn
