mirror of
https://github.com/BookStackApp/BookStack.git
synced 2024-10-30 07:32:39 +01:00
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:
parent
efb6a6b457
commit
d25cd83d8e
@ -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'));
|
||||
}
|
||||
}
|
||||
|
@ -29,6 +29,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
.input-fill-width {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.fake-input {
|
||||
@extend .input-base;
|
||||
overflow: auto;
|
||||
|
@ -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
|
||||
|
44
resources/views/mfa/totp-generate.blade.php
Normal file
44
resources/views/mfa/totp-generate.blade.php
Normal 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
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user