diff --git a/uffd/mfa/models.py b/uffd/mfa/models.py
index 547ae579be477d69ca40a37178b59d6d7d6b20ab..feffe466681ae9d86e0d8b7045b5afb322be7bf7 100644
--- a/uffd/mfa/models.py
+++ b/uffd/mfa/models.py
@@ -72,10 +72,18 @@ class TOTPMethod(MFAMethod):
 		s = self.key + '='*(8 - (len(self.key) % 8))
 		return base64.b32decode(s.encode())
 
+	@property
+	def issuer(self):
+		return urllib.parse.urlsplit(request.url).hostname
+
+	@property
+	def accountname(self):
+		return self.user.loginname
+
 	@property
 	def key_uri(self):
-		issuer = urllib.parse.quote(urllib.parse.urlsplit(request.url).netloc)
-		accountname = urllib.parse.quote(self.user.loginname.encode())
+		issuer = urllib.parse.quote(self.issuer)
+		accountname = urllib.parse.quote(self.accountname)
 		params = {'secret': self.key, 'issuer': issuer}
 		if 'MFA_ICON_URL' in current_app.config:
 			params['image'] = current_app.config['MFA_ICON_URL']
diff --git a/uffd/mfa/templates/setup.html b/uffd/mfa/templates/setup.html
index b09bd47d8312be3e637d88fe4c095b5145e136cf..5ca7003abb3762860c665d16b56ed281999f1805 100644
--- a/uffd/mfa/templates/setup.html
+++ b/uffd/mfa/templates/setup.html
@@ -1,44 +1,163 @@
 {% extends 'base.html' %}
 
 {% block body %}
+{% if totp_methods or webauthn_methods %}
+<p>Two-factor authentication is currently <strong>enabled</strong>. Delete all registered methods to disable it.</p>
+{% else %}
+<p>Two-factor authentication is currently <strong>disabled</strong>. Setup an authentication method below to enable it.</p>
+{% endif %}
+
+<hr>
+
+<div class="row mt-3">
+	<div class="col-12 col-md-5">
+		<h4>Authenticator Apps</h4>
+		<p>Use an authenticator application on your mobile device as a second factor.</p>
+		<p>The authenticator app generates a 6-digit one-time code each time you login.
+		Compatible apps are freely available for most phones.</p>
+	</div>
 
-<div class="btn-toolbar">
-	<a class="btn btn-primary mb-2 ml-auto" href="{{ url_for('mfa.setup_totp') }}">Setup TOTP</a>
-	<a class="btn btn-primary mb-2 ml-2" href="{{ url_for('mfa.setup_webauthn') }}">Setup FIDO</a>
+	<div class="col-12 col-md-7">
+		<form class="form mb-3" action="{{ url_for('mfa.setup_totp') }}">
+			<div class="row m-0">
+				<label class="sr-only" for="totp-name">Name</label>
+				<input type="text" name="name" class="form-control mb-2 col-12 col-lg-auto mr-2" style="width: 15em;" id="totp-name" placeholder="Name" required>
+				<button type="submit" id="totp-submit" class="btn btn-primary mb-2 col">Setup new authenticator</button>
+			</div>
+		</form>
+
+		<table class="table">
+			<thead>
+				<tr>
+					<th scope="col">Name</th>
+					<th scope="col">Registered On</th>
+					<th scope="col"></th>
+				</tr>
+			</thead>
+			<tbody>
+				{% for method in totp_methods %}
+				<tr>
+					<td>{{ method.name }}</td>
+					<td>{{ method.created.strftime('%b %d, %Y') }}</td>
+					<td><a class="btn btn-sm btn-danger float-right" href="{{ url_for('mfa.delete_totp', id=method.id) }}">Delete</a></td>
+				</tr>
+				{% endfor %}
+				{% if not totp_methods %}
+				<tr class="table-secondary">
+					<td colspan=3 class="text-center">No authenticator apps registered yet</td>
+				</tr>
+				{% endif %}
+			</tbody>
+		</table>
+	</div>
 </div>
 
