auth: make session binding optional, enforce WebAuthn credential ownership

- Make session_id parameter optional in validate_token — only enforced when
  provided, allowing WebSocket auth which cannot carry custom headers
- Remove decode_token round-trip from daemon WS handler
- Override WebAuthn registration username from JWT user_ctx to prevent users
  from registering credentials under another user's account
- Frontend no longer sends username for WebAuthn registration
- Redirect to login on 401 after token refresh fails for POST requests
- Remove redundant auth session check from login page load
- Add tests for session_id semantics and WebAuthn ownership guard
This commit is contained in:
2026-08-12 14:16:49 +00:00
parent 3654209b78
commit e01574c67e
7 changed files with 101 additions and 49 deletions
+3 -13
View File
@@ -91,20 +91,11 @@ function addCredentialModal() {
setModalProcessing(true);
refreshModals();
const user = JSON.parse(sessionStorage.getItem('vw:user') || 'null');
const username = user?.username || '';
if (!username) {
toast('Username not available', 'error');
setModalProcessing(false);
refreshModals();
return;
}
try {
// Step 1: Get registration options
// Step 1: Get registration options (username from JWT)
const beginRes = await apiFetch('/api/auth/webauthn/register-begin', {
method: 'POST',
body: { username },
body: {},
});
if (!beginRes.ok) {
@@ -117,11 +108,10 @@ function addCredentialModal() {
const credentialName = document.getElementById('cred-name')?.value?.trim() || '';
const credentialResponse = await startRegistration(options);
// Step 3: Verify with server
// Step 3: Verify with server (username from JWT)
const finishRes = await apiFetch('/api/auth/webauthn/register-finish', {
method: 'POST',
body: {
username,
credential_response: credentialResponse,
registration_options: options,
name: credentialName,