Skip to content
  • About
    • What is Symfony?
    • Community
    • News
    • Contributing
    • Support
  • Documentation
    • Symfony Docs
    • Symfony Book
    • Screencasts
    • Symfony Bundles
    • Symfony Cloud
    • Training
  • Services
    • Platform.sh for Symfony Best platform to deploy Symfony apps
    • SymfonyInsight Automatic quality checks for your apps
    • Symfony Certification Prove your knowledge and boost your career
    • SensioLabs Professional services to help you with Symfony
    • Blackfire Profile and monitor performance of your apps
  • Other
  • Blog
  • Download
sponsored by
  1. Home
  2. Documentation
  3. Bundles
  4. SchebTwoFactorBundle
  5. Code-via-Email Authentication

Code-via-Email Authentication

Edit this page

A two-factor provider to generate a random numeric code and send it to the user via email.

How authentication works

On successful authentication it generates a random number and persists it in the user entity. The number is sent to the user via email. Then the user must enter that number to gain access.

The number of digits can be configured:

1
2
3
4
# config/packages/scheb_2fa.yaml
scheb_two_factor:
    email:
        digits: 6

Installation

To make use of this feature, you have to install scheb/2fa-email.

1
composer require scheb/2fa-email

The bundle's default implementation for sending emails assumes you're using symfony/mailer. To install the package:

1
composer require symfony/mailer

You're free to use any other mail-sending library you like, but then you have to implement a custom mailer class (instructions below).

Basic Configuration

To enable this authentication method add this to your configuration:

1
2
3
4
5
6
# config/packages/scheb_2fa.yaml
scheb_two_factor:
    email:
        enabled: true
        sender_email: no-reply@example.com
        sender_name: John Doe  # Optional

Your user entity has to implement Scheb\TwoFactorBundle\Model\Email\TwoFactorInterface. The authentication code must be persisted, so make sure that it is stored in a persisted field.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<?php

namespace Acme\Demo\Entity;

use Doctrine\ORM\Mapping as ORM;
use Scheb\TwoFactorBundle\Model\Email\TwoFactorInterface;
use Symfony\Component\Security\Core\User\UserInterface;

class User implements UserInterface, TwoFactorInterface
{
    /**
     * @ORM\Column(type="string")
     */
    private string $email;

