Auth: rate limiter, WebAuthn domain awareness, misc fixes
- Rate limiter tracks failures only; success resets counter - Record failures/successes after password verification, not before - WebAuthn rp_id/origin resolved dynamically from request domain - Management domains auto-discovered from nginx backend config - All WebAuthn operations validate domain against management list - Add GET /api/auth/webauthn/capable endpoint for frontend checks - Frontend checkWebAuthnCapable() function for domain-gated UI - Timing side-channel fix: pre-compute dummy hash at module load - Builtin admin seeded with random password (logged at WARNING) - Logout handler returns consistent response shape
This commit is contained in:
+27
-8
@@ -429,11 +429,13 @@ class TestWebAuthnConfig:
|
||||
"""Test WebAuthn configuration helpers."""
|
||||
|
||||
def test_get_rp_defaults(self) -> None:
|
||||
from lib.webauthn import get_origin, get_rp_id, get_rp_name
|
||||
from lib.webauthn import get_management_domains, get_rp_name, is_enabled
|
||||
|
||||
assert get_rp_id() == "localhost"
|
||||
assert is_enabled() is True
|
||||
assert get_rp_name() == "Vacuum Wall"
|
||||
assert get_origin() == "http://localhost"
|
||||
# get_management_domains reads from nginx config
|
||||
domains = get_management_domains()
|
||||
assert isinstance(domains, list)
|
||||
|
||||
|
||||
@patch.dict(
|
||||
@@ -482,14 +484,18 @@ class TestWebAuthnRegistration:
|
||||
def test_create_registration_options_basic(self) -> None:
|
||||
from lib.webauthn import create_registration_options
|
||||
|
||||
options = create_registration_options("testuser")
|
||||
options = create_registration_options(
|
||||
"testuser",
|
||||
origin="https://wall.example.com",
|
||||
rp_id="wall.example.com",
|
||||
)
|
||||
|
||||
assert isinstance(options, dict)
|
||||
assert "challenge" in options
|
||||
assert "rp" in options
|
||||
assert "user" in options
|
||||
assert "pubKeyCredParams" in options
|
||||
assert options["rp"]["id"] == "localhost"
|
||||
assert options["rp"]["id"] == "wall.example.com"
|
||||
assert options["user"]["name"] == "testuser"
|
||||
assert len(options["pubKeyCredParams"]) >= 1
|
||||
|
||||
@@ -524,6 +530,8 @@ class TestWebAuthnRegistration:
|
||||
},
|
||||
{"challenge": b64u_encode(b"testchallenge123")},
|
||||
"My Key",
|
||||
origin="https://wall.example.com",
|
||||
rp_id="wall.example.com",
|
||||
)
|
||||
|
||||
assert "id" in result
|
||||
@@ -542,7 +550,11 @@ class TestWebAuthnRegistration:
|
||||
|
||||
from lib.webauthn import create_registration_options
|
||||
|
||||
options = create_registration_options("testuser")
|
||||
options = create_registration_options(
|
||||
"testuser",
|
||||
origin="https://wall.example.com",
|
||||
rp_id="wall.example.com",
|
||||
)
|
||||
|
||||
# Should contain the existing credential in exclude list
|
||||
exclude_ids = [c["id"] for c in options.get("excludeCredentials", [])]
|
||||
@@ -590,7 +602,9 @@ class TestWebAuthnAuthentication:
|
||||
def test_create_auth_options_no_credentials(self) -> None:
|
||||
from lib.webauthn import create_authentication_options
|
||||
|
||||
result = create_authentication_options("nonexistentuser")
|
||||
result = create_authentication_options(
|
||||
"nonexistentuser", rp_id="wall.example.com"
|
||||
)
|
||||
assert result is None
|
||||
|
||||
def test_create_auth_options_with_credentials(self) -> None:
|
||||
@@ -598,7 +612,8 @@ class TestWebAuthnAuthentication:
|
||||
|
||||
from lib.webauthn import create_authentication_options
|
||||
|
||||
result = create_authentication_options("testuser")
|
||||
result = create_authentication_options("testuser", rp_id="wall.example.com")
|
||||
assert result is not None
|
||||
|
||||
assert result is not None
|
||||
assert len(result["allowCredentials"]) == 1
|
||||
@@ -618,6 +633,8 @@ class TestWebAuthnAuthentication:
|
||||
},
|
||||
},
|
||||
{"challenge": _FAKE_CHALLENGE},
|
||||
origin="https://wall.example.com",
|
||||
rp_id="wall.example.com",
|
||||
)
|
||||
|
||||
def test_verify_authentication_success(self) -> None:
|
||||
@@ -643,6 +660,8 @@ class TestWebAuthnAuthentication:
|
||||
},
|
||||
},
|
||||
{"challenge": _FAKE_CHALLENGE, "sign_count": 0},
|
||||
origin="https://wall.example.com",
|
||||
rp_id="wall.example.com",
|
||||
)
|
||||
|
||||
assert result is True
|
||||
|
||||
Reference in New Issue
Block a user