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\Actions\ActivityType;
+use BookStack\Auth\Access\Mfa\MfaValue;
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';
+ use HandlesPartialLogins;
/**
* Show the view to setup MFA for the current user.
*/
public function setup()
{
- // TODO - Redirect back to profile/edit if already setup?
- // Show MFA setup route
- return view('mfa.setup');
+ $userMethods = $this->currentOrLastAttemptedUser()
+ ->mfaValues()
+ ->get(['id', 'method'])
+ ->groupBy('method');
+
+ $this->setPageTitle(trans('auth.mfa_setup'));
+
+ return view('mfa.setup', [
+ 'userMethods' => $userMethods,
+ ]);
}
/**
- * Show a view that generates and displays a TOTP QR code.
- * @throws IncompatibleWithGoogleAuthenticatorException
- * @throws InvalidCharactersException
- * @throws SecretKeyTooShortException
+ * Remove an MFA method for the current user.
+ *
+ * @throws \Exception
*/
- public function totpGenerate()
+ public function remove(string $method)
{
- // 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));
+ if (in_array($method, MfaValue::allMethods())) {
+ $value = user()->mfaValues()->where('method', '=', $method)->first();
+ if ($value) {
+ $value->delete();
+ $this->logActivity(ActivityType::MFA_REMOVE_METHOD, $method);
+ }
}
- $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);
-
- // Get user to verify setup via responding once.
- // If correct response, Save key against user
- return view('mfa.totp-generate', [
- 'secret' => $totpSecret,
- 'svg' => $svg,
- ]);
+ return redirect('/mfa/setup');
}
/**
- * Confirm the setup of TOTP and save the auth method secret
- * against the current user.
- * @throws ValidationException
+ * Show the page to start an MFA verification.
*/
- public function totpConfirm(Request $request)
+ public function verify(Request $request)
{
- $this->validate($request, [
- 'code' => 'required|max:12|min:4'
- ]);
+ $desiredMethod = $request->get('method');
+ $userMethods = $this->currentOrLastAttemptedUser()
+ ->mfaValues()
+ ->get(['id', 'method'])
+ ->groupBy('method');
- // TODO - Confirm code
- dd($request->input('code'));
+ // Basic search for the default option for a user.
+ // (Prioritises totp over backup codes)
+ $method = $userMethods->has($desiredMethod) ? $desiredMethod : $userMethods->keys()->sort()->reverse()->first();
+ $otherMethods = $userMethods->keys()->filter(function ($userMethod) use ($method) {
+ return $method !== $userMethod;
+ })->all();
+
+ return view('mfa.verify', [
+ 'userMethods' => $userMethods,
+ 'method' => $method,
+ 'otherMethods' => $otherMethods,
+ ]);
}
}