
Documentation
TYPO3 Module “Single-Sign-On”
Version 2.0.3 (04 Nov 2026)
Chapters of this Documentation
Testing SAML needs a real identity provider—there is no offline mode and no mock. KeyCloak in DDEV is the shortest way to one, and the repository includes an example realm so you do not have to click a SAML client together by hand.
Setup
Install the KeyCloak add-on and restart:
ddev add-on get b13/ddev-keycloak && ddev restart
Import the example realm. The two JSON files are development fixtures in this repository (Resources/Private/Examples/sso-example-*.json); they are not part of the Composer package. Copy them from a checkout:
cp /path/to/sso-authentication/Resources/Private/Examples/sso-example-*.json \
.ddev/keycloak/import/
ddev kcctl import
Point the backend at the matching service provider configuration:
$GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['sso_authentication']['BackendServiceProviderConfigurationFile']
= 'EXT:sso_authentication/Resources/Private/Examples/sso_authentication.yaml';
Then open the backend login screen and pick the SSO entry. The example user is benni-example with the password huhu2025.
This is a local-development shortcut, not a template for production. The SAML client does not require a client signature, and pointing the backend at the example inside the extension means the next composer update replaces your configuration. Copy sso_authentication.yaml into your own site package for anything else.
The realm export carries no keys. KeyCloak generates its own signing keys on import when the file does not include them. If you export a realm of your own to share, strip components."org.keycloak.keys.KeyProvider" before you commit it.
What the Example Realm Contains
| Piece | Value |
|---|---|
| Realm | sso-example |
| SAML client | ${DDEV_PROJECT}.ddev.site/typo3/—the DDEV project name is substituted on import, so the same file works in any project |
| Name identifier | The KeyCloak username |
| Client signature | Not required, so TYPO3 does not have to sign its requests for this realm |
| Group | Erste Gruppe, which benni-example is a member of |
| Attribute mappers | urn:oid:1.2.840.113549.1.9.1 (email), urn:oid:2.5.4.42 (given name), urn:oid:2.5.4.4 (surname), and a group-membership mapper under the attribute name bennis-group-list |
Those four attribute names are exactly the keys the shipped sso_authentication.yaml maps, which is why the pair works out of the box. Note what the realm does not send: no tenant and no object identifier claim. So tx_sso_tenantid stays empty and tx_sso_objectidentifier falls back to the name identifier—the same situation as a typical KeyCloak setup in production, and the reason the mapped email matters for matching an existing user.
Setting a Realm Up by Hand
If you would rather build it yourself, or need a second realm:
- Admin console at
your-project.ddev.site/admin, withadmin/password - Create a realm—Master > Create realm
- Create a user—Users > Add user, give them a password on the Credentials tab with Temporary off, and add them to a group under Users > user > Groups
- Create a SAML client—Clients > Create client, type SAML:
- Client ID:
your-project.ddev.site/typo3/for backend login, and mind the trailing slash—it has to match the entity ID TYPO3 sends - Root URL and valid redirect URIs: the same base, with
/*for the redirect URI - Master SAML Processing URL:
your-project.ddev.site/typo3/auth/sso/acs - Turn Client signature required off unless you have configured
x509certandprivateKeyand enabledsecurity.authnRequestsSigned
- Client ID:
- Attribute mappers—Client Scopes > add predefined mapper. Skip the role list mapper
- Group mapper—Add Mapper > By Configuration > Group list. The Group attribute name is what your
claimsMappingkey has to be
Export before you commit anything you changed in the admin console, or the next import overwrites it:
ddev kcctl export
ddev kcctl import
Troubleshooting
Check the environment variables first—a %env(...)% placeholder that resolves to an empty string produces an unhelpful failure a long way from its cause. Then switch on debug logging and request the metadata endpoint; between the two, nearly every local problem is visible.
For KeyCloak’s own side, add this to .ddev/docker-compose.keycloak.yaml, restart, and follow the log with ddev logs -s keycloak -f:
environment:
- KEYCLOAK_LOG_LEVEL=DEBUG
Common Issues
| Symptom | Cause |
|---|---|
client_not_found, or Cannot_match_source_hash | The client ID in KeyCloak does not match the entity ID TYPO3 sends. Almost always the trailing slash |
Found an Attribute element with duplicated Name | Two mappers in the client scopes produce the same attribute name. Remove one, or set security.allowRepeatAttributeName if you cannot |
| The login form comes back with nothing on screen | The normal symptom of every rejected assertion. Logging has the reason. Over https through a proxy, start at Reverse Proxy |
A new be_users row appears on every login | userStoragePid is not 0 for the backend, or nothing in the claims matches the existing user. See User Federation |
Mapping an Attribute You Can See in KeyCloak
The keys of claimsMapping are the SAML Attribute Name shown under Clients > your client > Client scopes, and the values are column names of be_users or fe_users. There is no discovery step: an attribute the identity provider sends and the mapping does not name is simply ignored, which is why an attribute arriving correctly and still not landing in the record is the most common mapping mistake. Debug logging prints both the raw claims and the mapped result of every login, which is the fastest way to see which of the two is missing.