"""WebAuthn passkey ceremony helpers for the single dashboard operator.""" import base64 import json import secrets from webauthn import ( generate_authentication_options, generate_registration_options, options_to_json, verify_authentication_response, verify_registration_response, ) from webauthn.helpers.structs import ( AuthenticatorSelectionCriteria, PublicKeyCredentialDescriptor, ResidentKeyRequirement, UserVerificationRequirement, ) def encode(value: bytes) -> str: return base64.urlsafe_b64encode(value).rstrip(b"=").decode() def decode(value: str) -> bytes: return base64.urlsafe_b64decode(value + "=" * (-len(value) % 4)) def registration_options(*, rp_id: str, excluded: list[bytes] | None = None) -> tuple[dict, bytes]: challenge = secrets.token_bytes(32) options = generate_registration_options( rp_id=rp_id, rp_name="Stackchain Dashboard", user_name="stackchain-operator", user_display_name="Stackchain operator", challenge=challenge, exclude_credentials=[ PublicKeyCredentialDescriptor(id=credential_id) for credential_id in (excluded or []) ], authenticator_selection=AuthenticatorSelectionCriteria( resident_key=ResidentKeyRequirement.PREFERRED, user_verification=UserVerificationRequirement.REQUIRED, ), ) return json.loads(options_to_json(options)), challenge def authentication_options(*, rp_id: str, credentials: list[bytes]) -> tuple[dict, bytes]: challenge = secrets.token_bytes(32) options = generate_authentication_options( rp_id=rp_id, challenge=challenge, allow_credentials=[ PublicKeyCredentialDescriptor(id=credential_id) for credential_id in credentials ], user_verification=UserVerificationRequirement.REQUIRED, ) return json.loads(options_to_json(options)), challenge def verify_registration( *, credential: dict, challenge: bytes, rp_id: str, origin: str ): return verify_registration_response( credential=credential, expected_challenge=challenge, expected_rp_id=rp_id, expected_origin=origin, require_user_verification=True, ) def verify_authentication( *, credential: dict, challenge: bytes, rp_id: str, origin: str, stored ): return verify_authentication_response( credential=credential, expected_challenge=challenge, expected_rp_id=rp_id, expected_origin=origin, credential_public_key=stored.public_key, credential_current_sign_count=stored.sign_count, require_user_verification=True, )