From 446b3df52d6400ed5b90d8160aa5aeabc4d0d455 Mon Sep 17 00:00:00 2001 From: Kyle Isom Date: Mon, 16 Mar 2026 19:27:44 -0700 Subject: [PATCH] Fix WebAuthn CSRF; clarify security key UI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix webauthn.js CSRF token: read HMAC header value from body hx-headers attribute instead of cookie nonce - Update profile labels to mention security keys/FIDO2 alongside passkeys Security: CSRF double-submit was broken for fetch()-based WebAuthn requests — JS was sending the cookie nonce as the header value instead of the HMAC. Fixed by reading the server-rendered header token from the DOM. Co-Authored-By: Claude Opus 4.6 --- web/static/webauthn.js | 13 ++++++++++--- web/templates/fragments/webauthn_enroll.html | 2 +- web/templates/profile.html | 6 +++--- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/web/static/webauthn.js b/web/static/webauthn.js index 87ad173..0796a15 100644 --- a/web/static/webauthn.js +++ b/web/static/webauthn.js @@ -25,10 +25,17 @@ return bytes.buffer; } - // Get the CSRF token from the cookie for mutating requests. + // Get the CSRF token from the body's hx-headers attribute (HMAC header value). + // The cookie holds the nonce; the header holds the HMAC — they are different. function getCSRFToken() { - var match = document.cookie.match(/(?:^|;\s*)mcias_csrf=([^;]+)/); - return match ? match[1] : ''; + try { + var hdr = document.body.getAttribute('hx-headers'); + if (hdr) { + var parsed = JSON.parse(hdr); + if (parsed['X-CSRF-Token']) return parsed['X-CSRF-Token']; + } + } catch (e) { /* fall through */ } + return ''; } function showError(id, msg) { diff --git a/web/templates/fragments/webauthn_enroll.html b/web/templates/fragments/webauthn_enroll.html index 6f30b95..419ac07 100644 --- a/web/templates/fragments/webauthn_enroll.html +++ b/web/templates/fragments/webauthn_enroll.html @@ -4,7 +4,7 @@
- +
diff --git a/web/templates/profile.html b/web/templates/profile.html index 7110845..42f562f 100644 --- a/web/templates/profile.html +++ b/web/templates/profile.html @@ -10,12 +10,12 @@
{{if .WebAuthnEnabled}}
-

Passkeys

+

Passkeys & Security Keys

- Passkeys let you sign in without a password using your device's biometrics or a security key. + Register a passkey (Touch ID, Windows Hello) or a hardware security key (YubiKey, FIDO2) for passwordless sign-in or two-factor authentication.

{{template "webauthn_credentials" .}} -

Add a Passkey

+

Add a Passkey or Security Key

{{template "webauthn_enroll" .}}