diff --git a/app/Http/Controllers/Auth/MfaController.php b/app/Http/Controllers/Auth/MfaController.php
index 1d0dbd1a4..be381e3b0 100644
--- a/app/Http/Controllers/Auth/MfaController.php
+++ b/app/Http/Controllers/Auth/MfaController.php
@@ -2,11 +2,24 @@
 
 namespace BookStack\Http\Controllers\Auth;
 
+use BaconQrCode\Renderer\Color\Rgb;
+use BaconQrCode\Renderer\Image\SvgImageBackEnd;
+use BaconQrCode\Renderer\ImageRenderer;
+use BaconQrCode\Renderer\RendererStyle\Fill;
+use BaconQrCode\Renderer\RendererStyle\RendererStyle;
+use BaconQrCode\Writer;
 use BookStack\Http\Controllers\Controller;
 use Illuminate\Http\Request;
+use Illuminate\Validation\ValidationException;
+use PragmaRX\Google2FA\Exceptions\IncompatibleWithGoogleAuthenticatorException;
+use PragmaRX\Google2FA\Exceptions\InvalidCharactersException;
+use PragmaRX\Google2FA\Exceptions\SecretKeyTooShortException;
+use PragmaRX\Google2FA\Google2FA;
 
 class MfaController extends Controller
 {
+    protected const TOTP_SETUP_SECRET_SESSION_KEY = 'mfa-setup-totp-secret';
+
     /**
      * Show the view to setup MFA for the current user.
      */
@@ -17,13 +30,57 @@ class MfaController extends Controller
         return view('mfa.setup');
     }
 
-    public function generateQr()
+    /**
+     * Show a view that generates and displays a TOTP QR code.
+     * @throws IncompatibleWithGoogleAuthenticatorException
+     * @throws InvalidCharactersException
+     * @throws SecretKeyTooShortException
+     */
+    public function totpGenerate()
     {
-        // https://github.com/antonioribeiro/google2fa#how-to-generate-and-use-two-factor-authentication
+        // TODO - Ensure a QR code doesn't already exist? Or overwrite?
+        $google2fa = new Google2FA();
+        if (session()->has(static::TOTP_SETUP_SECRET_SESSION_KEY)) {
+            $totpSecret = decrypt(session()->get(static::TOTP_SETUP_SECRET_SESSION_KEY));
+        } else {
+            $totpSecret = $google2fa->generateSecretKey();
+            session()->put(static::TOTP_SETUP_SECRET_SESSION_KEY, encrypt($totpSecret));
+        }
+
+        $qrCodeUrl = $google2fa->getQRCodeUrl(
+            setting('app-name'),
+            user()->email,
+            $totpSecret
+        );
+
+        $color = Fill::uniformColor(new Rgb(255, 255, 255), new Rgb(32, 110, 167));
+        $svg = (new Writer(
+            new ImageRenderer(
+                new RendererStyle(192, 0, null, null, $color),
+                new SvgImageBackEnd
+            )
+        ))->writeString($qrCodeUrl);
 
-        // Generate secret key
-        // Store key in session?
         // Get user to verify setup via responding once.
         // If correct response, Save key against user
+        return view('mfa.totp-generate', [
+            'secret' => $totpSecret,
+            'svg' => $svg,
+        ]);
+    }
+
+    /**
+     * Confirm the setup of TOTP and save the auth method secret
+     * against the current user.
+     * @throws ValidationException
+     */
+    public function totpConfirm(Request $request)
+    {
+        $this->validate($request, [
+            'code' => 'required|max:12|min:4'
+        ]);
+
+        // TODO - Confirm code
+        dd($request->input('code'));
     }
 }
diff --git a/resources/sass/_forms.scss b/resources/sass/_forms.scss
index 953d1d060..bb6d17f82 100644
--- a/resources/sass/_forms.scss
+++ b/resources/sass/_forms.scss
@@ -29,6 +29,10 @@
   }
 }
 
