In today's digital landscape, ensuring the security of your admin panel is paramount. One effective method to enhance security is by implementing Two-Factor Authentication (2FA). This extra layer of security significantly reduces the risk of unauthorized access, even if login credentials are compromised.
Follow the steps below to implement Two-Factor Authentication in Sylius Admin using the email authentication method. I'll tell you how to add new methods at the end of this article.
Step 1: Adding the Scheb Symfony packages
All the 2FA logic comes with the fantastic Symfony packages from Scheb. We need to add them to the project:
$ composer require scheb/2fa-bundle scheb/2fa-email scheb/2fa-trusted-device
If you are not using Symfony Flex, be sure to add this line to the config/bundles.php
:
<?php
return [
// ...
Scheb\TwoFactorBundle\SchebTwoFactorBundle::class => ['all' => true],
];
Step 2: Configure the Scheb Bundle
You can adjust this configuration based on your needs.
# config/packages/scheb_2fa.yaml
scheb_two_factor:
trusted_device:
enabled: true
lifetime: 2592000
email:
enabled: true
digits: 6
mailer: App\Mailer\TwoFactorAuthCodeEmailManager
template: admin/security/2fa_form.html.twig
security_tokens:
- Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken
- Symfony\Component\Security\Http\Authenticator\Token\PostAuthenticationToken
Now, add the following lines to the config/packages/security.yaml
file:
# config/packages/security.yaml
security:
# ...
firewalls:
admin:
# ...
two_factor:
auth_form_path: app_admin_2fa_login
check_path: app_admin_2fa_login_check
access_control:
# ...
- { path: "%sylius.security.admin_regex%/2fa", role: IS_AUTHENTICATED_2FA_IN_PROGRESS }
- { path: "%sylius.security.admin_regex%", role: ROLE_ADMINISTRATION_ACCESS }
# ...
Finally, replace the content of the config/routes/scheb_2fa.yaml
file with this:
# config/routes/scheb_2fa.yaml
app_admin_2fa_login:
path: '/%sylius_admin.path_name%/2fa'
defaults:
_controller: "scheb_two_factor.form_controller::form"
app_admin_2fa_login_check:
path: '/%sylius_admin.path_name%/2fa/check'
Step 3: Configuring Sylius Mailer to send the email with the Auth Code
# config/packages/_sylius.yaml
# ...
sylius_mailer:
sender:
name: Odiseo
address: team@odiseo.com.ar
emails:
two_factor_auth_code:
subject: app.emails.two_factor_auth_code.subject
template: "common/email/2fa_auth_code.html.twig"
Step 4: Creating the email manager
Now we need to create the service that uses the Sylius Mailer to send the auth code by email. Create this class in src/Mailer/TwoFactorAuthCodeEmailManager.php
:
<?php
declare(strict_types=1);
namespace App\Mailer;
use Scheb\TwoFactorBundle\Mailer\AuthCodeMailerInterface;
use Scheb\TwoFactorBundle\Model\Email\TwoFactorInterface;
use Sylius\Component\Channel\Context\ChannelContextInterface;
use Sylius\Component\Core\Model\ChannelInterface;
use Sylius\Component\Locale\Context\LocaleContextInterface;
use Sylius\Component\Mailer\Sender\SenderInterface;
final class TwoFactorAuthCodeEmailManager implements AuthCodeMailerInterface
{
private const TWO_FACTOR_AUTH_CODE = 'two_factor_auth_code';
public function __construct(
private SenderInterface $emailSender,
private ChannelContextInterface $channelContext,
private LocaleContextInterface $localeContext
) {
}
public function sendAuthCode(TwoFactorInterface $user): void
{
/** @var ChannelInterface $channel */
$channel = $this->channelContext->getChannel();
$localeCode = $this->localeContext->getLocaleCode();
$this->emailSender->send(
self::TWO_FACTOR_AUTH_CODE,
[$user->getEmailAuthRecipient()],
[
'authCode' => $user->getEmailAuthCode(),
'channel' => $channel,
'localeCode' => $localeCode,
],
);
}
}
Step 5: Creating the form and email templates
Create the twig template to render the two factor auth form in templates/admin/security/2fa_form.html.twig
:
{% extends '@SyliusUi/Layout/centered.html.twig' %}
{% import '@SyliusUi/Macro/messages.html.twig' as messages %}
{% block title %}{{ parent() }} | {{ 'app.ui.two_factor_auth'|trans }}{% endblock %}
{% block stylesheets %}
{{ sylius_template_event('sylius.admin.layout.stylesheets') }}
{% endblock %}
{% block content %}
<div class="ui middle aligned center aligned grid">
<div class="column">
<div style="max-width: 270px; margin: 0 auto; margin-bottom: 40px;">
<img src="{{ asset('build/admin/images/logo.png', 'admin') }}" class="ui fluid image" alt="Logo">
</div>
{% if authenticationError %}
<div class="ui left aligned basic segment">
{{ messages.error(authenticationError|trans(authenticationErrorData, 'SchebTwoFactorBundle')) }}
</div>
{% endif %}
<form class="ui large loadable form" action="{{ checkPathUrl ? checkPathUrl: path(checkPathRoute) }}" method="post" novalidate>
<div class="ui left aligned very padded segment">
<div class="required field">
<label for="_auth_code">{{ "auth_code"|trans({}, 'SchebTwoFactorBundle') }} {{ twoFactorProvider }}:</label>
<input
id="_auth_code"
type="text"
name="{{ authCodeParameterName }}"
autocomplete="one-time-code"
autofocus
/>
</div>
{% if displayTrustedOption %}
<div class="field">
<div class="ui toggle checkbox">
<input id="_trusted" type="checkbox" name="{{ trustedParameterName }}" />
<label for="_trusted">{{ "trusted"|trans({}, 'SchebTwoFactorBundle') }}</label>
</div>
</div>
{% endif %}
{% if isCsrfProtectionEnabled %}
<input type="hidden" name="{{ csrfParameterName }}" value="{{ csrf_token(csrfTokenId) }}">
{% endif %}
<button type="submit" class="ui fluid large primary submit button">{{ "login"|trans({}, 'SchebTwoFactorBundle') }}</button>
<div class="cancel" style="text-align: center; margin-top: 1em;">
<a href="{{ logoutPath }}">{{ "cancel"|trans({}, 'SchebTwoFactorBundle') }}</a>
</div>
</div>
</form>
</div>
</div>
{% endblock %}
{% block javascripts %}
{{ sylius_template_event('sylius.admin.layout.javascripts') }}
{% endblock %}
Now the twig email template in templates/common/email/2fa_auth_code.html.twig
:
{% extends '@SyliusCore/Email/layout.html.twig' %}
{% block subject %}
{{ 'app.email.two_factor_auth_code.subject'|trans({}, null, localeCode) }}
{% endblock %}
{% block content %}
<div style="text-align: center; margin-bottom: 30px;">
{{ 'app.email.two_factor_auth_code.your_code'|trans({}, null, localeCode) }}:
<div style="margin: 20px 0;">
<span style="border: 1px solid #1abb9c; padding: 10px; color: #1abb9c; font-size: 28px;">
{{ authCode }}
</span>
</div>
</div>
{% endblock %}
And don't forget to add the necessary translation keys:
# translations/messages.en.yaml
app:
email:
two_factor_auth_code:
subject: Two factor auth code
your_code: To complete the sign in, enter the following verification code
Step 6: Adding the auth code field to the Sylius Admin User entity
// src/Entity/User/AdminUser.php
<?php
declare(strict_types=1);
namespace App\Entity\User;
use Doctrine\ORM\Mapping as ORM;
use Scheb\TwoFactorBundle\Model\Email\TwoFactorInterface;
use Sylius\Component\Core\Model\AdminUser as BaseAdminUser;
#[ORM\Entity]
#[ORM\Table(name: 'sylius_admin_user')]
class AdminUser extends BaseAdminUser implements TwoFactorInterface
{
#[ORM\Column(name: 'auth_code', type: 'string', nullable: true)]
private ?string $authCode;
public function isEmailAuthEnabled(): bool
{
return true; // This can be a persisted field to switch email code authentication on/off
}
public function getEmailAuthRecipient(): string
{
return $this->email;
}
public function getEmailAuthCode(): string
{
if (null === $this->authCode) {
throw new \LogicException('The email authentication code was not set');
}
return $this->authCode;
}
public function setEmailAuthCode(string $authCode): void
{
$this->authCode = $authCode;
}
}
Remember to create and execute the migrations:
$ php bin/console doctrine:migrations:diff
$ php bin/console doctrine:migrations:migrate
Now you can enter to the Sylius Admin login page and test the 2FA integration!
Further reading
- You can customize the integration based on your project's needs. For example you can edit the twig templates adding more relevant information or the design.
- You can add more authentication methods such as SMS, Google Authenticator and more following the well documented Scheb bundle: https://symfony.com/bundles/SchebTwoFactorBundle/6.x/index.html.
- You can ask for help or another Symfony / Sylius request sending us an email here: team@odiseo.com.ar or using our contact page.
- Stay tuned for more Sylius recipes to enhance your e-commerce platform's functionality following us on LinkedIn.