Skip directly to page content
b13 GmbH

Hauptstätter Str. 59
70178 Stuttgart
Germany
+49 (0) 711 460 589 70 [email protected]
Icon featuring a key and overlapping squares, symbolizing security or access control.

Documentation

TYPO3 Module “Single-Sign-On”
Version 2.0.3 (04 Nov 2026)

Chapters of this Documentation

Two PSR-14 events sit in the path of every SSO login. One fires before a user is created, the other before an existing user's data is refreshed. Both hand you the data about to be written and the claims it came from.

Register a listener the usual way, in your extension's Configuration/Services.yaml:

services:
  MyVendor\MyExt\Sso\RestrictFederation:
    tags:
      - name: event.listener
        identifier: 'my-ext/restrict-federation'

BeforeUserCreationEvent

B13\SSOAuthentication\Event\BeforeUserCreationEvent

Dispatched immediately before the insert, only when federation is enabled and no existing user was found.

Method Returns
getUserData() array The record about to be inserted: the mapped claims plus username, pid, usergroup, crdate and tx_sso_sessionindex
setUserData(array $userData) void Replaces it
getUserClaims() SsoUserDetails The claims from the assertion, mapped and raw
getSamlSettings() SamlSettings The resolved settings for this scope
loginType ?string Readonly property, 'BE' or 'FE'
preventCreation() void Cancels the insert

preventCreation() is new in 2.0.0. The event implements StoppableEventInterface, and calling it stops propagation—so no listener registered after yours runs either. The login is then treated exactly as if federation had been switched off: no user is created, the login is refused, and the refusal is recorded at info level.

This is how you keep an identity provider that also serves other applications from handing out TYPO3 accounts to everybody it authenticates:

final class RestrictFederation
{
    public function __invoke(BeforeUserCreationEvent $event): void
    {
        if ($event->loginType !== 'BE') {
            return;
        }
        $department = $event->getUserClaims()->getClaim('department');
        if ($department !== 'Editorial') {
            $event->preventCreation();
        }
    }
}

Note the loginType guard. Both scopes dispatch the same event, and a listener without that check applies to frontend users too.

The extension registers a listener of its own on this event, which gives a federated backend user options = 3 and, with EXT:workspaces installed, live-workspace permission. Calling preventCreation() from your own listener stops propagation, which may stop that one from running—harmless here, since nothing is created anyway.

BeforeUserDataUpdatedEvent

B13\SSOAuthentication\Event\BeforeUserDataUpdatedEvent

Dispatched before the update that runs on every successful login, for federated and hand-made users alike.

Method Returns
getUpdateData() array The columns about to be written
setUpdateData(array $updateData) void Replaces them
getUserData() array The user record as it currently stands in the database
getUserClaims() SsoUserDetails The claims from the assertion
loginType ?string Readonly property, 'BE' or 'FE'

Removing a key is how you stop SSO from overwriting a value somebody corrected by hand:

final class KeepManualRealName
{
    public function __invoke(BeforeUserDataUpdatedEvent $event): void
    {
        $data = $event->getUpdateData();
        unset($data['realName']);
        $event->setUpdateData($data);
    }
}

tx_sso_sessionindex is added to the data after this event, so it cannot be removed. A single logout needs it, and a login that quietly skipped it would leave a session nobody can end.

Why This One Cannot Be Stopped

There is no preventUpdate(), and the event does not implement StoppableEventInterface. That is deliberate: at this point the user has already been authenticated and the login will succeed. An event that could cancel the update would not cancel the login—it would only produce a session whose user record no longer matches the assertion it was created from, with a stale tx_sso_sessionindex and stale group membership.

If you want to refuse somebody, refuse the creation with preventCreation() above, or disable the user record. If you want to keep a column, remove that column from the update data. Those two cover the cases a veto would be reached for, without the half-applied state.