AzuraCast/src/Controller/Frontend/Profile/EnableTwoFactorAction.php

81 lines
2.6 KiB
PHP

<?php
namespace App\Controller\Frontend\Profile;
use App\Auth;
use App\Config;
use App\Form\Form;
use App\Http\Response;
use App\Http\ServerRequest;
use App\Session\Flash;
use AzuraForms\Field\AbstractField;
use BaconQrCode;
use Doctrine\ORM\EntityManager;
use OTPHP\TOTP;
use ParagonIE\ConstantTime\Base32;
use Psr\Http\Message\ResponseInterface;
class EnableTwoFactorAction
{
public function __invoke(
ServerRequest $request,
Response $response,
Config $config,
EntityManager $em
): ResponseInterface {
$twoFactorFormConfig = $config->get('forms/profile_two_factor');
$user = $request->getUser();
$form = new Form($twoFactorFormConfig);
$session = $request->getSession();
if ($request->isPost()) {
$secret = $session->get('totp_secret');
} else {
// Generate new TOTP secret.
$secret = substr(trim(Base32::encodeUpper(random_bytes(128)), '='), 0, 64);
$session->set('totp_secret', $secret);
}
// Customize TOTP code
$totp = TOTP::create($secret);
$totp->setLabel($user->getEmail());
$form->getField('otp')->addValidator(function ($otp, AbstractField $element) use ($totp) {
return ($totp->verify($otp, null, Auth::TOTP_WINDOW))
? true
: __('The token you supplied is invalid. Please try again.');
});
if ($request->isPost() && $form->isValid($request->getParsedBody())) {
$user->setTwoFactorSecret($totp->getProvisioningUri());
$em->persist($user);
$em->flush();
$request->getFlash()->addMessage(__('Two-factor authentication enabled.'), Flash::SUCCESS);
return $response->withRedirect($request->getRouter()->named('profile:index'));
}
// Further customize TOTP code (with metadata that won't be stored in the DB)
$totp->setIssuer('AzuraCast');
$totp->setParameter('image', 'https://www.azuracast.com/img/logo.png');
// Generate QR code
$totp_uri = $totp->getProvisioningUri();
$renderer = new BaconQrCode\Renderer\ImageRenderer(
new BaconQrCode\Renderer\RendererStyle\RendererStyle(300),
new BaconQrCode\Renderer\Image\SvgImageBackEnd()
);
$writer = new BaconQrCode\Writer($renderer);
$qr_code = $writer->writeString($totp_uri);
return $request->getView()->renderToResponse($response, 'frontend/profile/enable_two_factor', [
'form' => $form,
'qr_code' => $qr_code,
'totp_uri' => $totp_uri,
]);
}
}