@excom/web-authn 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/.rush/temp/chunked-rush-logs/web-authn.apply-exports.chunks.jsonl +1 -0
  2. package/.rush/temp/chunked-rush-logs/web-authn.build_docs.chunks.jsonl +1 -0
  3. package/.rush/temp/chunked-rush-logs/web-authn.build_package-metas.chunks.jsonl +1 -0
  4. package/.rush/temp/operation/apply-exports/all.log +1 -0
  5. package/.rush/temp/operation/apply-exports/log-chunks.jsonl +1 -0
  6. package/.rush/temp/operation/apply-exports/state.json +3 -0
  7. package/.rush/temp/operation/build_docs/all.log +1 -0
  8. package/.rush/temp/operation/build_docs/log-chunks.jsonl +1 -0
  9. package/.rush/temp/operation/build_docs/state.json +3 -0
  10. package/.rush/temp/operation/build_package-metas/all.log +1 -0
  11. package/.rush/temp/operation/build_package-metas/log-chunks.jsonl +1 -0
  12. package/.rush/temp/operation/build_package-metas/state.json +3 -0
  13. package/.rush/temp/shrinkwrap-deps.json +4 -0
  14. package/config/rig.json +5 -0
  15. package/index.ts +17 -0
  16. package/package.json +44 -0
  17. package/rush-logs/web-authn.apply-exports.cache.log +1 -0
  18. package/rush-logs/web-authn.apply-exports.log +1 -0
  19. package/rush-logs/web-authn.build_docs.cache.log +1 -0
  20. package/rush-logs/web-authn.build_docs.log +1 -0
  21. package/rush-logs/web-authn.build_package-metas.cache.log +1 -0
  22. package/rush-logs/web-authn.build_package-metas.log +1 -0
  23. package/support/custom-elements.json +156 -0
  24. package/support/demos/authenticate.html +24 -0
  25. package/support/demos/register.html +24 -0
  26. package/support/dist-docs/web-authn.md +205 -0
  27. package/support/docs/README.md +81 -0
  28. package/support/package-meta.json +240 -0
  29. package/support/tests/authenticate.view.test.ts +28 -0
  30. package/support/tests/register.view.test.ts +29 -0
  31. package/support/tests/web-authn.test.ts +566 -0
  32. package/tsconfig.json +5 -0
  33. package/web-authn.ts +219 -0
