diff --git a/app/Auth/Access/Mfa/BackupCodeService.php b/app/Auth/Access/Mfa/BackupCodeService.php new file mode 100644 index 000000000..cc533bd31 --- /dev/null +++ b/app/Auth/Access/Mfa/BackupCodeService.php @@ -0,0 +1,21 @@ +generateNewSet(); + session()->put(self::SETUP_SECRET_SESSION_KEY, encrypt($codes)); + + $downloadUrl = 'data:application/octet-stream;base64,' . base64_encode(implode("\n\n", $codes)); + + return view('mfa.backup-codes-generate', [ + 'codes' => $codes, + 'downloadUrl' => $downloadUrl, + ]); + } + + /** + * Confirm the setup of backup codes, storing them against the user. + * @throws Exception + */ + public function confirm() + { + if (!session()->has(self::SETUP_SECRET_SESSION_KEY)) { + return response('No generated codes found in the session', 500); + } + + $codes = decrypt(session()->pull(self::SETUP_SECRET_SESSION_KEY)); + MfaValue::upsertWithValue(user(), MfaValue::METHOD_BACKUP_CODES, json_encode($codes)); + + $this->logActivity(ActivityType::MFA_SETUP_METHOD, 'backup-codes'); + return redirect('/mfa/setup'); + } +} diff --git a/app/Http/Controllers/Auth/MfaController.php b/app/Http/Controllers/Auth/MfaController.php index 8ddccaa98..caee416d3 100644 --- a/app/Http/Controllers/Auth/MfaController.php +++ b/app/Http/Controllers/Auth/MfaController.php @@ -2,18 +2,10 @@ namespace BookStack\Http\Controllers\Auth; -use BookStack\Actions\ActivityType; -use BookStack\Auth\Access\Mfa\MfaValue; -use BookStack\Auth\Access\Mfa\TotpService; -use BookStack\Auth\Access\Mfa\TotpValidationRule; use BookStack\Http\Controllers\Controller; -use Illuminate\Http\Request; -use Illuminate\Validation\ValidationException; 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. */ @@ -26,47 +18,4 @@ class MfaController extends Controller 'userMethods' => $userMethods, ]); } - - /** - * Show a view that generates and displays a TOTP QR code. - */ - public function totpGenerate(TotpService $totp) - { - if (session()->has(static::TOTP_SETUP_SECRET_SESSION_KEY)) { - $totpSecret = decrypt(session()->get(static::TOTP_SETUP_SECRET_SESSION_KEY)); - } else { - $totpSecret = $totp->generateSecret(); - session()->put(static::TOTP_SETUP_SECRET_SESSION_KEY, encrypt($totpSecret)); - } - - $qrCodeUrl = $totp->generateUrl($totpSecret); - $svg = $totp->generateQrCodeSvg($qrCodeUrl); - - 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) - { - $totpSecret = decrypt(session()->get(static::TOTP_SETUP_SECRET_SESSION_KEY)); - $this->validate($request, [ - 'code' => [ - 'required', - 'max:12', 'min:4', - new TotpValidationRule($totpSecret), - ] - ]); - - MfaValue::upsertWithValue(user(), MfaValue::METHOD_TOTP, $totpSecret); - $this->logActivity(ActivityType::MFA_SETUP_METHOD, 'totp'); - - return redirect('/mfa/setup'); - } } diff --git a/app/Http/Controllers/Auth/MfaTotpController.php b/app/Http/Controllers/Auth/MfaTotpController.php new file mode 100644 index 000000000..18f08e709 --- /dev/null +++ b/app/Http/Controllers/Auth/MfaTotpController.php @@ -0,0 +1,60 @@ +has(static::SETUP_SECRET_SESSION_KEY)) { + $totpSecret = decrypt(session()->get(static::SETUP_SECRET_SESSION_KEY)); + } else { + $totpSecret = $totp->generateSecret(); + session()->put(static::SETUP_SECRET_SESSION_KEY, encrypt($totpSecret)); + } + + $qrCodeUrl = $totp->generateUrl($totpSecret); + $svg = $totp->generateQrCodeSvg($qrCodeUrl); + + 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 confirm(Request $request) + { + $totpSecret = decrypt(session()->get(static::SETUP_SECRET_SESSION_KEY)); + $this->validate($request, [ + 'code' => [ + 'required', + 'max:12', 'min:4', + new TotpValidationRule($totpSecret), + ] + ]); + + MfaValue::upsertWithValue(user(), MfaValue::METHOD_TOTP, $totpSecret); + session()->remove(static::SETUP_SECRET_SESSION_KEY); + $this->logActivity(ActivityType::MFA_SETUP_METHOD, 'totp'); + + return redirect('/mfa/setup'); + } +} diff --git a/resources/views/mfa/backup-codes-generate.blade.php b/resources/views/mfa/backup-codes-generate.blade.php new file mode 100644 index 000000000..8b437846e --- /dev/null +++ b/resources/views/mfa/backup-codes-generate.blade.php @@ -0,0 +1,40 @@ +@extends('simple-layout') + +@section('body') + +
+ Store the below list of codes in a safe place. + When accessing the system you'll be able to use one of the codes + as a second authentication mechanism. +
+ ++ Download Codes +
+ ++ Each code can only be used once +
+ + +- Print out or securely store a set of one-time backup codes + Securely store a set of one-time-use backup codes which you can enter to verify your identity.