feat: update login UI and JS for usernameless WebAuthn authentication

This commit is contained in:
Johan Lundberg 2026-02-17 13:42:35 +01:00
parent 32567b5484
commit ebe5497879
No known key found for this signature in database
GPG key ID: A6C152738D03C7D1
3 changed files with 24 additions and 27 deletions

View file

@ -73,20 +73,15 @@ async function beginRegistration() {
}
}
async function beginAuthentication(username) {
async function beginAuthentication() {
const statusEl = document.getElementById('webauthn-login-status');
const form = new FormData();
form.append('username', username);
try {
// Step 1: Get options from server
const beginRes = await fetch('/login/webauthn/begin', {
method: 'POST',
body: form,
});
// Step 1: Get options from server (no username needed)
const beginRes = await fetch('/login/webauthn/begin');
if (!beginRes.ok) {
const text = await beginRes.text();
if (statusEl) statusEl.innerHTML = text;
const data = await beginRes.json();
if (statusEl) statusEl.innerHTML = '<div role="alert">' + (data.error || 'Failed to start authentication') + '</div>';
return;
}
const options = await beginRes.json();
@ -100,7 +95,7 @@ async function beginAuthentication(username) {
});
}
// Step 3: Call browser WebAuthn API
// Step 3: Call browser WebAuthn API — browser shows passkey picker
const assertion = await navigator.credentials.get({ publicKey: publicKey });
// Step 4: Encode response for server
@ -126,24 +121,27 @@ async function beginAuthentication(username) {
if (completeRes.ok) {
const data = await completeRes.json();
if (data.redirect) {
window.location.href = data.redirect;
} else {
window.location.href = '/manage/credentials';
}
window.location.href = data.redirect || '/manage/credentials';
} else {
const text = await completeRes.text();
if (statusEl) statusEl.innerHTML = text;
const data = await completeRes.json().catch(function () { return {}; });
if (statusEl) statusEl.innerHTML = '<div role="alert">' + (data.error || 'Authentication failed') + '</div>';
}
} catch (err) {
if (statusEl) statusEl.innerHTML = '<div role="alert">Authentication failed: ' + err.message + '</div>';
}
}
// Wire up the registration button
// Wire up buttons
document.addEventListener('DOMContentLoaded', function () {
const registerBtn = document.getElementById('webauthn-register-btn');
if (registerBtn) {
registerBtn.addEventListener('click', beginRegistration);
}
const loginBtn = document.getElementById('webauthn-login-btn');
if (loginBtn) {
loginBtn.addEventListener('click', function () {
beginAuthentication();
});
}
});

View file

@ -24,12 +24,11 @@
<section>
<h2>Security key</h2>
<form id="webauthn-login-form">
<div>
<label for="webauthn-username">Username</label>
<input type="text" id="webauthn-username" name="username" required autocomplete="username">
</div>
<button type="button" id="webauthn-login-btn">Sign in with security key</button>
</form>
<div id="webauthn-login-status"></div>
<button type="button" id="webauthn-login-btn">Sign in with security key</button>
</section>
{% endblock %}
{% block scripts %}
<script src="/static/webauthn.js" defer></script>
{% endblock %}