tangled
alpha
login
or
join now
mary.my.id
/
danaus
4
fork
atom
work-in-progress atproto PDS
typescript
atproto
pds
atcute
4
fork
atom
overview
issues
pulls
pipelines
fix: make webauthn forms work
mary.my.id
1 month ago
7fa59b39
c1991119
verified
This commit was signed with the committer's
known signature
.
mary.my.id
SSH Key Fingerprint:
SHA256:ZlTP/auFSGpGnaoDg4mCTG1g9OZvXp62jWR4c6H4O3c=
+53
-48
3 changed files
expand all
collapse all
unified
split
packages
danaus
src
web
controllers
account
security
webauthn.tsx
verify.tsx
scripts
webauthn-authenticate.js
+25
-25
packages/danaus/src/web/controllers/account/security/webauthn.tsx
···
65
66
<div class="flex flex-1 items-center justify-center p-4">
67
<div class="w-full max-w-120 rounded-xl bg-neutral-background-1 shadow-64">
0
68
<form {...completeWebAuthnForm} class="contents">
69
<Dialog.Body>
70
<Dialog.Title>Set up security key</Dialog.Title>
···
76
77
<input {...fields.token.as('hidden', token!)} />
78
79
-
<danaus-webauthn-register data-options={JSON.stringify(options)}>
80
-
<p
81
-
data-target="webauthn-register.status"
82
-
class="text-base-300 text-neutral-foreground-3"
83
-
>
84
-
Initializing...
85
-
</p>
86
87
-
<input
88
-
{...fields.response.as('hidden', '')}
89
-
data-target="webauthn-register.response"
90
-
/>
91
92
-
<Field
93
-
label="Name"
94
-
hint="Give this security key a name to help you identify it"
95
-
validationMessageText={fields.name.issues()?.at(0)?.message}
96
-
>
97
-
<Input
98
-
{...fields.name.as('text')}
99
-
placeholder={accountManager.generateWebAuthnName(
100
-
session.did,
101
-
WebAuthnCredentialType.SecurityKey,
102
-
)}
103
-
/>
104
-
</Field>
105
-
</danaus-webauthn-register>
106
107
{generalError && (
108
<p role="alert" class="text-base-300 text-status-danger-foreground-1">
···
122
</Dialog.Actions>
123
</Dialog.Body>
124
</form>
0
125
</div>
126
</div>
127
</BaseLayout>,
···
65
66
<div class="flex flex-1 items-center justify-center p-4">
67
<div class="w-full max-w-120 rounded-xl bg-neutral-background-1 shadow-64">
68
+
<danaus-webauthn-register class="contents" data-options={JSON.stringify(options)}>
69
<form {...completeWebAuthnForm} class="contents">
70
<Dialog.Body>
71
<Dialog.Title>Set up security key</Dialog.Title>
···
77
78
<input {...fields.token.as('hidden', token!)} />
79
80
+
<p
81
+
data-target="webauthn-register.status"
82
+
class="text-base-300 text-neutral-foreground-3"
83
+
>
84
+
Initializing...
85
+
</p>
0
86
87
+
<input
88
+
{...fields.response.as('hidden', '')}
89
+
data-target="webauthn-register.response"
90
+
/>
91
92
+
<Field
93
+
label="Name"
94
+
hint="Give this security key a name to help you identify it"
95
+
validationMessageText={fields.name.issues()?.at(0)?.message}
96
+
>
97
+
<Input
98
+
{...fields.name.as('text')}
99
+
placeholder={accountManager.generateWebAuthnName(
100
+
session.did,
101
+
WebAuthnCredentialType.SecurityKey,
102
+
)}
103
+
/>
104
+
</Field>
0
105
106
{generalError && (
107
<p role="alert" class="text-base-300 text-status-danger-foreground-1">
···
121
</Dialog.Actions>
122
</Dialog.Body>
123
</form>
124
+
</danaus-webauthn-register>
125
</div>
126
</div>
127
</BaseLayout>,
+22
-22
packages/danaus/src/web/controllers/verify.tsx
···
200
201
<div class="flex flex-1 items-center justify-center p-4">
202
<div class="w-full max-w-96 rounded-xl bg-neutral-background-1 p-6 shadow-16">
203
-
<form {...verifyWebAuthnForm} class="flex flex-col gap-6">
204
-
<input {...fields.challenge.as('hidden', ctx.challenge.token)} />
205
-
<input {...fields.redirect.as('hidden', ctx.redirectUrl)} />
0
206
207
-
<div class="flex flex-col gap-2">
208
-
<h1 class="text-base-500 font-semibold">
209
-
{ctx.isSudo ? 'Confirm your identity' : 'Two-factor authentication'}
210
-
</h1>
211
-
<p class="text-base-300 text-neutral-foreground-3">
212
-
Insert your security key and touch it
213
-
{ctx.isSudo ? ' to continue.' : ' to verify your identity.'}
214
-
</p>
215
-
</div>
216
217
-
<danaus-webauthn-authenticate data-options={JSON.stringify(options)}>
218
<input {...fields.response.as('hidden', '')} data-target="webauthn-authenticate.response" />
219
220
<Button data-target="webauthn-authenticate.start" type="button" variant="primary">
···
225
data-target="webauthn-authenticate.status"
226
class="text-center text-base-300 text-neutral-foreground-3"
227
/>
228
-
</danaus-webauthn-authenticate>
229
230
-
<OtherMethodsMenu
231
-
factor="webauthn"
232
-
challenge={ctx.challenge.token}
233
-
redirectUrl={ctx.redirectUrl}
234
-
mfaStatus={ctx.mfaStatus}
235
-
isSudo={ctx.isSudo}
236
-
/>
237
-
</form>
0
238
</div>
239
</div>
240
</BaseLayout>,
···
200
201
<div class="flex flex-1 items-center justify-center p-4">
202
<div class="w-full max-w-96 rounded-xl bg-neutral-background-1 p-6 shadow-16">
203
+
<danaus-webauthn-authenticate class="contents" data-options={JSON.stringify(options)}>
204
+
<form {...verifyWebAuthnForm} class="flex flex-col gap-6" data-target="webauthn-authenticate.form">
205
+
<input {...fields.challenge.as('hidden', ctx.challenge.token)} />
206
+
<input {...fields.redirect.as('hidden', ctx.redirectUrl)} />
207
208
+
<div class="flex flex-col gap-2">
209
+
<h1 class="text-base-500 font-semibold">
210
+
{ctx.isSudo ? 'Confirm your identity' : 'Two-factor authentication'}
211
+
</h1>
212
+
<p class="text-base-300 text-neutral-foreground-3">
213
+
Insert your security key and touch it
214
+
{ctx.isSudo ? ' to continue.' : ' to verify your identity.'}
215
+
</p>
216
+
</div>
217
0
218
<input {...fields.response.as('hidden', '')} data-target="webauthn-authenticate.response" />
219
220
<Button data-target="webauthn-authenticate.start" type="button" variant="primary">
···
225
data-target="webauthn-authenticate.status"
226
class="text-center text-base-300 text-neutral-foreground-3"
227
/>
0
228
229
+
<OtherMethodsMenu
230
+
factor="webauthn"
231
+
challenge={ctx.challenge.token}
232
+
redirectUrl={ctx.redirectUrl}
233
+
mfaStatus={ctx.mfaStatus}
234
+
isSudo={ctx.isSudo}
235
+
/>
236
+
</form>
237
+
</danaus-webauthn-authenticate>
238
</div>
239
</div>
240
</BaseLayout>,
+6
-1
packages/danaus/src/web/scripts/webauthn-authenticate.js
···
26
return this.querySelector('[data-target="webauthn-authenticate.status"]');
27
}
28
0
0
0
0
0
29
connectedCallback() {
30
const optionsJson = this.dataset.options;
31
if (!optionsJson) {
···
103
status.textContent = 'Security key verified!';
104
105
// auto-submit the form
106
-
this.closest('form')?.submit();
107
} catch (err) {
108
if (startButton) {
109
startButton.disabled = false;
···
26
return this.querySelector('[data-target="webauthn-authenticate.status"]');
27
}
28
29
+
/** @type {HTMLFormElement | null} */
30
+
get formElement() {
31
+
return this.querySelector('[data-target="webauthn-authenticate.form"]');
32
+
}
33
+
34
connectedCallback() {
35
const optionsJson = this.dataset.options;
36
if (!optionsJson) {
···
108
status.textContent = 'Security key verified!';
109
110
// auto-submit the form
111
+
this.formElement?.submit();
112
} catch (err) {
113
if (startButton) {
114
startButton.disabled = false;