@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.
- package/.rush/temp/chunked-rush-logs/web-authn.apply-exports.chunks.jsonl +1 -0
- package/.rush/temp/chunked-rush-logs/web-authn.build_docs.chunks.jsonl +1 -0
- package/.rush/temp/chunked-rush-logs/web-authn.build_package-metas.chunks.jsonl +1 -0
- package/.rush/temp/operation/apply-exports/all.log +1 -0
- package/.rush/temp/operation/apply-exports/log-chunks.jsonl +1 -0
- package/.rush/temp/operation/apply-exports/state.json +3 -0
- package/.rush/temp/operation/build_docs/all.log +1 -0
- package/.rush/temp/operation/build_docs/log-chunks.jsonl +1 -0
- package/.rush/temp/operation/build_docs/state.json +3 -0
- package/.rush/temp/operation/build_package-metas/all.log +1 -0
- package/.rush/temp/operation/build_package-metas/log-chunks.jsonl +1 -0
- package/.rush/temp/operation/build_package-metas/state.json +3 -0
- package/.rush/temp/shrinkwrap-deps.json +4 -0
- package/config/rig.json +5 -0
- package/index.ts +17 -0
- package/package.json +44 -0
- package/rush-logs/web-authn.apply-exports.cache.log +1 -0
- package/rush-logs/web-authn.apply-exports.log +1 -0
- package/rush-logs/web-authn.build_docs.cache.log +1 -0
- package/rush-logs/web-authn.build_docs.log +1 -0
- package/rush-logs/web-authn.build_package-metas.cache.log +1 -0
- package/rush-logs/web-authn.build_package-metas.log +1 -0
- package/support/custom-elements.json +156 -0
- package/support/demos/authenticate.html +24 -0
- package/support/demos/register.html +24 -0
- package/support/dist-docs/web-authn.md +205 -0
- package/support/docs/README.md +81 -0
- package/support/package-meta.json +240 -0
- package/support/tests/authenticate.view.test.ts +28 -0
- package/support/tests/register.view.test.ts +29 -0
- package/support/tests/web-authn.test.ts +566 -0
- package/tsconfig.json +5 -0
- 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 @@
|
|
|
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 @@
|
|
|
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"}
|
package/config/rig.json
ADDED
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
|
+
|