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
Ejemplo n.º 2
0
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))
Ejemplo n.º 3
0
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
Ejemplo n.º 5
0
 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
Ejemplo n.º 6
0
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))
Ejemplo n.º 7
0
    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")
Ejemplo n.º 8
0
    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))