hi1/contao-2fa-email-bundle

Adds email-based two-factor authentication for Contao backend users

Maintainers

Package info

github.com/hi1-yildiz/contao-2fa-email-bundle

Homepage

Type:contao-bundle

pkg:composer/hi1/contao-2fa-email-bundle

Transparency log

Statistics

Installs: 48

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

0.1.16 2026-08-24 12:12 UTC

README

License: LGPL v3 Contao PHP Packagist

Email-based two-factor authentication for Contao backend users.

Contao ships with two-factor authentication based on an authenticator app (TOTP). That works well, but it requires every editor to install and configure an app — which is a real hurdle for occasional users, and a support problem when someone loses their phone.

This bundle adds a second option: a six-digit code sent by email at every login. Users choose the method that suits them, administrators can require that some method is active, and both methods live side by side on Contao's existing security page.

Features

  • One-time codes by email — a six-digit code is sent to the user's registered address at every login
  • Free choice of method — every user picks either an authenticator app or email, and can switch at any time
  • Optional enforcement — a single global setting requires all backend users to set up two-factor authentication before they can work
  • Brute-force protection — codes expire after five minutes, and the account is locked after five wrong attempts
  • Password confirmation — disabling or switching a method requires the account password
  • Integrated, not bolted on — extends Contao's own security page instead of adding a separate module
  • German and English included

Screenshots

Choosing a method

Users open their profile menu and select "Security". Both methods are offered side by side, with an explanation of the trade-offs.

Choosing a two-factor method

Activating email authentication

A code is sent to the registered address and confirmed once to activate the method.

Activating email authentication

Enforcing two-factor authentication

Administrators enable the requirement globally under System settings → Two-factor authentication.

Enforcement setting in the system settings

Requirements

PHP 8.1 or higher
Contao 5.3 or higher
Dependencies scheb/2fa-bundle ^7.0 || ^8.0, scheb/2fa-email ^7.0 || ^8.0

A working mailer configuration is required — the bundle sends codes through Contao's mailer, so MAILER_DSN and the system sender address must be set up correctly.

Installation

Contao Manager

Search for hi1/contao-2fa-email-bundle and install it.

Composer

composer require hi1/contao-2fa-email-bundle

Then update the database, either through the Contao install tool or on the command line:

vendor/bin/contao-console contao:migrate

This adds four columns to tl_user; no existing data is modified.

Usage

For backend users

  1. Open the profile menu in the top right corner and select Security.
  2. Choose Email or Authenticator app.
  3. For email: click Send verification code, then enter the six-digit code from your inbox.
  4. From the next login onwards, a code is sent automatically after the password step.

The active method can be changed at any time on the same page. Switching or disabling a method requires the account password. Note that when the authenticator app method is disabled, its configuration key changes — the app entry has to be set up again.

For administrators

To require two-factor authentication for everyone, open System settings and enable Enforce two-factor authentication under the Two-factor authentication legend.

Backend users without an active method are then redirected to the security page on every request until they set one up. The redirect deliberately steps aside for a few cases:

  • while a two-factor challenge is already running
  • while an administrator is impersonating another user
  • while a forced password change is pending
  • on the security page itself

If a user is locked out after five failed code attempts, their account is disabled. An administrator has to re-enable it in the user management.

How it works

Registration as a two-factor provider

The bundle registers EmailTwoFactorProvider as a scheb_two_factor.provider under the alias contao_email. Contao's own TOTP provider stays untouched, so both methods can be offered next to each other.

TotpProviderDecorator decorates Contao's contao.security.two_factor.provider and suppresses it when the user has no TOTP secret stored. Without that, Contao's provider would claim every login and the email method would never be reached.

Code lifecycle

Step Behaviour
Generation Six digits from random_int(), zero-padded, stored in tl_user
Delivery Contao\Email, subject and body in German or English depending on the user's language
Validity 300 seconds (5 minutes)
Comparison hash_equals(), so the check is not timing-dependent
On success Code, timestamp and attempt counter are cleared
On failure Attempt counter is incremented
After 5 failures Code cleared and the user account is set to disabled

Code entry during login is handled by Contao's own two-factor screen; the provider intentionally does not register a scheb form renderer.

Enforcement

EnforceTwoFactorListener hooks into kernel.request at priority -1 and only acts on backend requests. It reads the enforceTwoFactor system setting and redirects to contao_backend?do=security when neither useTwoFactor nor useEmailTwoFactor is set for the current user.

Backend integration

The bundle replaces the callback of Contao's existing security module ($GLOBALS['BE_MOD']['accounts']['security']['callback']) with ModuleTwoFactorEmail, which renders both methods, the switch between them and the password confirmation.

Database

Four columns are added to tl_user, all hidden from the DCA listing and excluded from record copying:

Column Type Purpose
useEmailTwoFactor boolean Email method active for this user
emailTwoFactorCode varchar(6) Currently valid code
emailTwoFactorCodeCreatedAt int unsigned Issue timestamp, used for expiry
emailTwoFactorFailedAttempts int unsigned Failed attempts since the last valid code

One setting is added to tl_settings:

Setting Purpose
enforceTwoFactor Require every backend user to set up two-factor authentication

Security notes

Codes sent by email are less secure than TOTP: they travel through the mail system and are only as safe as the recipient's mailbox. Where the threat model calls for it, prefer the authenticator app — this bundle exists to raise the floor for users who would otherwise run without any second factor at all, not to replace TOTP.

Codes are stored in plain text in the database for their five-minute lifetime and are cleared immediately on success. Anyone with database read access during that window could read a pending code.

Translations

German and English are included. Additional languages can be contributed as Contao language files under contao/languages/<locale>/.

Contributing

Issues and merge requests are welcome. Please keep the coding style consistent with the existing code (declare(strict_types=1), constructor property promotion, readonly properties).

License

Licensed under the GNU Lesser General Public License v3.0 or later (LGPL-3.0-or-later), the same license Contao itself uses.

You may use this bundle freely, including in commercial projects and on client sites, and you may distribute it as part of a larger work without that work having to adopt the same license. If you modify the bundle itself and distribute your modified version, those changes have to be made available under the LGPL as well.

The full text is in LICENSE. The LGPL incorporates the terms of the GNU General Public License v3 by reference.

Developed by HI¹ · support@hi1.at