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:
required | enabled | Login |
|---|---|---|
| no | any | Password only |
| yes | yes | Password, then a code from the authenticator app |
| yes | no | Password, 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
POST /loginwith user name and password. Without two-factor authentication the answer is an empty200and the user is logged in, as before. Otherwise the answer is200with{"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 answers401, except the setup endpoint in stepsetup.digitsis the number of digits in a code (scheb_two_factor.google.digits, default 6).- Step
verify:POST /login/2fawith{"code": "123456"}. A wrong code answers401and can be retried. - Step
setup:POST /user/two-factor/setupreturns{secret, otpauthUri, digits}; showotpauthUrias a QR code andsecretfor manual entry. Then send the first code from the app toPOST /login/2fa; it enables two-factor authentication and finishes the login. - 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
| Endpoint | Who | What |
|---|---|---|
POST /user/two-factor/setup | the current user | New secret, pending until confirmed. During a renewal the current secret keeps working until then. |
POST /user/two-factor/confirm | the current user | {"code"} from the new secret enables it and sets required; a wrong code answers 422, no pending setup 409. |
DELETE /user/two-factor | the current user | Disables two-factor authentication; refused (403) while it is required, so it only removes a setup that is no longer required. |
DELETE /user/{id}/two-factor | user management permission | Resets 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.