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
+7 -3
View File
@@ -259,13 +259,14 @@ def webauthn_register_begin():
Endpoint:
POST /api/auth/webauthn/register-begin
Body:
{ "username": "..." }
Returns:
Registration options for navigator.credentials.create()
"""
try:
body = request.get_json(silent=True) or {}
user_ctx = getattr(request, "_user_ctx", None)
if user_ctx is not None:
body["username"] = user_ctx["username"]
origin, rp_id = _resolve_webauthn_origin()
body["webauthn_origin"] = origin
body["webauthn_rp_id"] = rp_id
@@ -282,12 +283,15 @@ def webauthn_register_finish():
Endpoint:
POST /api/auth/webauthn/register-finish
Body:
{ "username": "...", "credential_response": {...}, "registration_options": {...}, "name": "..." }
{ "credential_response": {...}, "registration_options": {...}, "name": "..." }
Returns:
{ "ok": true, "credential": {...} }
"""
try:
body = request.get_json(silent=True) or {}
user_ctx = getattr(request, "_user_ctx", None)
if user_ctx is not None:
body["username"] = user_ctx["username"]
origin, rp_id = _resolve_webauthn_origin()
body["webauthn_origin"] = origin
body["webauthn_rp_id"] = rp_id