feat: require discoverable credentials and prefer user verification in WebAuthnService
This commit is contained in:
parent
8aebd04d2a
commit
2ffe968342
2 changed files with 44 additions and 1 deletions
|
|
@ -10,6 +10,8 @@ from fido2.webauthn import (
|
|||
PublicKeyCredentialRpEntity,
|
||||
PublicKeyCredentialUserEntity,
|
||||
RegistrationResponse,
|
||||
ResidentKeyRequirement,
|
||||
UserVerificationRequirement,
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -36,6 +38,8 @@ class WebAuthnService:
|
|||
options, state = self._server.register_begin(
|
||||
user=user,
|
||||
credentials=existing_credentials,
|
||||
resident_key_requirement=ResidentKeyRequirement.REQUIRED,
|
||||
user_verification=UserVerificationRequirement.PREFERRED,
|
||||
)
|
||||
return dict(options), state
|
||||
|
||||
|
|
@ -58,7 +62,10 @@ class WebAuthnService:
|
|||
|
||||
Returns (options_dict, state_dict).
|
||||
"""
|
||||
options, state = self._server.authenticate_begin(credentials=credentials)
|
||||
options, state = self._server.authenticate_begin(
|
||||
credentials=credentials,
|
||||
user_verification=UserVerificationRequirement.PREFERRED,
|
||||
)
|
||||
return dict(options), state
|
||||
|
||||
def complete_authentication(
|
||||
|
|
|
|||
|
|
@ -134,6 +134,23 @@ def test_complete_registration_returns_credential_data() -> None:
|
|||
assert result.credential_data.credential_id == credential_id
|
||||
|
||||
|
||||
def test_begin_registration_requires_resident_key() -> None:
|
||||
service = _make_service()
|
||||
options, _state = service.begin_registration(user_id=b"user-123", username="alice")
|
||||
pub_key = options["publicKey"]
|
||||
auth_sel = pub_key["authenticatorSelection"]
|
||||
assert auth_sel["residentKey"] == "required"
|
||||
assert auth_sel["requireResidentKey"] is True
|
||||
|
||||
|
||||
def test_begin_registration_prefers_user_verification() -> None:
|
||||
service = _make_service()
|
||||
options, _state = service.begin_registration(user_id=b"user-123", username="alice")
|
||||
pub_key = options["publicKey"]
|
||||
auth_sel = pub_key["authenticatorSelection"]
|
||||
assert auth_sel["userVerification"] == "preferred"
|
||||
|
||||
|
||||
def test_begin_registration_with_existing_credentials() -> None:
|
||||
service = _make_service()
|
||||
_, cred_id, _attested = _generate_credential()
|
||||
|
|
@ -157,6 +174,25 @@ def test_begin_registration_with_existing_credentials() -> None:
|
|||
# --- Authentication tests ---
|
||||
|
||||
|
||||
def test_begin_authentication_without_credentials() -> None:
|
||||
"""Usernameless flow: no allowCredentials, browser shows passkey picker."""
|
||||
service = _make_service()
|
||||
options, state = service.begin_authentication()
|
||||
assert "publicKey" in options
|
||||
assert "challenge" in state
|
||||
pub_key = options["publicKey"]
|
||||
# allowCredentials should be absent or empty
|
||||
allow = pub_key.get("allowCredentials", [])
|
||||
assert allow is None or len(allow) == 0
|
||||
|
||||
|
||||
def test_begin_authentication_prefers_user_verification() -> None:
|
||||
service = _make_service()
|
||||
options, _state = service.begin_authentication()
|
||||
pub_key = options["publicKey"]
|
||||
assert pub_key["userVerification"] == "preferred"
|
||||
|
||||
|
||||
def test_begin_authentication_returns_options_and_state() -> None:
|
||||
service = _make_service()
|
||||
_, cred_id, _attested = _generate_credential()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue