diff --git a/resources/assets/javascripts/bootstrap/qr_code.js b/resources/assets/javascripts/bootstrap/qr_code.js
index 3a627f7e2906e2a16e269a555490a814ce173274..9ed0b211e159d0613991aaa76c4b1510d1522dba 100644
--- a/resources/assets/javascripts/bootstrap/qr_code.js
+++ b/resources/assets/javascripts/bootstrap/qr_code.js
@@ -8,3 +8,19 @@ $(document).on('click', 'a[data-qr-code]', function (event) {
 
     event.preventDefault();
 });
+
+STUDIP.ready(event => {
+    $('code.qr', event.target).each(function () {
+        const text = $(this).text().trim();
+        if ($(this).hasClass('hide-text')) {
+            $(this).text('');
+        }
+
+        $(this).qrcode({
+            text: text,
+            width: 1280,
+            height: 1280,
+            correctLevel: 3
+        }).addClass('has-qr-code');
+    });
+});
diff --git a/resources/assets/stylesheets/scss/tfa.scss b/resources/assets/stylesheets/scss/tfa.scss
index e6b939000c4f5a332bacdf836ef98824969fa779..178eb7aaf078d99e25fa65d53b0dc966076a6504 100644
--- a/resources/assets/stylesheets/scss/tfa.scss
+++ b/resources/assets/stylesheets/scss/tfa.scss
@@ -1,11 +1,18 @@
 .tfa-app-code {
     code.qr {
-        display: none;
-    }
-    .qrcode img {
+        display: block;
         margin: auto;
-        width: 40%;
         max-width: 50vw;
+        width: 40%;
+
+        canvas {
+            max-width: 100%;
+            height: auto;
+        }
+
+        &:not(.has-qr-code) {
+            display: none;
+        }
     }
 }
 
diff --git a/templates/tfa-validate.php b/templates/tfa-validate.php
index 62a5eb3f25e90738f7c8af12ded3c014a36b7ab1..f5221bc705f70390b5573e662b3e5ebe84099cb8 100644
--- a/templates/tfa-validate.php
+++ b/templates/tfa-validate.php
@@ -17,7 +17,7 @@
                 . 'anschliessend ein gültiges Token ein.') ?>
         </p>
         <div class="tfa-app-code">
-            <code class="qr"><?= $secret->getProvisioningUri() ?></code>
+            <code class="qr hide-text"><?= $secret->getProvisioningUri() ?></code>
         </div>
     <? elseif ($secret->type === 'app' && $secret->confirmed): ?>
         <p>