diff --git a/app/Auth/Access/Mfa/MfaSession.php b/app/Auth/Access/Mfa/MfaSession.php
new file mode 100644
index 000000000..67574cbaf
--- /dev/null
+++ b/app/Auth/Access/Mfa/MfaSession.php
@@ -0,0 +1,44 @@
+<?php
+
+namespace BookStack\Auth\Access\Mfa;
+
+class MfaSession
+{
+    private const MFA_VERIFIED_SESSION_KEY = 'mfa-verification-passed';
+
+    /**
+     * Check if MFA is required for the current user.
+     */
+    public function requiredForCurrentUser(): bool
+    {
+        // TODO - Test both these cases
+        return user()->mfaValues()->exists() || $this->currentUserRoleEnforcesMfa();
+    }
+
+    /**
+     * Check if a role of the current user enforces MFA.
+     */
+    protected function currentUserRoleEnforcesMfa(): bool
+    {
+        return user()->roles()
+            ->where('mfa_enforced', '=', true)
+            ->exists();
+    }
+
+    /**
+     * Check if the current MFA session has already been verified.
+     */
+    public function isVerified(): bool
+    {
+        return session()->get(self::MFA_VERIFIED_SESSION_KEY) === 'true';
+    }
+
+    /**
+     * Mark the current session as MFA-verified.
+     */
+    public function markVerified(): void
+    {
+        session()->put(self::MFA_VERIFIED_SESSION_KEY, 'true');
+    }
+
+}
\ No newline at end of file
diff --git a/app/Http/Controllers/Auth/MfaController.php b/app/Http/Controllers/Auth/MfaController.php
index 9feda9433..39a4e852f 100644
--- a/app/Http/Controllers/Auth/MfaController.php
+++ b/app/Http/Controllers/Auth/MfaController.php
@@ -37,4 +37,18 @@ class MfaController extends Controller
 
         return redirect('/mfa/setup');
     }
+
+    /**
+     * Show the page to start an MFA verification.
+     */
+    public function verify()
+    {
+        $userMethods = user()->mfaValues()
+            ->get(['id', 'method'])
+            ->groupBy('method');
+
+        return view('mfa.verify', [
+            'userMethods' => $userMethods,
+        ]);
+    }
 }
diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php
index 4f9bfc1e6..c9e59ed3e 100644
--- a/app/Http/Kernel.php
+++ b/app/Http/Kernel.php
@@ -48,6 +48,7 @@ class Kernel extends HttpKernel
      */
     protected $routeMiddleware = [
         'auth'       => \BookStack\Http\Middleware\Authenticate::class,
+        'mfa'        => \BookStack\Http\Middleware\EnforceMfaRequirements::class,
         'can'        => \Illuminate\Auth\Middleware\Authorize::class,
         'guest'      => \BookStack\Http\Middleware\RedirectIfAuthenticated::class,
         'throttle'   => \Illuminate\Routing\Middleware\ThrottleRequests::class,
diff --git a/app/Http/Middleware/EnforceMfaRequirements.php b/app/Http/Middleware/EnforceMfaRequirements.php
index 957b42ae1..ac3c9609b 100644
--- a/app/Http/Middleware/EnforceMfaRequirements.php
+++ b/app/Http/Middleware/EnforceMfaRequirements.php
@@ -2,10 +2,21 @@
 
 namespace BookStack\Http\Middleware;
 
+use BookStack\Auth\Access\Mfa\MfaSession;
 use Closure;
 
 class EnforceMfaRequirements
 {
+    protected $mfaSession;
+
+    /**
+     * EnforceMfaRequirements constructor.
+     */
+    public function __construct(MfaSession $mfaSession)
+    {
+        $this->mfaSession = $mfaSession;
+    }
+
     /**
      * Handle an incoming request.
      *
@@ -15,10 +26,23 @@ class EnforceMfaRequirements
      */
     public function handle($request, Closure $next)
     {
-        $mfaRequired = user()->roles()->where('mfa_enforced', '=', true)->exists();
-        // TODO - Run this after auth (If authenticated)
-        // TODO - Redirect user to setup MFA or verify via MFA.
+        if (
+            !$this->mfaSession->isVerified()
+            && !$request->is('mfa/verify*', 'uploads/images/user/*')
+            && $this->mfaSession->requiredForCurrentUser()
+        ) {
+            return redirect('/mfa/verify');
+        }
+
+        // TODO - URI wildcard exceptions above allow access to the 404 page of this user
+        //  which could then expose content. Either need to lock that down (Tricky to do image thing)
+        //  or prevent any level of auth until verified.
+
+        // TODO - Need to redirect to setup if not configured AND ONLY IF NO OPTIONS CONFIGURED
+        //    Might need to change up such routes to start with /configure/ for such identification.
+        //    (Can't allow access to those if already configured)
         // TODO - Store mfa_pass into session for future requests?
+
         return $next($request);
     }
 }
diff --git a/resources/views/mfa/verify.blade.php b/resources/views/mfa/verify.blade.php
new file mode 100644
index 000000000..4ff0e6c49
--- /dev/null
+++ b/resources/views/mfa/verify.blade.php
@@ -0,0 +1,31 @@
+@extends('simple-layout')
+
+@section('body')
+    <div class="container small py-xl">
+
+        <div class="card content-wrap auto-height">
+            <h1 class="list-heading">Verify Access</h1>
+            <p class="mb-none">
+                Your user account requires you to confirm your identity via an additional level
+                of verification before you're granted access.
+                Verify using one of your configure methods to continue.
+            </p>
+
+            <div class="setting-list">
+                <div class="grid half gap-xl">
+                    <div>
+                        <div class="setting-list-label">METHOD A</div>
+                        <p class="small">
+                            ...
+                        </p>
+                    </div>
+                    <div class="pt-m">
+                            <a href="{{ url('/mfa/verify/totp') }}" class="button outline">BUTTON</a>
+                    </div>
+                </div>
+
+            </div>
+
+        </div>
+    </div>
+@stop
diff --git a/routes/web.php b/routes/web.php
index e3329edc4..406cfd767 100644
--- a/routes/web.php
+++ b/routes/web.php
@@ -4,7 +4,7 @@ Route::get('/status', 'StatusController@show');
 Route::get('/robots.txt', 'HomeController@getRobots');
 
 // Authenticated routes...
-Route::group(['middleware' => 'auth'], function () {
+Route::group(['middleware' => ['auth', 'mfa']], function () {
 
     // Secure images routing
     Route::get('/uploads/images/{path}', 'Images\ImageController@showImage')
@@ -224,13 +224,14 @@ Route::group(['middleware' => 'auth'], function () {
         Route::put('/roles/{id}', 'RoleController@update');
     });
 
-    // MFA Setup Routes
+    // MFA Routes
     Route::get('/mfa/setup', 'Auth\MfaController@setup');
     Route::get('/mfa/totp-generate', 'Auth\MfaTotpController@generate');
     Route::post('/mfa/totp-confirm', 'Auth\MfaTotpController@confirm');
     Route::get('/mfa/backup-codes-generate', 'Auth\MfaBackupCodesController@generate');
     Route::post('/mfa/backup-codes-confirm', 'Auth\MfaBackupCodesController@confirm');
     Route::delete('/mfa/remove/{method}', 'Auth\MfaController@remove');
+    Route::get('/mfa/verify', 'Auth\MfaController@verify');
 });
 
 // Social auth routes