OpenID Connect login for TYPO3 v14, frontend and backend, configured per site
through Site Settings. Successor of causal/oidc + wapplersystems/oidc-addons
in WapplerSystems projects.
- Discovery: issuer + client id is all the provider configuration you need
- Authorization Code Flow with PKCE (S256) always on, state, nonce, browser-bound state cookie
- Full ID token validation: signature via JWKS (cached, rotation-aware), algorithm
allow-list,
iss,aud,azp,exp/iat,nonce,at_hash - RFC 9207
issresponse parameter check (mix-up defence) - Login through the core authentication chain (session-id regeneration, rate limiting, login events, MFA stay intact)
- RP-initiated logout with
id_token_hint(no Keycloak confirmation page) - Back-channel logout for frontend and backend sessions, any session backend
- Silent SSO (
prompt=none) for visitors who are already signed in at the provider - Backend login ("Single Sign-On" tab on the TYPO3 login screen)
- Account linking of existing users by e-mail/username (only verified e-mail), group mapping
oidc:checkdiagnostics andoidc:provisionto create/update the Keycloak client- Context variants: different client per environment in one
settings.yaml
composer require wapplersystems/oidc-connect
vendor/bin/typo3 extension:setup -e oidc_connectAdd the site set wapplersystems/oidc-connect to the site (config/sites/<site>/config.yaml,
dependencies), then configure it.
config/sites/<site>/settings.yaml:
oidcConnect:
issuer: 'https://auth.example.com/realms/main'
clientId: typo3-www
users:
storagePid: 14 # folder with the fe_users records
defaultGroups: '1' # every SSO user gets this groupThe client secret is never a site setting (the settings editor would write a resolved
%env()% back as plain text, and resolved settings are cached on disk). It is looked up in:
- the environment variable named in
oidcConnect.clientSecretEnv OIDC_CONNECT_<SITE_IDENTIFIER>_CLIENT_SECRET(e.g.OIDC_CONNECT_MAIN_CLIENT_SECRET)$GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['oidc_connect']['clientSecrets']['<site>'](e.g. inconfig/system/additional.php)
Register these URLs at the provider (or let oidc:provision do it):
| Purpose | URL |
|---|---|
| Redirect URI (frontend) | <site base>/oauth_callback |
| Redirect URI (backend) | <origin>/typo3/oidc-connect/callback |
| Post-logout redirect | <origin>/* |
| Back-channel logout | <site base>/oauth_backchannel_logout |
vendor/bin/typo3 oidc:check <site> verifies discovery, issuer, JWKS, secret and URLs.
export OIDC_CONNECT_PROVISION_CLIENT_ID=typo3-provisioner
export OIDC_CONNECT_PROVISION_CLIENT_SECRET=...
vendor/bin/typo3 oidc:provision main --dry-run # show what would change
vendor/bin/typo3 oidc:provision main # create or update the clientThe provisioner client is a confidential client in the same realm with Service accounts
enabled and the realm-management roles manage-clients and view-clients.
The command works for the current application context: it adds the redirect URIs of the
site base that is active there (baseVariants apply) and never removes URIs of other
environments sharing the client. Run it once per environment. A newly created client's secret
is only printed with --show-secret.
Links (Fluid, namespace http://typo3.org/ns/WapplerSystems/OidcConnect/ViewHelpers):
<a href="{oidc:loginUrl()}">Log in</a> <!-- returns to the current page -->
<a href="{oidc:loginUrl(redirect: '/members/')}">Log in</a>
<a href="{oidc:loginUrl(prompt: 'create')}">Register</a>
<a href="{oidc:logoutUrl()}">Log out</a>or plain URLs: /oauth_authorize?redirect=/members/ and /oauth_logout.
The content element Login / logout button (SSO) renders the right button and shows
error messages (?oidc_error=<code>).
Any local logout (felogin, ?logintype=logout) closes the OIDC session binding as well; only
the logout route (and the backend logout) additionally ends the session at the provider.
Lookup order on login:
- record linked to
issuer+sub(tx_oidcconnect_issuer,tx_oidcconnect_subject) users.linkByField(usernameoremail) equal to claimusers.linkByClaim— only unlinked, non-deleted records, only if unambiguous, and for e-mail only when the provider reportsemail_verified: true- new record in
users.storagePid(unlessusers.mustExistLocally); a new frontend user needs at least one group
Claim mapping and group mapping are YAML-only:
oidcConnect:
mapping:
fe_users:
email: email
first_name: given_name
last_name: family_name
company: organization.name # dotted path into nested claims
country: '=DE' # constant
users:
groupsClaim: groups # e.g. Keycloak group membership mapper
groupMapping:
fe_groups:
'/partner': '6'
'/staff/*': '11,12'Groups listed in groupMapping are managed: they are added and removed on every login;
other groups of the record stay untouched. defaultGroups are always added.
Listeners of WapplerSystems\OidcConnect\Event\BeforeUserProvisionedEvent can change the
data or deny the login.
oidcConnect:
backend:
enable: true
linkByField: email # link existing be_users by verified e-mail
createUsers: falseThe backend has no site context: the extension configuration backendSite names the site
whose settings are used (default: the first site with backend.enable).
oidcConnect:
issuer: 'https://auth.example.com/realms/main'
clientId: typo3-www
variants:
- condition: 'applicationContext matches "#^Development#"'
issuer: 'https://localhost:8443/realms/test'
clientId: typo3-localThe first matching variant is merged over the base values.
auth.silentSso: true checks anonymous visitors once per auth.silentSsoInterval seconds
with prompt=none. Bots, non-HTML requests, prefetches and iframes are skipped. Every
first visit of a browser gets one extra redirect round trip, so only enable it where most
visitors are expected to have a provider session.
- Access and refresh tokens are discarded after login; only the ID token is kept (for
id_token_hint) and deleted on logout. - The callback refuses responses without a matching state record and browser cookie, issuer mismatches, unsigned or HMAC-signed tokens and replayed logout tokens.
uid,pid,password,usergroup,adminand similar columns can never be written through claim mapping.
GPL-2.0-or-later