-{% if totp_methods or webauthn_methods %}
-<table class="table">
-	<thead>
-		<tr>
-			<th scope="col" colspan=2>Name</th>
-			<th scope="col">Registered On</th>
-			<th scope="col"></th>
-		</tr>
-	</thead>
-	<tbody>
-		{% for method in totp_methods %}
-		<tr>
-			<td style="width: 0.5em;"><i class="fas fa-mobile-alt"></i></td>
-			<td>{{ method.name }}</td>
-			<td>{{ method.created }}</td>
-			<td><a class="btn btn-sm btn-danger float-right" href="{{ url_for('mfa.delete_totp', id=method.id) }}">Delete</a></td>
-		</tr>
-		{% endfor %}
-		{% for method in webauthn_methods %}
-		<tr>
-			<td style="width: 0.5em;"><i class="fab fa-usb"></i></td>
-			<td>{{ method.name }}</td>
-			<td>{{ method.created }}</td>
-			<td><a class="btn btn-sm btn-danger float-right" href="{{ url_for('mfa.delete_webauthn', id=method.id) }}">Delete</a></td>
-		</tr>
-		{% endfor %}
-	</tbody>
-</table>
-{% else %}
-<div class="alert alert-info" role="alert">
-	You have not setup any two-factor methods yet!
+<hr>
+
+<div class="row">
+	<div class="col-12 col-md-5">
+		<h4>U2F and FIDO2 Devices</h4>
+		<p>Use an U2F or FIDO2 compatible hardware security key as a second factor.</p>
+		<p>U2F and FIDO2 devices are not supported by all browsers and can be particularly difficult to use on mobile devices.
+		<strong>It is strongly recommended to also setup an authenticator app</strong> to be able to login on all browsers.</p>
+	</div>
+
+	<div class="col-12 col-md-7">
+		<noscript>
+			<div class="alert alert-warning" role="alert">Enable javascript in your browser to use U2F and FIDO2 devices!</div>
+		</noscript>
+		<div id="webauthn-alert" class="alert alert-warning d-none" role="alert"></div>
+		<form id="webauthn-form" class="form mb-3">
+			<div class="row m-0">
+				<label class="sr-only" for="webauthn-name">Name</label>
+				<input type="text" class="form-control mb-2 col-12 col-lg-auto mr-2" style="width: 15em;" id="webauthn-name" placeholder="Name" required>
+				<button type="submit" id="webauthn-btn" class="btn btn-primary mb-2 col" disabled>
+					<span id="webauthn-spinner" class="spinner-border spinner-border-sm d-none" role="status" aria-hidden="true"></span>
+					<span id="webauthn-btn-text">Setup new device</span>
+				</button>
+			</div>
+		</form>
+
+		<table class="table">
+			<thead>
+				<tr>
+					<th scope="col">Name</th>
+					<th scope="col">Registered On</th>
+					<th scope="col"></th>
+				</tr>
+			</thead>
+			<tbody>
+				{% for method in webauthn_methods %}
+				<tr>
+					<td>{{ method.name }}</td>
+					<td>{{ method.created.strftime('%b %d, %Y') }}</td>
+					<td><a class="btn btn-sm btn-danger float-right" href="{{ url_for('mfa.delete_webauthn', id=method.id) }}">Delete</a></td>
+				</tr>
+				{% endfor %}
+				{% if not webauthn_methods %}
+				<tr class="table-secondary">
+					<td colspan=3 class="text-center">No devices registered yet</td>
+				</tr>
+				{% endif %}
+			</tbody>
+		</table>
+	</div>
 </div>
-{% endif %}
+
+<!-- spacer for floating footer -->
+<div class="mb-5"></div>
+
+<script src="{{ url_for('static', filename="cbor.js") }}"></script>
+<script>
+
+$('#webauthn-form').on('submit', function(e) {
+	$('#webauthn-alert').addClass('d-none');
+	$('#webauthn-spinner').removeClass('d-none');
+	$('#webauthn-btn-text').text('Contacting server');
+	$('#webauthn-btn').prop('disabled', true);
+	fetch({{ url_for('mfa.setup_webauthn_begin')|tojson }}, {
+		method: 'POST',
+	}).then(function(response) {
+		if(response.ok) return response.arrayBuffer();
+		throw new Error('Error getting registration data!');
+	}).then(CBOR.decode).then(function(options) {
+		$('#webauthn-btn-text').text('Waiting for response from your device');
+		return navigator.credentials.create(options);
+	}).then(function(attestation) {
+		return fetch({{ url_for('mfa.setup_webauthn_complete')|tojson }}, {
+			method: 'POST',
+			headers: {'Content-Type': 'application/cbor'},
+			body: CBOR.encode({
+				"attestationObject": new Uint8Array(attestation.response.attestationObject),
+				"clientDataJSON": new Uint8Array(attestation.response.clientDataJSON),
+				"name": $('#webauthn-name').val()
+			})
+		});
+	}).then(function(response) {
+		if (response.ok) {
+			$('#webauthn-spinner').addClass('d-none');
+			$('#webauthn-btn-text').text('Success');
+			window.location = {{ url_for('mfa.setup')|tojson }};
+		} else {
+		throw new Error('Server rejected authenticator response');
+		}
+	}, function(reason) {
+		$('#webauthn-alert').text('Registration failed!');
+		$('#webauthn-alert').removeClass('d-none');
+		$('#webauthn-spinner').addClass('d-none');
+		$('#webauthn-btn-text').text('Retry registration');
+		$('#webauthn-btn').prop('disabled', false);
+	});
+	return false;
+});
+
+if (typeof(PublicKeyCredential) != "undefined") {
+	$('#webauthn-btn').prop('disabled', false);
+} else {
+	$('#webauthn-alert').text('U2F and FIDO2 devices are not supported by your browser!');
+	$('#webauthn-alert').removeClass('d-none');
+}
+
+</script>
 
 {% endblock %}
diff --git a/uffd/mfa/templates/setup_totp.html b/uffd/mfa/templates/setup_totp.html
index 8272a8b5ce0b295c4c5033d4dfb4b150b29ae3dc..db83e46be5865aa7510becc68f7988fb91d19d35 100644
--- a/uffd/mfa/templates/setup_totp.html
+++ b/uffd/mfa/templates/setup_totp.html
@@ -2,22 +2,37 @@
 
 {% block body %}
 
-<div style="max-width: 75vh;" class="mx-auto">
-	{{ method.key_uri|qrcode_svg(width='100%', height='100%') }}
-</div>
+<p>Install an authenticator application on your mobile device like FreeOTP or Google Authenticator and scan this QR code. On Apple devices you can use an app called "Authenticator".</p>
 
-<form action="{{ url_for('mfa.setup_totp') }}" method="POST" class="form">
-<div class="form-group">
-	<label for="code">Code</label>
-	<input name="code" type="text" class="form-control" required="required">
-</div>
-<div class="form-group">
-	<label for="name">Authenticator Name</label>
-	<input name="name" type="text" class="form-control" required="required">
-</div>
-<div class="form-group">
-	<button type="submit" class="btn btn-primary btn-block">Verify</button>
+<div class="row">
+	<div class="mx-auto col-9 col-md-4 mb-3">
+		<a href="{{ method.key_uri }}">
+			{{ method.key_uri|qrcode_svg(width='100%', height='100%') }}
+		</a>
+	</div>
+	<div class="col-12 col-md-8">
+		<p>If you are on your mobile device and cannot scan the code, you can click on it to open it with your authenticator app. If that does not work, enter the following details manually into your authenticator app:</p>
+		<p>
+		Issuer: {{ method.issuer }}<br>
+		Account: {{ method.accountname }}<br>
+		Secret: {{ method.key }}<br>
+		Type: TOTP (time-based)<br>
+		Digits: 6<br>
+		Hash algorithm: SHA1<br>
+		Interval/period: 30 seconds
+		</p>
+
+	</div>
 </div>
+
+<form action="{{ url_for('mfa.setup_totp', name=name) }}" method="POST" class="form">
+	<div class="row m-0">
+		<input type="text" name="code" class="form-control mb-2 mr-2 col-auto col-md" id="code" placeholder="Code" required autofocus>
+		<button type="submit" class="btn btn-primary mb-2 col col-md-auto">Verify and complete setup</button>
+	</div>
 </form>
 
+<!-- spacer for floating footer -->
+<div class="mb-5"></div>
+
 {% endblock %}
