Skip to the content
Documentation
Manuals

Two-step sign-in

After the password, a code from an authenticator app on the phone, or one sent by e-mail: the password alone is then useless to anyone else.

  • Topic type: Concept
  • For: Visitors Administrators
  • doc.guides.two-factor-login
  • Last changed
  • JSON
On this page

After the password, a code from an authenticator app on the phone, or one sent by e-mail: the password alone is then useless to anyone else. This guide is for site owners and administrators who turn it on, and for users who set it up. It is provided by the extension sevenx_authentication_2fa (composer.json requires it; the installers activate it). The extension's own documentation has the details: extension/sevenx_authentication_2fa/README.md, INSTALL.md and doc/two-factor-pages.md.

Time: 15 minutes to turn it on, 2 minutes per user to set it up.

What a user sees

  1. Sign in as always, with username and password.

  2. Confirm it is you: a page asks for the 6-digit code. With an authenticator app, open the app and type the code it shows for the site; with e-mail codes, the code is in the mail (and in its subject). Each code works once. After five wrong codes the sign-in ends and starts again with the password.

  3. Set it up under My account > Two-step sign-in (/user2fa/setup; in the administration the same address): choose Authenticator app, scan the QR code (or type the key shown in groups of four), and enter the code the app shows. Until that code is entered nothing changes. The key is not shown again afterwards.

  4. Change or turn off: switching to e-mail codes or off, or Reset 2FA, asks for a current code from the app, so someone at an unattended computer cannot weaken the account.

  5. Keep the recovery codes: when the app is set up, the page shows ten recovery codes, once. Print them or keep them in a password manager. Each signs in once in place of the app's code (Use a recovery code on the code page); the setup page shows how many are left and makes a new set with a current code.

Lost the phone? Sign in with a recovery code and set the app up again. Without one, an administrator opens the user's object in the administration, sets the Two-step sign-in field to E-mail one-time code (or Disabled, where the second step is not required), and publishes. An administrator who is locked out is helped on the server:

php extension/sevenx_authentication_2fa/bin/php/sevenx2fauser.php --login=<login>          # what the account has
php extension/sevenx_authentication_2fa/bin/php/sevenx2fauser.php --login=<login> --reset  # remove it, unlock the account

After --reset the next sign-in asks for a new setup where the second step is required.