@@ -0,0 +1 @@
1
+ {"kind":"O","text":"Invoking: cd \"$RUSH_PROJECT_FOLDER\" && node ../heft-rig/scripts/apply-exports.mjs \n"}
@@ -0,0 +1 @@
1
+ {"kind":"O","text":"Invoking: node node_modules/@excom/heft-rig/scripts/build-docs.mjs \n"}
@@ -0,0 +1 @@
1
+ {"kind":"O","text":"Invoking: node node_modules/@excom/heft-rig/scripts/build-package-metas.mjs \n"}
@@ -0,0 +1 @@
1
+ Invoking: cd "$RUSH_PROJECT_FOLDER" && node ../heft-rig/scripts/apply-exports.mjs
@@ -0,0 +1 @@
1
+ {"kind":"O","text":"Invoking: cd \"$RUSH_PROJECT_FOLDER\" && node ../heft-rig/scripts/apply-exports.mjs \n"}
@@ -0,0 +1,3 @@
1
+ {
2
+ "nonCachedDurationMs": 36.38584700000001
3
+ }
@@ -0,0 +1 @@
1
+ Invoking: node node_modules/@excom/heft-rig/scripts/build-docs.mjs
@@ -0,0 +1 @@
1
+ {"kind":"O","text":"Invoking: node node_modules/@excom/heft-rig/scripts/build-docs.mjs \n"}
@@ -0,0 +1,3 @@
1
+ {
2
+ "nonCachedDurationMs": 53.987307999999985
3
+ }
@@ -0,0 +1 @@
1
+ Invoking: node node_modules/@excom/heft-rig/scripts/build-package-metas.mjs
@@ -0,0 +1 @@
1
+ {"kind":"O","text":"Invoking: node node_modules/@excom/heft-rig/scripts/build-package-metas.mjs \n"}
@@ -0,0 +1,3 @@
1
+ {
2
+ "nonCachedDurationMs": 108.155575
3
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "../../packages/web-authn": "../../packages/web-authn:17NeRWNV7ASUpQlpof8OGTmS5fiIo/Je4xCdP8FuMh0=:",
3
+ "@simplewebauthn/browser@13.2.2": "@simplewebauthn/browser@13.2.2:410ZPeyt1GhQ1ofae14PzU+ZB+gpNY5i7/CdhT/Fviw=:"
4
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
3
+ "rigPackageName": "@excom/heft-rig",
4
+ "rigProfile": "default"
5
+ }
package/index.ts ADDED
@@ -0,0 +1,17 @@
1
+ import { WebAuthn } from "./web-authn";
2
+
3
+ WebAuthn.define();
4
+
5
+ export { WebAuthn };
6
+
7
+ type T_HTMLWebAuthnElement = typeof WebAuthn.CustomElement;
8
+ declare global {
9
+ interface HTMLWebAuthnElement extends T_HTMLWebAuthnElement {}
10
+ interface Window {
11
+ HTMLWebAuthnElement: HTMLWebAuthnElement;
12
+ }
13
+ interface HTMLElementTagNameMap {
14
+ "web-authn": HTMLWebAuthnElement;
15
+ }
16
+ }
17
+ export type { HTMLWebAuthnElement };
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "@excom/web-authn",
3
+ "version": "0.1.0",
4
+ "description": "<web-authn> custom element",
5
+ "license": "MIT",
6
+ "engines": {
7
+ "node": ">=24.13.0"
8
+ },
9
+ "type": "module",
10
+ "dependencies": {
11
+ "@simplewebauthn/browser": "^13.1.2",
12
+ "@excom/neutron": "^0.1.0",
13
+ "@excom/fetchable-element": "^0.1.0"
14
+ },
15
+ "peerDependencies": {},
16
+ "devDependencies": {
17
+ "@excom/heft-rig": "^0.1.0"
18
+ },
19
+ "repository": {
20
+ "url": "excom-dev/nucleus",
21
+ "directory": "packages/web-authn"
22
+ },
23
+ "homepage": "https://github.com/excom-dev/nucleus/tree/main/packages/web-authn/support/docs/README.md",
24
+ "bugs": "https://github.com/excom-dev/nucleus/issues",
25
+ "keywords": [
26
+ "web-authn",
27
+ "neutron",
28
+ "custom-elements"
29
+ ],
30
+ "excom": {
31
+ "packageType": "kit-element"
32
+ },
33
+ "scripts": {
34
+ "build": "node node_modules/@excom/heft-rig/scripts/vite-build.mjs",
35
+ "build:watch": "node node_modules/@excom/heft-rig/scripts/vite-build-watch.mjs",
36
+ "format": "node node_modules/@excom/heft-rig/scripts/format.mjs",
37
+ "test": "node node_modules/@excom/heft-rig/scripts/vitest.mjs",
38
+ "coverage": "node node_modules/@excom/heft-rig/scripts/coverage.mjs",
39
+ "dev": "node node_modules/@excom/heft-rig/scripts/vite-dev.mjs",
40
+ "preview": "node node_modules/@excom/heft-rig/scripts/vite-preview.mjs",
41
+ "build:package-metas": "node node_modules/@excom/heft-rig/scripts/build-package-metas.mjs",
42
+ "build:docs": "node node_modules/@excom/heft-rig/scripts/build-docs.mjs"
43
+ }
44
+ }
@@ -0,0 +1 @@
1
+ Caching has been disabled for this project's "apply-exports" command.
@@ -0,0 +1 @@
1
+ Invoking: cd "$RUSH_PROJECT_FOLDER" && node ../heft-rig/scripts/apply-exports.mjs
@@ -0,0 +1 @@
1
+ This project does not define the caching behavior of the "build:docs" command, so caching has been disabled.
@@ -0,0 +1 @@
1
+ Invoking: node node_modules/@excom/heft-rig/scripts/build-docs.mjs
@@ -0,0 +1 @@
1
+ This project does not define the caching behavior of the "build:package-metas" command, so caching has been disabled.
@@ -0,0 +1 @@
1
+ Invoking: node node_modules/@excom/heft-rig/scripts/build-package-metas.mjs
@@ -0,0 +1,156 @@
1
+ {
2
+ "schemaVersion": "1.0.0",
3
+ "modules": [
4
+ {
5
+ "kind": "javascript-module",
6
+ "path": "web-authn.ts",
7
+ "declarations": [
8
+ {
9
+ "kind": "class",
10
+ "name": "WebAuthn",
11
+ "customElement": true,
12
+ "tagName": "web-authn",
13
+ "mixins": [
14
+ {
15
+ "name": "FetchableElement",
16
+ "package": "@excom/fetchable-element"
17
+ }
18
+ ],
19
+ "summary": "Passkey register / authenticate wired to a `<form>`.",
20
+ "description": "Native form `submit` this element intercepts, not a Neutron emit.",
21
+ "attributes": [
22
+ {
23
+ "name": "form-ref",
24
+ "type": {
25
+ "text": "string"
26
+ },
27
+ "description": "CSS selector for the `<form>` to intercept. Must be a descendant to be heard directly — point elsewhere and invoke the `--submit` command instead.",
28
+ "fieldName": "formRef",
29
+ "default": ":scope form",
30
+ "values": [
31
+ "<CSS Selector>"
32
+ ]
33
+ },
34
+ {
35
+ "name": "start-method",
36
+ "type": {
37
+ "text": "string"
38
+ },
39
+ "description": "Which WebAuthn ceremony to run.",
40
+ "fieldName": "startMethod",
41
+ "values": [
42
+ "register",
43
+ "authenticate"
44
+ ]
45
+ },
46
+ {
47
+ "name": "verify-url",
48
+ "type": {
49
+ "text": "string"
50
+ },
51
+ "description": "Endpoint that verifies the credential produced by the browser prompt (your server's `verifyRegistrationResponse` / `verifyAuthenticationResponse`). Receives the credential as the request body.",
52
+ "fieldName": "verifyUrl",
53
+ "values": [
54
+ "<URL>"
55
+ ]
56
+ }
57
+ ],
58
+ "members": [
59
+ {
60
+ "kind": "field",
61
+ "name": "formRef",
62
+ "type": {
63
+ "text": "string"
64
+ },
65
+ "privacy": "public",
66
+ "readonly": false,
67
+ "description": "CSS selector for the `<form>` to intercept. Must be a descendant to be heard directly — point elsewhere and invoke the `--submit` command instead.",
68
+ "default": ":scope form",
69
+ "_neutron": {
70
+ "surface": "option"
71
+ }
72
+ },
73
+ {
74
+ "kind": "field",
75
+ "name": "startMethod",
76
+ "type": {
77
+ "text": "string"
78
+ },
79
+ "privacy": "public",
80
+ "readonly": false,
81
+ "description": "Which WebAuthn ceremony to run.",
82
+ "_neutron": {
83
+ "surface": "option"
84
+ }
85
+ },
86
+ {
87
+ "kind": "field",
88
+ "name": "verifyUrl",
89
+ "type": {
90
+ "text": "string"
91
+ },
92
+ "privacy": "public",
93
+ "readonly": false,
94
+ "description": "Endpoint that verifies the credential produced by the browser prompt (your server's `verifyRegistrationResponse` / `verifyAuthenticationResponse`). Receives the credential as the request body.",
95
+ "_neutron": {
96
+ "surface": "option"
97
+ }
98
+ }
99
+ ],
100
+ "events": [
101
+ {
102
+ "name": "web-authn-submit",
103
+ "description": "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()`.",
104
+ "type": {
105
+ "text": "WebAuthnSubmitEvent",
106
+ "expanded": "CustomEvent & { type: \"web-authn-submit\"; detail: [url: string, requestInit: RequestInit]; bubbles: true; cancelable: true; composed: true }"
107
+ }
108
+ }
109
+ ],
110
+ "_neutron": {
111
+ "listens": [
112
+ {
113
+ "name": "submit",
114
+ "description": "The default action of the `<form>` matched by `form-ref`; prevented, then starts the ceremony.",
115
+ "type": {
116
+ "text": "WebAuthnNativeSubmitEvent",
117
+ "expanded": "SubmitEvent & { type: \"submit\"; bubbles: true; cancelable: true; composed: false; }"
118
+ }
119
+ }
120
+ ],
121
+ "commands": [
122
+ {
123
+ "name": "--submit",
124
+ "description": "Starts the ceremony programmatically (`<button command=\"--submit\" commandfor=\"…\">`) — the only option when `form-ref` points to a form that isn't a descendant."
125
+ }
126
+ ],
127
+ "defaultActions": [
128
+ {
129
+ "name": "web-authn-submit",
130
+ "description": "Calls `doFetch(url, requestInit)` with the event's detail (the verify-url request)."
131
+ }
132
+ ]
133
+ }
134
+ }
135
+ ],
136
+ "exports": [
137
+ {
138
+ "kind": "js",
139
+ "name": "WebAuthn",
140
+ "declaration": {
141
+ "name": "WebAuthn",
142
+ "module": "web-authn.ts"
143
+ }
144
+ },
145
+ {
146
+ "kind": "custom-element-definition",
147
+ "name": "web-authn",
148
+ "declaration": {
149
+ "name": "WebAuthn",
150
+ "module": "web-authn.ts"
151
+ }
152
+ }
153
+ ]
154
+ }
155
+ ]
156
+ }
@@ -0,0 +1,24 @@
1
+ <section>
2
+ <web-authn options-url="/api/webauthn/authenticate/options" verify-url="/api/webauthn/authenticate/verify"
3
+ start-method="authenticate">
4
+ <form>
5
+ <label>
6
+ Username
7
+ <input name="username" required autocomplete="username webauthn">
8
+ </label>
9
+ <button type="submit">Sign in with passkey</button>
10
+ </form>
11
+ <output></output>
12
+ </web-authn>
13
+ <quark-sheet>
14
+ web-authn[is-success] {
15
+ $res: prop("provision").body;
16
+ output {
17
+ content: "verified: #{$res.verified}";
18
+ }
19
+ }
20
+ web-authn[is-error] output {
21
+ content: "Ceremony failed (this mock needs an authenticator).";
22
+ }
23
+ </quark-sheet>
24
+ </section>
@@ -0,0 +1,24 @@
1
+ <section>
2
+ <web-authn options-url="/api/webauthn/register/options" verify-url="/api/webauthn/register/verify"
3
+ start-method="register">
4
+ <form>
5
+ <label>
6
+ Username
7
+ <input name="username" required autocomplete="username">
8
+ </label>
9
+ <button type="submit">Register passkey</button>
10
+ </form>
11
+ <output></output>
12
+ </web-authn>
13
+ <quark-sheet>
14
+ web-authn[is-success] {
15
+ $res: prop("provision").body;
16
+ output {
17
+ content: "verified: #{$res.verified}";
18
+ }
19
+ }
20
+ web-authn[is-error] output {
21
+ content: "Ceremony failed (this mock needs an authenticator).";
22
+ }
23
+ </quark-sheet>
24
+ </section>
@@ -0,0 +1,205 @@
1
+ # web-authn
2
+
3
+ Passkey register / authenticate with HTML. Pair it with Quark to render the result.
4
+
5
+
6
+ ```html
7
+ <section>
8
+ <web-authn options-url="/api/webauthn/register/options" verify-url="/api/webauthn/register/verify"
9
+ start-method="register">
10
+ <form>
11
+ <label>
12
+ Username
13
+ <input name="username" required autocomplete="username">
14
+ </label>
15
+ <button type="submit">Register passkey</button>
16
+ </form>
17
+ <output></output>
18
+ </web-authn>
19
+ <quark-sheet>
20
+ web-authn[is-success] {
21
+ $res: prop("provision").body;
22
+ output {
23
+ content: "verified: #{$res.verified}";
24
+ }
25
+ }
26
+ web-authn[is-error] output {
27
+ content: "Ceremony failed (this mock needs an authenticator).";
28
+ }
29
+ </quark-sheet>
30
+ </section>
31
+ ```
32
+
33
+
34
+ ```html
35
+ <web-authn start-method="register" options-url="/api/registration-options" verify-url="/api/users">
36
+ <form>
37
+ <button type="submit">One click sign up!</button>
38
+ </form>
39
+ </web-authn>
40
+ ```
41
+
42
+ ## Features
43
+
44
+ - **Provides data** Use Quark to render the verify response
45
+ - **Full ceremony** Fetches options, runs the browser's WebAuthn prompt, then verifies — one element
46
+ - **Register or authenticate** `start-method` picks the ceremony
47
+ - **Submit command** `--submit` starts the ceremony programmatically — for forms outside the DOM subtree, or buttons outside the `<form>`
48
+ - **Chainable** `web-authn-success` fires like any `{tag}-success` event — chain a redirect or next step
49
+ - **Highly configurable** Headers, credentials, redirect, etc
50
+
51
+ ## Installation
52
+
53
+
54
+ `@excom/web-authn` v0.1.0
55
+
56
+ ```bash
57
+ pnpm add @excom/web-authn
58
+ ```
59
+
60
+ ```bash
61
+ npm install @excom/web-authn
62
+ ```
63
+
64
+ ```bash
65
+ yarn add @excom/web-authn
66
+ ```
67
+
68
+ ### Import
69
+
70
+ ```ts
71
+ import "@excom/web-authn";
72
+ ```
73
+
74
+
75
+
76
+ Depends on [`@simplewebauthn/browser`](https://simplewebauthn.dev/) 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`](https://simplewebauthn.dev/), since they seamlessly understand the same contract.
77
+
78
+ ## Usage
79
+
80
+ ```html
81
+ <web-authn options-url="/api/webauthn/register/options"
82
+ verify-url="/api/webauthn/register/verify" start-method="register">
83
+ <form>
84
+ <input name="username" required>
85
+ <button type="submit">Register passkey</button>
86
+ </form>
87
+ </web-authn>
88
+ ```
89
+
90
+ 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.
91
+
92
+ Hook the lifecycle state with CSS:
93
+
94
+ ```css
95
+ web-authn[is-loading] { /* show loading spinner */ }
96
+ web-authn[is-error]::before { content: "An error occurred." }
97
+ ```
98
+
99
+ Or Quark:
100
+
101
+ ```quark
102
+ web-authn[is-success] {
103
+ $res: prop("provision").body;
104
+ span { content: $res.verified; }
105
+ }
106
+ ```
107
+
108
+ Chain a next step off success the same way you would for any `<super-form>` or `<provider-fetch>`:
109
+
110
+ ```html
111
+ <event-handler listen-for="web-authn-success" fire-event="onboarding-step-complete">
112
+ <web-authn options-url="/api/webauthn/register/options"
113
+ verify-url="/api/webauthn/register/verify" start-method="register">
114
+ <form><input name="username"><button type="submit">Register</button></form>
115
+ </web-authn>
116
+ </event-handler>
117
+ ```
118
+
119
+ ### Examples
120
+
121
+ #### Authenticate
122
+
123
+
124
+ ```html
125
+ <section>
126
+ <web-authn options-url="/api/webauthn/authenticate/options" verify-url="/api/webauthn/authenticate/verify"
127
+ start-method="authenticate">
128
+ <form>
129
+ <label>
130
+ Username
131
+ <input name="username" required autocomplete="username webauthn">
132
+ </label>
133
+ <button type="submit">Sign in with passkey</button>
134
+ </form>
135
+ <output></output>
136
+ </web-authn>
137
+ <quark-sheet>
138
+ web-authn[is-success] {
139
+ $res: prop("provision").body;
140
+ output {
141
+ content: "verified: #{$res.verified}";
142
+ }
143
+ }
144
+ web-authn[is-error] output {
145
+ content: "Ceremony failed (this mock needs an authenticator).";
146
+ }
147
+ </quark-sheet>
148
+ </section>
149
+ ```
150
+
151
+
152
+ ### API Reference
153
+
154
+
155
+ #### Attributes
156
+
157
+ | Name | Surface | Type | Default | Values | Description | Inherited from |
158
+ | --- | --- | --- | --- | --- | --- | --- |
159
+ | `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. | |
160
+ | `start-method` | option | `string` | | `"register"` \| `"authenticate"` | Which WebAuthn ceremony to run. | |
161
+ | `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. | |
162
+ | `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` |
163
+ | `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` |
164
+ | `api-method` | option | `string` | `"GET"` | | HTTP method. Always uppercased before the request is sent. | `@excom/fetchable-element` |
165
+ | `header-accept` | option | `string` | `"application/json"` | | `Accept` request header. | `@excom/fetchable-element` |
166
+ | `header-content-type` | option | `string` | `"application/json"` | | `Content-Type` request header. Dropped entirely when the request has no body. | `@excom/fetchable-element` |
167
+ | `header-cache-control` | option | `string` | | | `Cache-Control` request header. Unset by default (browser default caching applies). | `@excom/fetchable-element` |
168
+ | `fetch-redirect` | option | `string` | | `"follow"` \| `"error"` \| `"manual"` | `RequestInit.redirect` mode. Unset defers to the browser default (`follow`). | `@excom/fetchable-element` |
169
+ | `fetch-credentials` | option | `string` | `"include"` | `"omit"` \| `"same-origin"` \| `"include"` | `RequestInit.credentials` mode. | `@excom/fetchable-element` |
170
+ | `is-loading` | state | `boolean` | | | A request is currently in flight. | `@excom/fetchable-element` |
171
+ | `is-success` | state | `boolean` | | | The most recent request resolved successfully. Mutually exclusive with `is-error`. | `@excom/fetchable-element` |
172
+ | `is-error` | state | `boolean` | | | The most recent request failed (non-2xx status, network error, or a thrown error other than `AbortError`). Fires with the `error` event. | `@excom/fetchable-element` |
173
+
174
+ #### Provision
175
+
176
+ | Name | Type | Description | Inherited from |
177
+ | --- | --- | --- | --- |
178
+ | `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` |
179
+
180
+ #### Fires
181
+
182
+ | Name | Type | Description | Inherited from |
183
+ | --- | --- | --- | --- |
184
+ | `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()`. | |
185
+ | `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` |
186
+ | `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` |
187
+ | `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 — non-2xx status, network error, or a thrown error. `event.detail` is the error payload (see `provision`). Not dispatched for aborted requests. | `@excom/fetchable-element` |
188
+
189
+ #### Listens for
190
+
191
+ | Name | Type | Description |
192
+ | --- | --- | --- |
193
+ | `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. |
194
+
195
+ #### Commands
196
+
197
+ | Command | Action |
198
+ | --- | --- |
199
+ | `--submit` | Starts the ceremony programmatically (`<button command="--submit" commandfor="…">`) — the only option when `form-ref` points to a form that isn't a descendant. |
200
+
201
+ #### Default actions
202
+
203
+ | Event | Default behavior (unless preventDefault() is called) |
204
+ | --- | --- |
205
+ | `web-authn-submit` | Calls `doFetch(url, requestInit)` with the event's detail (the verify-url request). |
@@ -0,0 +1,81 @@
1
+ # web-authn
2
+
3
+ Passkey register / authenticate with HTML. Pair it with Quark to render the result.
4
+
5
+ <include-content data-demo="register"></include-content>
6
+
7
+ ```html
8
+ <web-authn start-method="register" options-url="/api/registration-options" verify-url="/api/users">
9
+ <form>
10
+ <button type="submit">One click sign up!</button>
11
+ </form>
12
+ </web-authn>
13
+ ```
14
+
15
+ ## Features
16
+
17
+ - **Provides data** Use Quark to render the verify response
18
+ - **Full ceremony** Fetches options, runs the browser's WebAuthn prompt, then verifies — one element
19
+ - **Register or authenticate** `start-method` picks the ceremony
20
+ - **Submit command** `--submit` starts the ceremony programmatically — for forms outside the DOM subtree, or buttons outside the `<form>`
21
+ - **Chainable** `web-authn-success` fires like any `{tag}-success` event — chain a redirect or next step
22
+ - **Highly configurable** Headers, credentials, redirect, etc
23
+
24
+ ## Installation
25
+
26
+ <include-content is-active template-ref="/views/install-section/install-section.html"></include-content>
27
+
28
+ Depends on [`@simplewebauthn/browser`](https://simplewebauthn.dev/) 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`](https://simplewebauthn.dev/), since they seamlessly understand the same contract.
29
+
30
+ ## Usage
31
+
32
+ ```html
33
+ <web-authn options-url="/api/webauthn/register/options"
34
+ verify-url="/api/webauthn/register/verify" start-method="register">
35
+ <form>
36
+ <input name="username" required>
37
+ <button type="submit">Register passkey</button>
38
+ </form>
39
+ </web-authn>
40
+ ```
41
+
42
+ 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.
43
+
44
+ Hook the lifecycle state with CSS:
45
+
46
+ ```css
47
+ web-authn[is-loading] { /* show loading spinner */ }
48
+ web-authn[is-error]::before { content: "An error occurred." }
49
+ ```
50
+
51
+ Or Quark:
52
+
53
+ ```quark
54
+ web-authn[is-success] {
55
+ $res: prop("provision").body;
56
+ span { content: $res.verified; }
57
+ }
58
+ ```
59
+
60
+ Chain a next step off success the same way you would for any `<super-form>` or `<provider-fetch>`:
61
+
62
+ ```html
63
+ <event-handler listen-for="web-authn-success" fire-event="onboarding-step-complete">
64
+ <web-authn options-url="/api/webauthn/register/options"
65
+ verify-url="/api/webauthn/register/verify" start-method="register">
66
+ <form><input name="username"><button type="submit">Register</button></form>
67
+ </web-authn>
68
+ </event-handler>
69
+ ```
70
+
71
+ ### Examples
72
+
73
+ #### Authenticate
74
+
75
+ <include-content data-demo="authenticate"></include-content>
76
+
77
+ ### API Reference
78
+
79
+ <include-content is-active template-ref="/views/api-reference/api-reference.html"></include-content>
80
+
81
+