diff --git a/uffd/mfa/templates/setup_webauthn.html b/uffd/mfa/templates/setup_webauthn.html
deleted file mode 100644
index 3a14315602e7572360d2b7e1f44be2cb48bd202e..0000000000000000000000000000000000000000
--- a/uffd/mfa/templates/setup_webauthn.html
+++ /dev/null
@@ -1,65 +0,0 @@
-{% extends 'base.html' %}
-
-{% block body %}
-
-<div id="register-alert" class="alert alert-warning d-none" role="alert"></div>
-
-<form action="{{ url_for('mfa.setup_webauthn') }}" method="POST" class="form">
-<div class="form-group">
-	<label for="name">Authenticator Name</label>
-	<input name="name" type="text" id="method-name" class="form-control" required="required">
-</div>
-<div class="form-group">
-	<button type="submit" id="register-btn" class="btn btn-primary btn-block">
-		<span id="register-spinner" class="spinner-border spinner-border-sm d-none" role="status" aria-hidden="true"></span>
-		<span id="register-btn-text">Register token</span>
-	</button>
-</div>
-</form>
-
-<script src="{{ url_for('static', filename="cbor.js") }}"></script>
-<script>
-
-$('form').on('submit', function(e) {
-	$('#register-alert').addClass('d-none');
-	$('#register-spinner').removeClass('d-none');
-	$('#register-btn-text').text('Contacting server');
-	$('#register-btn').prop('disabled', true);
-	fetch({{ url_for('mfa.setup_webauthn_begin')|tojson }}, {
-		method: 'POST',
-	}).then(function(response) {
-		if(response.ok) return response.arrayBuffer();
-		throw new Error('Error getting registration data!');
-	}).then(CBOR.decode).then(function(options) {
-		$('#register-btn-text').text('Waiting for response from your device');
-		return navigator.credentials.create(options);
-	}).then(function(attestation) {
-		return fetch({{ url_for('mfa.setup_webauthn_complete')|tojson }}, {
-			method: 'POST',
-			headers: {'Content-Type': 'application/cbor'},
-			body: CBOR.encode({
-				"attestationObject": new Uint8Array(attestation.response.attestationObject),
-				"clientDataJSON": new Uint8Array(attestation.response.clientDataJSON),
-				"name": $('#method-name').val()
-			})
-		});
-	}).then(function(response) {
-		if (response.ok) {
-			$('#register-spinner').addClass('d-none');
-			$('#register-btn-text').text('Success');
-			window.location = {{ url_for('mfa.setup')|tojson }};
-		} else {
-		throw new Error('Server rejected authenticator response');
-		}
-	}, function(reason) {
-		$('#register-alert').text('Registration failed!');
-		$('#register-alert').removeClass('d-none');
-		$('#register-spinner').addClass('d-none');
-		$('#register-btn-text').text('Retry registration');
-		$('#register-btn').prop('disabled', false);
-	});
-	return false;
-});
-</script>
-
-{% endblock %}
diff --git a/uffd/mfa/views.py b/uffd/mfa/views.py
index 3baebf11eb8e80b676306697a4e5ec7531675775..a46a6802d56841dfc1a8196a6c65b1caa14f9176 100644
--- a/uffd/mfa/views.py
+++ b/uffd/mfa/views.py
@@ -27,13 +27,13 @@ def setup_totp():
 	user = get_current_user()
 	method = TOTPMethod(user)
 	session['mfa_totp_key'] = method.key
-	return render_template('setup_totp.html', method=method)
+	return render_template('setup_totp.html', method=method, name=request.values['name'])
 
 @bp.route('/setup/totp', methods=['POST'])
 @login_required()
 def setup_totp_finish():
 	user = get_current_user()
-	method = TOTPMethod(user, name=request.form['name'], key=session['mfa_totp_key'])
+	method = TOTPMethod(user, name=request.values['name'], key=session['mfa_totp_key'])
 	del session['mfa_totp_key']
 	if method.verify(request.form['code']):
 		db.session.add(method)
@@ -64,6 +64,8 @@ def get_webauthn_server():
 @login_required()
 def setup_webauthn_begin():
 	user = get_current_user()
+	methods = WebauthnMethod.query.filter_by(dn=user.dn).all()
+	creds = [method.cred_data.credential_data for method in methods]
 	server = get_webauthn_server()
 	registration_data, state = server.register_begin(
 		{
@@ -72,7 +74,7 @@ def setup_webauthn_begin():
 			"displayName": user.displayname,
 			"icon": "https://example.com/image.png",
 		},
-		[],
+		creds,
 		user_verification=UserVerificationRequirement.DISCOURAGED,
 		authenticator_attachment="cross-platform",
 	)
diff --git a/uffd/template_helper.py b/uffd/template_helper.py
index 0dff9a43eeca4bcf736d0d76cfabb1e1f8bae695..63bad80ede4ddf8236cd6fad6982552cdd13936d 100644
--- a/uffd/template_helper.py
+++ b/uffd/template_helper.py
@@ -14,7 +14,7 @@ def register_template_helper(app):
 
 	@app.template_filter()
 	def qrcode_svg(content, **attrs):
-		img = qrcode.make(content, image_factory=qrcode.image.svg.SvgPathImage)
+		img = qrcode.make(content, image_factory=qrcode.image.svg.SvgPathImage, border=0)
 		svg = img.get_image()
 		for key, value, in attrs.items():
 			svg.set(key, value)