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.
*/
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'));
}
}
<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
--- /dev/null
+@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