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()