Turning it on

  1. Activate the extension ([ExtensionSettings] ActiveExtensions[]=sevenx_authentication_2fa) and the login handler in settings/override/site.ini.append.php, before standard:

    [UserSettings]
    LoginHandler[]
    LoginHandler[]=sevenxUser2fa
    LoginHandler[]=standard
  2. Add the field to the user class: a class attribute of the datatype 7x 2FA Configuration (identifier for example two_factor). Users of a class without it cannot keep a second step; the setup page says so.

  3. Give every role that signs in the policies user2fa/setup and user2fa/verify (Member, Editor, Administrator ...). Nothing is needed for anonymous visitors: the steps before signing in are in [RoleSettings] PolicyOmitList of the extension and check their own state.

  4. Recommended: a key that encrypts the authenticator secrets in the database, in settings/override/sevenxauthentication2fa.ini.append.php (keep it out of version control; losing it means everyone sets the app up again):

    [TOTPSettings]
    SecretKey=<output of: openssl rand -base64 48>
  5. Decide whether it is everyone's choice or required:

    [General] Enforce2FADefaultMethodEffect
    disabled (default)anyUsers opt in on the setup page.
    enabledtotpA user without a second step sets up the app right after the password, before being signed in.
    enabledemailA user without a second step gets e-mail codes straight away (the account's e-mail address must be real).

    Whom a required second step applies to:

    SettingEffect
    EnforcedGroups[]Only members of these user groups (node id or object remote id), also through groups below them. Empty: every user.
    ExemptGroups[], ExemptEmailDomains[]Never asked (they may still opt in): members of these groups, and addresses in these domains, e.g. example.invalid for the accounts of automated checks.
    GracePeriodDaysDays a user may still choose Set up later, from the first sign-in that asked. 0: the setup comes first.
    [Security] RecoveryCodesRecovery codes per authenticator (default 10, 0 for none).

    A new installation is made with Enforce2FA=enabled for the groups Administrator users (9b47a45624b023b1a76c73b74d704acf) and Editors (3c160cca19fb135f83bd02d911f04db2), no exemptions and no grace period: whoever can change the site needs a second step, front-end members choose for themselves. The extension's own default stays disabled, so updating it never locks anyone out of an existing site.

    Before requiring it, php extension/sevenx_authentication_2fa/bin/php/sevenx2fauser.php --enforced lists the accounts that would be asked to set it up at their next sign-in.

  6. Clear the caches and, for Velocity, deploy: ./console exp:velocity deploy --allow-root-user.

Try it on one siteaccess first: activate the extension as an access extension of a test siteaccess and put the LoginHandler lines in that siteaccess's site.ini.append.php. Remember that settings/override outranks a siteaccess: mail sent from the test siteaccess still uses the global [MailSettings] Transport.

On the demonstration server

Two-step sign-in is on at the demonstration server since 6 October 2026, as opt-in: the extension is active, the login handler is sevenxUser2fa then standard, the user class has the field Two-step sign-in (two_factor), every role that signs in (Member, Editor and the custom editor role; Administrator has all) has user2fa/setup and user2fa/verify, and the secrets are encrypted with a key in settings/override/sevenxauthentication2fa.ini.append.php. Nobody is enrolled by it: every account signs in as before until its owner turns the second step on under My account.

To require it for administrators and editors, as a new installation does, set in settings/override/sevenxauthentication2fa.ini.append.php:

[General]
Enforce2FA=enabled
DefaultMethod=totp
EnforcedGroups[]
EnforcedGroups[]=9b47a45624b023b1a76c73b74d704acf
EnforcedGroups[]=3c160cca19fb135f83bd02d911f04db2

then clear the INI cache (php bin/php/ezcache.php --clear-tag=ini) and deploy (./console exp:velocity deploy). Each of them without a second step then sets up an authenticator right after the password. Enrol the administrators first, so nobody is caught without a phone at hand, and decide for the accounts of automated checks: exempt their reserved domain (ExemptEmailDomains[]=example.invalid) or give each an authenticator from the console (sevenx2fauser.php --login=<login> --enrol-totp --secret-file=<path>). A role made later needs user2fa/setup and user2fa/verify for its users to set it up. To turn it off again, set Enforce2FA=disabled.

Signing in through WebDAV, the REST API's basic authentication, or code that calls eZUser::loginUser() directly asks for the password only. Keep those off where the second step is required, or limited to accounts that do not need it. A session signed in before the second step was required stays signed in until it ends.

Social login

Buttons such as Continue with Google appear on the login page (admin4, admin, Admin UI and the media design) for each provider enabled in sevenxauthentication2fa.ini; INSTALL.md of the extension has the steps per provider. A user found this way still has to pass their own second step. A cancelled or failed social login ends on a page that says what happened, with the way back to the login form.

What protects it

  • The password alone never signs in: the session only remembers that the password was right; the user is signed in, with a new session id, after the right code.
  • Codes: each authenticator code works once and only within a minute or so of the clock; wrong codes are limited per sign-in and count as failed logins of the account ([UserSettings] MaxNumberOfFailedLogin locks it); e-mail codes are kept hashed and a new one can be asked for once a minute, three times per sign-in; the link in the mail only works in the browser where the password was typed.
  • The QR code is drawn on the server; the key goes to no other service. Pages with a key or a pending sign-in are not cached.
  • Every address the sign-in sends a visitor on to passes the safe redirect rules.
  • Social login checks a one-time state bound to the provider and refuses addresses the provider has not verified.
  • Every step is written to var/log/auth.log (never a code or a secret).

Testing it

# the logic without a database
php vendor/bin/phpunit --no-configuration --bootstrap extension/sevenx_authentication_2fa/tests/bootstrap.php \
    extension/sevenx_authentication_2fa/tests/unit

By use: sign in with a test user, set up the app, sign out, sign in with a code, try a wrong code and the same code twice, then turn it off with a current code. Never enrol a real administrator account while trying it out.

Become an Exponential Partner. Partners share their information, provide funding, contribute back and develop the products with us, and that is how Exponential grows. A partner website and dashboard are planned for mid-2027: please become a partner today to speed up our growth rate and land new features faster. Start with Partners: start here and How to apply.

Was this helpful?

Only your answer is kept, not who you are.

Something wrong on this page? Report an error