Skip to main content
Version: Next

Two-Factor Authentication

Studio users can protect their login with a second factor: a time-based code from an authenticator app (Google Authenticator, Microsoft Authenticator, Aegis or any other TOTP app). Studio configures scheb/2fa itself, so no further bundle is needed.

Available from Pimcore Studio 2026.4.0.

Who needs a code​

Only required decides whether the login asks for a code, as in the classic admin. A setup the user confirms in the profile sets required as well, so it protects the login from then on. Switching required off in the user management lets the user in with the password alone again; the stored setup stays and is asked for again once required is on.

required is the two-factor switch in the user management, enabled comes from the user's own setup:

requiredenabledLogin
noanyPassword only
yesyesPassword, then a code from the authenticator app
yesnoPassword, then the user sets up an authenticator app; the first code from it finishes the login

Until a required user has set up two-factor authentication, whoever logs in with that user's password first sets it up. Make sure the password reaches the right person.

The login with a login token (POST /login/token, e.g. from a password reset mail or a login link created in the user management) does not ask for a code.

Configuration​

pimcore_studio_backend:
two_factor_authentication:
# Name shown in the authenticator app. Default: Pimcore
issuer: 'Acme PIM'
# Shown next to the user name in the authenticator app.
# Default: the main domain (pimcore.general.domain), if set
server_name: 'pim.acme.com'

Without server_name, the entry shows the user name together with the main domain (pimcore.general.domain), e.g. admin@pim.acme.com, or only the user name when no main domain is set. Set server_name so users can tell the entries of several installations apart in the app.

Codes sent to POST /login/2fa are limited per user: after 5 attempts within 5 minutes, further codes are refused with 429 until the window is over, even a correct one. A correct code resets the count, a password login does not. The limiter is studio_two_factor_code; adjust it under framework.rate_limiter (see Rate Limiting).

Login flow​

  1. POST /login with user name and password. Without two-factor authentication the answer is an empty 200 and the user is logged in, as before. Otherwise the answer is 200 with {"twoFactorRequired": true, "twoFactorStep": "verify", "digits": 6} (or step "setup"), and no user data. The login is not finished: until it is, every endpoint that needs a login answers 401, except the setup endpoint in step setup. digits is the number of digits in a code (scheb_two_factor.google.digits, default 6).
  2. Step verify: POST /login/2fa with {"code": "123456"}. A wrong code answers 401 and can be retried.
  3. Step setup: POST /user/two-factor/setup returns {secret, otpauthUri, digits}; show otpauthUri as a QR code and secret for manual entry. Then send the first code from the app to POST /login/2fa; it enables two-factor authentication and finishes the login.
  4. To go back, POST /logout.

Until the code is in, the 401 from other endpoints carries the step as well: {"message": "Two-factor authentication is required.", "twoFactorRequired": true, "twoFactorStep": "verify", "digits": 6} (or step "setup"). A client uses it when it did not send the login itself, e.g. after the redirect back from an OpenID Connect (SSO) login. An SSO login asks for the code too when its authenticator creates a token type that scheb covers; the default security_tokens cover Symfony's PostAuthenticationToken, which authenticators create unless they use their own token class.

The texts of the code and setup steps are among the public translations, so POST /translations returns them before the login is finished.

The setup endpoint is opened for this step by Studio itself; the access_control rules from the installation stay as they are. This relies on the documented rule (ROLE_PIMCORE_USER for the Studio API) and Symfony's default access decision strategy (affirmative). With a different role in that rule or another strategy, the setup endpoint stays closed and users who must set up two-factor authentication cannot log in.

Profile and user management​

EndpointWhoWhat
POST /user/two-factor/setupthe current userNew secret, pending until confirmed. During a renewal the current secret keeps working until then.
POST /user/two-factor/confirmthe current user{"code"} from the new secret enables it and sets required; a wrong code answers 422, no pending setup 409.
DELETE /user/two-factorthe current userDisables two-factor authentication; refused (403) while it is required, so it only removes a setup that is no longer required.
DELETE /user/{id}/two-factoruser management permissionResets a user's two-factor authentication. Admins may reset any user, other users only themselves, also while it is required for them. required stays as it is, so a required user sets it up again at the next login.

Anyone with the user management permission can switch required on and off for users who are not admins, themselves included; only admins can change it for admin users. This is the same as in the classic admin. Switching it off removes the code from that user's login without telling the user.

The secret is only ever returned by the setup endpoint.