---
title: User Federation
url: "https://b13.com/products/single-sign-on-for-typo3/documentation/user-federation"
date: 2026-09-10
modified: 2026-11-10
---

# User Federation

![Icon featuring a key and overlapping squares, symbolizing security or access control.](https://b13.com/fileadmin/_processed_/0/a/csm_sharing-ext-SingleSignOn-S_512e772865.webp)

  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](https://b13.com/products/single-sign-on-for-typo3/documentation/logging#c12418).

     Copy

```
```
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`](https://b13.com/products/single-sign-on-for-typo3/documentation/events#c12402).

   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:

1. A `claimsMapping` entry targeting `usergroup` wins if the assertion carried a value it accepts
2. Otherwise `federation.userGroups` is 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`](#c12396) 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`](https://b13.com/products/single-sign-on-for-typo3/documentation/configuration#c12343)
- `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 way
- `tx_sso_tenantid`, only when a tenant claim is present
- `tx_sso_sessionindex`, from the assertion. This is written after the update event and cannot be removed by a listener, because a [single logout](https://b13.com/products/single-sign-on-for-typo3/documentation/how-it-works#c12359) needs it
- `usergroup`, reconciled rather than replaced: every group listed anywhere in a `usergroup` `valueMapping` is 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 a `valueMapping`.** 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`](https://b13.com/products/single-sign-on-for-typo3/documentation/events#c12405).

   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.

  ![The Access tab of a TYPO3 backend user record: the core fields "Login enabled" and "Last login", followed by the two read-only fields the extension adds - the tenant ID, empty because KeyCloak sends no tenant claim, and the object identifier, holding the name identifier "homer" the identity provider sent.](https://b13.com/fileadmin/_processed_/1/e/csm_Documentation-Images-backend-user-sso-columns_3297a5844d.webp)

  ###  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.