
Documentation
TYPO3 Module “Single-Sign-On”
Version 2.0.3 (04 Nov 2026)
Chapters of this Documentation
Federation means creating the TYPO3 user record from the SAML assertion the first time somebody signs in. It is off by default in both scopes, and off is the safe default: an identity the provider authenticates but TYPO3 has no user for is rejected, and the rejection is recorded—see Logging.
federation:
enabled: true
# userGroups: [2]
The key sits at the top level of the backend configuration file, and under settings.sso_authentication per site for the frontend. Turning it on means every identity your identity provider authenticates receives a TYPO3 account. That is the intended behavior for a tenant whose users should all have access, and the wrong one for a company-wide identity provider that also serves other applications—for that case, restrict it with a listener on BeforeUserCreationEvent.
userStoragePid
The page a new record is created on. Defaults to 0.
Leave it at 0 for the backend. TYPO3 only looks up be_users at the root of the page tree, so a backend user created anywhere else is invisible to every later login. Federation then does the only thing it can and creates another one: one extra be_users row per login, each carrying whatever group assignment the current claims produce, and none of them the one an administrator edited. The symptom is a be_users list that grows every time somebody signs in.
For the frontend, point it at your fe_users folder. A frontend SSO login only looks up users on that page, not in subfolders, so users of another site's folder are never matched.
Group Assignment
For a newly created user, in this order:
- A
claimsMappingentry targetingusergroupwins if the assertion carried a value it accepts - Otherwise
federation.userGroupsis used verbatim, regardless of what the identity provider sent
Backend users created through federation additionally get options = 3—database and file mounts taken from the group—and, when EXT:workspaces is installed, permission to edit the live workspace. Without those a federated backend user would sign in to a backend with no accessible page tree.
Matching an Existing User
Once a user exists, later logins have to find it again, or federation creates a duplicate. The lookup accepts any of the following as a match:
| Match | Compared Against |
|---|---|
| The name identifier from the assertion | username—and email too when allowEmailAsUsername is on and the name identifier is an email address |
A raw name claim, or login_name when name is absent | The same two columns, under the same condition |
A raw emailaddress claim, or email when emailaddress is absent | username and email, unconditionally |
The claimsMapping-resolved email value | username and email, unconditionally |
tx_sso_tenantid and tx_sso_objectidentifier together | Both columns, as an alternative in its own right |
Two of these deserve a note.
The raw-claim rows rarely fire in practice. They match an attribute literally named name, login_name, emailaddress or email—or one of the WS-Federation claim URIs that share those friendly names. Neither Entra ID nor a typical KeyCloak SAML client sends those names; they send namespaced URIs or OIDs. The row that carries the frontend and backend of a normal installation is the claimsMapping-resolved email, which is why declaring an email mapping matters even when you do not care about the column.
Tenant plus object identifier is a genuine alternative, not an extra condition, and it wins. It identifies a user even when the TYPO3 username has since been changed by hand and nothing else matches any more. Both columns have to be filled for it to apply, which happens on the first login that carries both claims. Among the other matches, the lowest uid wins.
The lookup excludes deleted records and honors the table’s enable clause, so a disabled or expired user is not found—correctly, because such a user must not be logged in.
The Disabled-User Guard
That last point creates a trap federation has to avoid. A disabled user with the right username is invisible to the lookup, so federation would otherwise create a second, active row with the same username—and the user an administrator disabled would be signed in again under it.
Before creating anything, federation therefore checks whether any row already holds that exact username, including disabled and expired ones. If one does, nothing is created and the login is refused, with a warning in the log naming the username. The user stays locked out, which is what disabling them meant.
allowEmailAsUsername
A top-level key in the configuration file, off by default. On, the name identifier—and a raw name or login_name claim—is additionally compared against the email column, not only username. The comparison only happens when the value parses as an email address.
It is a migration aid, for users originally created with an email address where their username is something else. TYPO3 does not enforce uniqueness on email, so as soon as two active users share an address, a login can bind to whichever row the database returns first—and that means signing somebody in with another person's permissions. Turn it off once the affected users are migrated.
What a Login Writes Back
On every successful login, found user or freshly created:
- All mapped claims, as resolved by
claimsMapping tx_sso_objectidentifier, from the object identifier claim—falling back to the name identifier when the identity provider sends no such claim, so the column is filled either waytx_sso_tenantid, only when a tenant claim is presenttx_sso_sessionindex, from the assertion. This is written after the update event and cannot be removed by a listener, because a single logout needs itusergroup, reconciled rather than replaced: every group listed anywhere in ausergroupvalueMappingis treated as identity-provider-managed and removed first, then the mapped groups are added. A group an administrator assigned by hand survives, as long as it does not appear in avalueMapping. One that does appear is removed on the next login if the claims no longer grant it
A listener can change all of this except the session index, see BeforeUserDataUpdatedEvent.
The tx_sso_* Columns
Four columns are added to both be_users and fe_users.
| Column | Written By | Purpose |
|---|---|---|
tx_sso_enabled | An editor | Editorial flag only, see below. Rendered as a toggle after the admin field |
tx_sso_sessionindex | Every login | The identity provider's session index, needed to build a logout request |
tx_sso_tenantid | A login carrying a tenant claim | Half of the tenant/object identity match. Read-only in the form |
tx_sso_objectidentifier | Every login | The other half. Read-only in the form |
tx_sso_tenantid and tx_sso_objectidentifier are shown after lastlogin, and read-only on purpose: editing them by hand would break the match they exist for.

tx_sso_enabled Is Editorial Only
It does two things in the backend form: it hides the password field while checked, and it shows a warning badge when it is checked on a user whose tenant or object identifier is missing—that is, one who has not completed an SSO login.
It has no effect on whether an SSO login is accepted. Checking it does not enable SSO for a user, and unchecking it does not lock a user out of SSO. The authentication service ignores it entirely; the check that would consult it exists in the code but is deliberately commented out. If you need to keep a user out, disable the user, or stop the identity provider from authenticating them.