def test_generates_options_with_defaults( self, token_bytes_mock: MagicMock) -> None: token_bytes_mock.return_value = b"12345" options = generate_registration_options( rp_id="example.com", rp_name="Example Co", user_id="ABAV6QWPBEY9WOTOA1A4", user_name="lee", ) assert options.rp == PublicKeyCredentialRpEntity( id="example.com", name="Example Co", ) assert options.challenge == b"12345" assert options.user == PublicKeyCredentialUserEntity( id=b"ABAV6QWPBEY9WOTOA1A4", name="lee", display_name="lee", ) assert options.pub_key_cred_params[0] == PublicKeyCredentialParameters( type="public-key", alg=COSEAlgorithmIdentifier.ECDSA_SHA_256, ) assert options.timeout == 60000 assert options.exclude_credentials == [] assert options.authenticator_selection is None assert options.attestation == AttestationConveyancePreference.NONE
def get_challenge(self, *args, **kwargs) -> Challenge: # clear session variables prior to starting a new registration self.request.session.pop("challenge", None) stage: AuthenticateWebAuthnStage = self.executor.current_stage user = self.get_pending_user() # library accepts none so we store null in the database, but if there is a value # set, cast it to string to ensure it's not a django class authenticator_attachment = stage.authenticator_attachment if authenticator_attachment: authenticator_attachment = str(authenticator_attachment) registration_options: PublicKeyCredentialCreationOptions = generate_registration_options( rp_id=get_rp_id(self.request), rp_name=self.request.tenant.branding_title, user_id=user.uid, user_name=user.username, user_display_name=user.name, authenticator_selection=AuthenticatorSelectionCriteria( resident_key=str(stage.resident_key_requirement), user_verification=str(stage.user_verification), authenticator_attachment=authenticator_attachment, ), ) self.request.session["challenge"] = registration_options.challenge return AuthenticatorWebAuthnChallenge( data={ "type": ChallengeTypes.NATIVE.value, "registration": loads(options_to_json(registration_options)), })
def test_converts_options_to_JSON(self) -> None: options = generate_registration_options( rp_id="example.com", rp_name="Example Co", user_id="ABAV6QWPBEY9WOTOA1A4", user_name="lee", user_display_name="Lee", attestation=AttestationConveyancePreference.DIRECT, authenticator_selection=AuthenticatorSelectionCriteria( authenticator_attachment=AuthenticatorAttachment.PLATFORM, resident_key=ResidentKeyRequirement.REQUIRED, ), challenge=b"1234567890", exclude_credentials=[ PublicKeyCredentialDescriptor(id=b"1234567890"), ], supported_pub_key_algs=[COSEAlgorithmIdentifier.ECDSA_SHA_512], timeout=120000, ) output = options_to_json(options) assert json.loads(output) == { "rp": { "name": "Example Co", "id": "example.com" }, "user": { "id": "QUJBVjZRV1BCRVk5V09UT0ExQTQ", "name": "lee", "displayName": "Lee", }, "challenge": "MTIzNDU2Nzg5MA", "pubKeyCredParams": [{ "type": "public-key", "alg": -36 }], "timeout": 120000, "excludeCredentials": [{ "type": "public-key", "id": "MTIzNDU2Nzg5MA" }], "authenticatorSelection": { "authenticatorAttachment": "platform", "residentKey": "required", "requireResidentKey": True, "userVerification": "preferred", }, "attestation": "direct", }
def get(self, request, *args, **kwargs): credentials = [] for user_device in request.user.userwebauthn_set.all(): credentials.append(PublicKeyCredentialDescriptor(id=user_device.get_key_handle_bytes())) registration_options = json.loads( options_to_json( generate_registration_options( rp_id=zentral_settings["api"]["fqdn"], rp_name="Zentral", exclude_credentials=credentials, user_id=str(request.user.pk), user_name=request.user.username, ) ) ) request.session["webauthn_challenge"] = registration_options return super().get(request, *args, **kwargs)
def get_credential_options(user, *, challenge, rp_name, rp_id): """ Returns a dictionary of options for credential creation on the client side. """ _authenticator_selection = AuthenticatorSelectionCriteria() _authenticator_selection.user_verification = UserVerificationRequirement.DISCOURAGED options = pywebauthn.generate_registration_options( rp_id=rp_id, rp_name=rp_name, user_id=str(user.id), user_name=user.username, user_display_name=user.name or user.username, challenge=challenge, attestation=AttestationConveyancePreference.NONE, authenticator_selection=_authenticator_selection, ) return json.loads(options_to_json(options))
def test_generates_options_with_custom_values(self) -> None: options = generate_registration_options( rp_id="example.com", rp_name="Example Co", user_id="ABAV6QWPBEY9WOTOA1A4", user_name="lee", user_display_name="Lee", attestation=AttestationConveyancePreference.DIRECT, authenticator_selection=AuthenticatorSelectionCriteria( authenticator_attachment=AuthenticatorAttachment.PLATFORM, resident_key=ResidentKeyRequirement.REQUIRED, ), challenge=b"1234567890", exclude_credentials=[ PublicKeyCredentialDescriptor(id=b"1234567890"), ], supported_pub_key_algs=[COSEAlgorithmIdentifier.ECDSA_SHA_512], timeout=120000, ) assert options.rp == PublicKeyCredentialRpEntity(id="example.com", name="Example Co") assert options.challenge == b"1234567890" assert options.user == PublicKeyCredentialUserEntity( id=b"ABAV6QWPBEY9WOTOA1A4", name="lee", display_name="Lee", ) assert options.pub_key_cred_params[0] == PublicKeyCredentialParameters( type="public-key", alg=COSEAlgorithmIdentifier.ECDSA_SHA_512, ) assert options.timeout == 120000 assert options.exclude_credentials == [ PublicKeyCredentialDescriptor(id=b"1234567890") ] assert options.authenticator_selection == AuthenticatorSelectionCriteria( authenticator_attachment=AuthenticatorAttachment.PLATFORM, resident_key=ResidentKeyRequirement.REQUIRED, require_resident_key=True, ) assert options.attestation == AttestationConveyancePreference.DIRECT
def test_includes_optional_value_when_set(self) -> None: options = generate_registration_options( rp_id="example.com", rp_name="Example Co", user_id="ABAV6QWPBEY9WOTOA1A4", user_name="lee", exclude_credentials=[ PublicKeyCredentialDescriptor( id=b"1234567890", transports=[AuthenticatorTransport.USB], ) ], ) output = options_to_json(options) assert json.loads(output)["excludeCredentials"] == [{ "id": "MTIzNDU2Nzg5MA", "transports": ["usb"], "type": "public-key", }]
AuthenticatorSelectionCriteria, PublicKeyCredentialDescriptor, ResidentKeyRequirement, RegistrationCredential, ) ################ # # Examples of using webauthn for registration ceremonies # ################ # Simple Options simple_registration_options = generate_registration_options( rp_id="example.com", rp_name="Example Co", user_id="12345", user_name="bob", ) print("\n[Registration Options - Simple]") print(options_to_json(simple_registration_options)) # Complex Options complex_registration_options = generate_registration_options( rp_id="example.com", rp_name="Example Co", user_id="ABAV6QWPBEY9WOTOA1A4", user_name="lee", user_display_name="Lee", attestation=AttestationConveyancePreference.DIRECT, authenticator_selection=AuthenticatorSelectionCriteria(