py_webauthn (the webauthn package on PyPI) generates registration and authentication options and verifies the responses, including every attestation format, on top of pyca/cryptography. The check fed it the same software-authenticator registration and assertions used for SimpleWebAuthn, go-webauthn and WebAuthn4J, which is what makes the differences between them visible.
The policy defaults are the loosest of the four. generate_registration_options() sends no authenticatorSelection member at all, so browsers fall back to user verification preferred and resident keys discouraged — passkeys that are not discoverable — and verify_registration_response() and verify_authentication_response() accepted responses with the UV flag clear unless require_user_verification=True is passed. A login built on the defaults therefore accepts a tap on a security key without a PIN. Set the policy explicitly on both sides, as the sample does.
Assertion signatures must be ASN.1 DER: a raw 64-byte r‖s signature failed with Could not verify authentication signature, a message that does not mention the encoding; the DER ⇄ raw converter shows whether a test client produced the wrong form. A high-S DER signature verified. Ed25519 (-8), ES256 (-7) and RS256 (-257) are offered by default; there is no ML-DSA entry, unlike SimpleWebAuthn 14.
Counters are strict: Response sign count of 3 was not greater than current count of 5, and the same for 0 after 5, while 0 after 0 — what synced passkeys report — verified. Store the returned new_sign_count after every successful login.
Decode raw browser output with the WebAuthn inspector and experiment safely in the passkey sandbox.