diff --git a/uffd/mfa/views.py b/uffd/mfa/views.py index 69b5cc94a75144ca266c0f581094cac928723484..699d7ce99b8a3d0a3ac9ed2fe676550fc0b0ad63 100644 --- a/uffd/mfa/views.py +++ b/uffd/mfa/views.py @@ -103,13 +103,24 @@ def delete_totp(id): #pylint: disable=redefined-builtin return redirect(url_for('mfa.setup')) # WebAuthn support is optional because fido2 has a pretty unstable -# interface (v0.5.0 on buster and current version are completely -# incompatible) and might be difficult to install with the correct version +# interface and might be difficult to install with the correct version try: - from fido2.client import ClientData - from fido2.server import Fido2Server, RelyingParty - from fido2.ctap2 import AttestationObject, AuthenticatorData - from fido2 import cbor + import fido2 + if fido2.__version__.startswith('0.5.'): + from fido2.client import ClientData + from fido2.server import Fido2Server, RelyingParty as PublicKeyCredentialRpEntity + from fido2.ctap2 import AttestationObject, AuthenticatorData + from fido2 import cbor + cbor.encode = cbor.dumps + cbor.decode = lambda arg: cbor.loads(arg)[0] + elif fido2.__version__.startswith('0.9.'): + from fido2.client import ClientData + from fido2.webauthn import PublicKeyCredentialRpEntity + from fido2.server import Fido2Server + from fido2.ctap2 import AttestationObject, AuthenticatorData + from fido2 import cbor + else: + raise ImportError(f'Unsupported fido2 version: {fido2.__version__}') WEBAUTHN_SUPPORTED = True except ImportError as err: warn(_('2FA WebAuthn support disabled because import of the fido2 module failed (%s)')%err) @@ -119,7 +130,9 @@ bp.add_app_template_global(WEBAUTHN_SUPPORTED, name='webauthn_supported') if WEBAUTHN_SUPPORTED: def get_webauthn_server(): - return Fido2Server(RelyingParty(current_app.config.get('MFA_RP_ID', urllib.parse.urlsplit(request.url).hostname), current_app.config['MFA_RP_NAME'])) + hostname = urllib.parse.urlsplit(request.url).hostname + return Fido2Server(PublicKeyCredentialRpEntity(current_app.config.get('MFA_RP_ID', hostname), + current_app.config['MFA_RP_NAME'])) @bp.route('/setup/webauthn/begin', methods=['POST']) @login_required() @@ -140,14 +153,14 @@ if WEBAUTHN_SUPPORTED: user_verification='discouraged', ) session["webauthn-state"] = state - return cbor.dumps(registration_data) + return cbor.encode(registration_data) @bp.route('/setup/webauthn/complete', methods=['POST']) @login_required() @csrf_protect(blueprint=bp) def setup_webauthn_complete(): server = get_webauthn_server() - data = cbor.loads(request.get_data())[0] + data = cbor.decode(request.get_data()) client_data = ClientData(data["clientDataJSON"]) att_obj = AttestationObject(data["attestationObject"]) auth_data = server.register_complete(session["webauthn-state"], client_data, att_obj) @@ -156,7 +169,7 @@ if WEBAUTHN_SUPPORTED: db.session.commit() request.user.update_groups() ldap.session.commit() - return cbor.dumps({"status": "OK"}) + return cbor.encode({"status": "OK"}) @bp.route("/auth/webauthn/begin", methods=["POST"]) @login_required_pre_mfa(no_redirect=True) @@ -167,7 +180,7 @@ if WEBAUTHN_SUPPORTED: abort(404) auth_data, state = server.authenticate_begin(creds, user_verification='discouraged') session["webauthn-state"] = state - return cbor.dumps(auth_data) + return cbor.encode(auth_data) @bp.route("/auth/webauthn/complete", methods=["POST"]) @login_required_pre_mfa(no_redirect=True) @@ -176,7 +189,7 @@ if WEBAUTHN_SUPPORTED: creds = [method.cred for method in request.user_pre_mfa.mfa_webauthn_methods] if not creds: abort(404) - data = cbor.loads(request.get_data())[0] + data = cbor.decode(request.get_data()) credential_id = data["credentialId"] client_data = ClientData(data["clientDataJSON"]) auth_data = AuthenticatorData(data["authenticatorData"]) @@ -193,7 +206,7 @@ if WEBAUTHN_SUPPORTED: ) session['user_mfa'] = True set_request_user() - return cbor.dumps({"status": "OK"}) + return cbor.encode({"status": "OK"}) @bp.route('/setup/webauthn/<int:id>/delete') @login_required()