Added TOTP generation view and started verification stage

Also updated MFA setup view to have settings-like listed interface to
make it possible to extend with extra options in the future.
This commit is contained in:
Dan Brown 2021-06-29 22:06:49 +01:00
parent efb6a6b457
commit d25cd83d8e
No known key found for this signature in database
GPG Key ID: 46D9F943C24A2EF9
5 changed files with 141 additions and 7 deletions

View File

@ -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'));
}
}

View File

@ -29,6 +29,10 @@
}
}
.input-fill-width {
width: 100% !important;
}
.fake-input {
@extend .input-base;
overflow: auto;

View File

@ -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

View File

@ -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

View File

@ -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