Useful for some non-scanner type apps.
Closes #2908
$svg = $totp->generateQrCodeSvg($qrCodeUrl);
return view('mfa.totp-generate', [
$svg = $totp->generateQrCodeSvg($qrCodeUrl);
return view('mfa.totp-generate', [
- 'secret' => $totpSecret,
- 'svg' => $svg,
+ 'url' => $qrCodeUrl,
+ 'svg' => $svg,
.flex {
min-height: 0;
flex: 1;
.flex {
min-height: 0;
flex: 1;
&.fit-content {
flex-basis: auto;
flex-grow: 0;
&.fit-content {
flex-basis: auto;
flex-grow: 0;
<div class="block inline">
{!! $svg !!}
</div>
<div class="block inline">
{!! $svg !!}
</div>
+ <div class="code-base small text-muted px-s py-xs my-xs" style="overflow-x: scroll; white-space: nowrap;">
+ {{ $url }}
+ </div>
</div>
<h2 class="list-heading">{{ trans('auth.mfa_gen_totp_verify_setup') }}</h2>
</div>
<h2 class="list-heading">{{ trans('auth.mfa_gen_totp_verify_setup') }}</h2>
$resp->assertSee('The provided code is not valid or has expired.');
$revisitSvg = $resp->getElementHtml('#main-content .card svg');
$this->assertTrue($svg === $revisitSvg);
$resp->assertSee('The provided code is not valid or has expired.');
$revisitSvg = $resp->getElementHtml('#main-content .card svg');
$this->assertTrue($svg === $revisitSvg);
+ $secret = decrypt(session()->get('mfa-setup-totp-secret'));
+
+ $resp->assertSee(htmlentities("?secret={$secret}&issuer=BookStack&algorithm=SHA1&digits=6&period=30"));
// Successful confirmation
$google2fa = new Google2FA();
// Successful confirmation
$google2fa = new Google2FA();
- $secret = decrypt(session()->get('mfa-setup-totp-secret'));
$otp = $google2fa->getCurrentOtp($secret);
$resp = $this->post('/mfa/totp/confirm', [
'code' => $otp,
$otp = $google2fa->getCurrentOtp($secret);
$resp = $this->post('/mfa/totp/confirm', [
'code' => $otp,