+.input-fill-width {
+  width: 100% !important;
+}
+
 .fake-input {
   @extend .input-base;
   overflow: auto;
diff --git a/resources/views/mfa/setup.blade.php b/resources/views/mfa/setup.blade.php
index 25eb5d925..577841af5 100644
--- a/resources/views/mfa/setup.blade.php
+++ b/resources/views/mfa/setup.blade.php
@@ -5,12 +5,39 @@
 
         <div class="card content-wrap auto-height">
             <h1 class="list-heading">Setup Multi-Factor Authentication</h1>
-            <p>
+            <p class="mb-none">
                 Setup multi-factor authentication as an extra layer of security
                 for your user account.
-                To use multi-factor authentication you'll need a mobile application
-                that supports TOTP such as Google Authenticator, Authy or Microsoft Authenticator.
             </p>
+
+            <div class="setting-list">
+                <div class="grid half gap-xl">
+                    <div>
+                        <div class="setting-list-label">Mobile App</div>
+                        <p class="small">
+                            To use multi-factor authentication you'll need a mobile application
+                            that supports TOTP such as Google Authenticator, Authy or Microsoft Authenticator.
+                        </p>
+                    </div>
+                    <div class="pt-m">
+                        <a href="{{ url('/mfa/totp-generate') }}" class="button outline">Setup</a>
+                    </div>
+                </div>
+
+                <div class="grid half gap-xl">
+                    <div>
+                        <div class="setting-list-label">Backup Codes</div>
+                        <p class="small">
+                            Print out or securely store a set of one-time backup codes
+                            which you can enter to verify your identity.
+                        </p>
+                    </div>
+                    <div class="pt-m">
+                        <a href="{{ url('/mfa/codes/generate') }}" class="button outline">Setup</a>
+                    </div>
+                </div>
+            </div>
+
         </div>
     </div>
 @stop
diff --git a/resources/views/mfa/totp-generate.blade.php b/resources/views/mfa/totp-generate.blade.php
new file mode 100644
index 000000000..17d38adaa
--- /dev/null
+++ b/resources/views/mfa/totp-generate.blade.php
@@ -0,0 +1,44 @@
+@extends('simple-layout')
+
+@section('body')
+
+    <div class="container very-small py-xl">
+        <div class="card content-wrap auto-height">
+            <h1 class="list-heading">Mobile App Setup</h1>
+            <p>
+                To use multi-factor authentication you'll need a mobile application
+                that supports TOTP such as Google Authenticator, Authy or Microsoft Authenticator.
+            </p>
+            <p>
+                Scan the QR code below using your preferred authentication app to get started.
+            </p>
+
+            <div class="text-center">
+                <div class="block inline">
+                    {!! $svg !!}
+                </div>
+            </div>
+
+            <h2 class="list-heading">Verify Setup</h2>
+            <p id="totp-verify-input-details" class="mb-s">
+                Verify that all is working by entering a code, generated within your
+                authentication app, in the input box below:
+            </p>
+            <form action="{{ url('/mfa/totp-confirm') }}" method="POST">
+                {{ csrf_field() }}
+                <input type="text"
+                       name="code"
+                       aria-labelledby="totp-verify-input-details"
+                       placeholder="Provide your app generated code here"
+                       class="input-fill-width {{ $errors->has('code') ? 'neg' : '' }}">
+                @if($errors->has('code'))
+                    <div class="text-neg text-small px-xs">{{ $errors->first('code') }}</div>
+                @endif
+                <div class="mt-s text-right">
+                    <button class="button">Confirm and Enable</button>
+                </div>
+            </form>
+        </div>
+    </div>
+
+@stop
diff --git a/routes/web.php b/routes/web.php
index 7807d5477..f9967465b 100644
--- a/routes/web.php
+++ b/routes/web.php
@@ -225,6 +225,8 @@ Route::group(['middleware' => 'auth'], function () {
     });
 
     Route::get('/mfa/setup', 'Auth\MfaController@setup');
+    Route::get('/mfa/totp-generate', 'Auth\MfaController@totpGenerate');
+    Route::post('/mfa/totp-confirm', 'Auth\MfaController@totpConfirm');
 });
 
 // Social auth routes