Add WebAuthn passkey biometric unlock + dark toggle on auth screen

- Dark/light theme toggle now always visible (moved outside auth/vault sections)
- New passkey_challenges and passkey_credentials tables
- /passkey/register/begin + /passkey/register/complete endpoints
- /passkey/login/begin + /passkey/login/complete endpoints
- CBOR decoder + COSE key parser for WebAuthn attestation/assertion
- ES256 (P-256) signature verification via OpenSSL
- Client-side: register passkey button in settings, passkey login on auth screen
- First passkey login prompts for master password once to derive AES-GCM key
- Passkey login requires username input before biometric prompt
This commit is contained in:
2026-05-09 13:01:59 +01:00
parent 8dd2fc71cf
commit 367b85e991
3 changed files with 288 additions and 3 deletions
+9 -3
View File
@@ -57,9 +57,10 @@
<!-- Floating action button -->
<button class="fab" onclick="openAdd()" title="Add password">+</button>
<div class="vault">
<h1>🔐 Vault <span>XAMPP</span>
<button class="btn btn-outline btn-xs" onclick="toggleTheme()" style="margin-left:auto">🌓</button>
</h1>
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:0.6rem">
<h1 style="margin-bottom:0">🔐 Vault <span>XAMPP</span></h1>
<button class="btn btn-outline btn-xs" onclick="toggleTheme()" title="Toggle theme">🌓</button>
</div>
<!-- Auth Section -->
<div id="authSection" class="auth-section">
@@ -72,6 +73,7 @@
<input type="text" id="loginUsername" placeholder="Username" autocomplete="off">
<input type="password" id="loginPassword" placeholder="Master Password" autocomplete="off">
<button class="btn" id="loginBtn" onclick="login()">🔓 Unlock</button>
<button class="btn btn-outline btn-sm" onclick="loginWithPasskey()" title="Use passkey" style="white-space:nowrap">🔐 Passkey</button>
</div>
</div>
<div id="registerForm" class="hidden">
@@ -122,6 +124,10 @@
<span>📧 Show email</span>
<div class="toggle-switch active" id="showEmailToggle"></div>
</div>
<div class="settings-item" onclick="if(window.PublicKeyCredential){registerPasskey()}else{toast('Passkeys not supported','error')};event.stopPropagation();">
<span>🔐 Set up passkey</span>
<span style="color:var(--accent);font-size:0.7rem"></span>
</div>
</div>
</div>
<button class="btn btn-outline btn-sm" onclick="doLogout()">🔒 Lock</button>