    /**
     * @ORM\Column(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;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<?php

namespace Acme\Demo\Entity;

use Doctrine\ORM\Mapping as ORM;
use Scheb\TwoFactorBundle\Model\Email\TwoFactorInterface;
use Symfony\Component\Security\Core\User\UserInterface;

class User implements UserInterface, TwoFactorInterface
{
    #[ORM\Column(type: 'string')]
    private string $email;

    #[ORM\Column(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;
    }
}

Configuration Reference

1
2
3
4
5
6
7
8
9
10
# config/packages/scheb_2fa.yaml
scheb_two_factor:
    email:
        enabled: true                  # If email authentication should be enabled, default false
        mailer: acme.custom_mailer_service  # Use alternative service to send the authentication code
        code_generator: acme.custom_code_generator_service  # Use alternative service to generate authentication code
        sender_email: me@example.com   # Sender email address
        sender_name: John Doe          # Sender name
        digits: 4                      # Number of digits in authentication code
        template: security/2fa_form.html.twig   # Template used to render the authentication form

Custom Mailer

By default the email is plain text and very simple. If you want a different style (e.g. HTML) you have to create your own mailer service. It must implement Scheb\TwoFactorBundle\Mailer\AuthCodeMailerInterface.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php

namespace Acme\Demo\Mailer;

use Scheb\TwoFactorBundle\Model\Email\TwoFactorInterface;
use Scheb\TwoFactorBundle\Mailer\AuthCodeMailerInterface;

class MyAuthCodeMailer implements AuthCodeMailerInterface
{
    // [...]

    public function sendAuthCode(TwoFactorInterface $user): void
    {
        $authCode = $user->getEmailAuthCode();

        // Send email
    }
}

Then register it as a service and update your configuration:

1
2
3
4
# config/packages/scheb_2fa.yaml
scheb_two_factor:
    email:
        mailer: acme.custom_mailer_service

Re-send Authentication Code

When you're using the default authentication code generator that is coming with the bundle, there's an easy way to resend the email with the authentication code. Get/inject service scheb_two_factor.security.email.code_generator and call method reSend(\Scheb\TwoFactorBundle\Model\Email\TwoFactorInterface $user).

Custom Code Generator

If you want to have the code generated differently, you can have your own code generator. Create a service implementing Scheb\TwoFactorBundle\Security\TwoFactor\Provider\Email\Generator\CodeGeneratorInterface and register it in the configuration:

1
2
3
4
# config/packages/scheb_2fa.yaml
scheb_two_factor:
    email:
        code_generator: acme.custom_code_generator_service

Custom Authentication Form Template

The bundle uses Resources/views/Authentication/form.html.twig to render the authentication form. If you want to use a different template you can simply register it in configuration:

1
2
3
4
# config/packages/scheb_2fa.yaml
scheb_two_factor:
    email:
        template: security/2fa_form.html.twig

Custom Form Rendering

There are certain cases when it's not enough to just change the template. For example, you're using two-factor authentication on multiple firewalls and you need to render the form differently for each firewall. In such a case you can implement a form renderer to fully customize the rendering logic.

Create a class implementing Scheb\TwoFactorBundle\Security\TwoFactor\Provider\TwoFactorFormRendererInterface:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php

namespace Acme\Demo\FormRenderer;

use Scheb\TwoFactorBundle\Security\TwoFactor\Provider\TwoFactorFormRendererInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

class MyFormRenderer implements TwoFactorFormRendererInterface
{
    // [...]

    public function renderForm(Request $request, array $templateVars): Response
    {
        // Customize form rendering
    }
}

Then register it as a service and update your configuration:

1
2
3
4
# config/packages/scheb_2fa.yaml
scheb_two_factor:
    email:
        form_renderer: acme.custom_form_renderer_service
This work, including the code samples, is licensed under a Creative Commons BY-SA 3.0 license.
TOC
    Version
    Peruse our complete Symfony & PHP solutions catalog for your web development needs.

    Peruse our complete Symfony & PHP solutions catalog for your web development needs.

    Be safe against critical risks to your projects and businesses

    Be safe against critical risks to your projects and businesses

    Version:

    Table of Contents

    • How authentication works
    • Installation
    • Basic Configuration
    • Configuration Reference
    • Custom Mailer
    • Re-send Authentication Code
    • Custom Code Generator
    • Custom Authentication Form Template
    • Custom Form Rendering

    Symfony footer

    Avatar of Neil Peyssard, a Symfony contributor

    Thanks Neil Peyssard (@nepey) for being a Symfony contributor

    4 commits • 498 lines changed

    View all contributors that help us make Symfony

    Become a Symfony contributor

    Be an active part of the community and contribute ideas, code and bug fixes. Both experts and newcomers are welcome.

    Learn how to contribute

    Symfony™ is a trademark of Symfony SAS. All rights reserved.

    • What is Symfony?

      • What is Symfony?
      • Symfony at a Glance
      • Symfony Components
      • Symfony Releases
      • Security Policy
      • Logo & Screenshots
      • Trademark & Licenses
      • symfony1 Legacy
    • Learn Symfony

      • Symfony Docs
      • Symfony Book
      • Reference
      • Bundles
      • Best Practices
      • Training
      • eLearning Platform
      • Certification
    • Screencasts

      • Learn Symfony
      • Learn PHP
      • Learn JavaScript
      • Learn Drupal
      • Learn RESTful APIs
    • Community

      • Symfony Community
      • SymfonyConnect
      • Events & Meetups
      • Projects using Symfony
      • Contributors
      • Symfony Jobs
      • Backers
      • Code of Conduct
      • Downloads Stats
      • Support
    • Blog

      • All Blog Posts
      • A Week of Symfony
      • Case Studies
      • Cloud
      • Community
      • Conferences
      • Diversity
      • Living on the edge
      • Releases
      • Security Advisories
      • Symfony Insight
      • Twig
      • SensioLabs Blog
    • Services

      • SensioLabs services
      • Train developers
      • Manage your project quality
      • Improve your project performance
      • Host Symfony projects

      Powered by

    Follow Symfony