def test_generates_options_with_defaults( self, token_bytes_mock: MagicMock) -> None: token_bytes_mock.return_value = b"12345" options = generate_authentication_options(rp_id="example.com") assert options.challenge == b"12345" assert options.timeout == 60000 assert options.rp_id == "example.com" assert options.allow_credentials == [] assert options.user_verification == UserVerificationRequirement.PREFERRED
def get_webauthn_challenge_userless(request: HttpRequest) -> dict: """Same as `get_webauthn_challenge`, but allows any client device. We can then later check who the device belongs to.""" request.session.pop("challenge", None) authentication_options = generate_authentication_options( rp_id=get_rp_id(request), allow_credentials=[], ) request.session["challenge"] = authentication_options.challenge return loads(options_to_json(authentication_options))
def get_assertion_options(user, *, challenge, rp_id): """ Returns a dictionary of options for assertion retrieval on the client side. """ options = pywebauthn.generate_authentication_options( rp_id=rp_id, challenge=challenge, allow_credentials=_get_webauthn_user_public_key_credential_descriptors( user, rp_id=rp_id ), user_verification=UserVerificationRequirement.DISCOURAGED, ) return json.loads(options_to_json(options))
def test_generates_options_with_custom_values(self) -> None: options = generate_authentication_options( rp_id="example.com", allow_credentials=[ PublicKeyCredentialDescriptor(id=b"12345"), ], challenge=b"this_is_a_challenge", timeout=12000, user_verification=UserVerificationRequirement.REQUIRED, ) assert options.challenge == b"this_is_a_challenge" assert options.timeout == 12000 assert options.rp_id == "example.com" assert options.allow_credentials == [ PublicKeyCredentialDescriptor(id=b"12345"), ] assert options.user_verification == UserVerificationRequirement.REQUIRED
def set_challenge(self): credentials = [] appid = None for user_device in self.user.userwebauthn_set.all(): credentials.append( PublicKeyCredentialDescriptor( id=user_device.get_key_handle_bytes())) device_appid = user_device.get_appid() if device_appid: appid = device_appid if credentials: authentication_options = json.loads( options_to_json( generate_authentication_options( rp_id=zentral_settings["api"]["fqdn"], allow_credentials=credentials, ))) if appid: authentication_options["extensions"] = {"appid": appid} challenge = self.session["webauthn_challenge"] = dict( authentication_options) return challenge
def get_webauthn_challenge(request: HttpRequest, device: Optional[WebAuthnDevice] = None) -> dict: """Send the client a challenge that we'll check later""" request.session.pop("challenge", None) allowed_credentials = [] if device: # We want all the user's WebAuthn devices and merge their challenges for user_device in WebAuthnDevice.objects.filter( user=device.user).order_by("name"): user_device: WebAuthnDevice allowed_credentials.append(user_device.descriptor) authentication_options = generate_authentication_options( rp_id=get_rp_id(request), allow_credentials=allowed_credentials, ) request.session["challenge"] = authentication_options.challenge return loads(options_to_json(authentication_options))
def get(self, request): """ :param request: The current request :type request: ~django.http.HttpResponse :return: The mfa challenge as JSON :rtype: ~django.http.JsonResponse """ if "mfa_user_id" not in request.session: return JsonResponse( { "success": False, "error": _("You need to log in first") }, status=403) if request.user.is_authenticated: return JsonResponse({ "success": False, "error": _("You are already logged in.") }) user = get_user_model().objects.get(id=request.session["mfa_user_id"]) webauthn_challenge = generate_authentication_options( rp_id=settings.HOSTNAME, allow_credentials=[ PublicKeyCredentialDescriptor(id=key.key_id) for key in user.mfa_keys.all() ], ) request.session["challenge"] = bytes_to_base64url( webauthn_challenge.challenge) # pylint: disable=http-response-with-content-type-json return HttpResponse(options_to_json(webauthn_challenge), content_type="application/json")
UserVerificationRequirement, AuthenticationCredential, ) ################ # # Examples of using webauthn for authentication ceremonies # # Authentication responses are representative of WebAuthn credential responses # as they would be encoded for transmission from the browser to the RP as JSON. This # primarily means byte arrays are encoded as Base64URL on the client. # ################ # Simple Options simple_authentication_options = generate_authentication_options( rp_id="example.com") print("\n[Authentication Options - Simple]") print(options_to_json(simple_authentication_options)) # Complex Options complex_authentication_options = generate_authentication_options( rp_id="example.com", challenge=b"1234567890", timeout=12000, allow_credentials=[PublicKeyCredentialDescriptor(id=b"1234567890")], user_verification=UserVerificationRequirement.REQUIRED, ) print("\n[Authentication Options - Complex]") print(options_to_json(complex_authentication_options))