Architecture
The extension consists of these core components:
Classes/
Authentication/ Auth service (TYPO3 auth chain)
Configuration/ Extension configuration value object
Controller/ REST API + backend module controllers
Domain/Dto/ DTOs and value objects
Domain/Enum/ EnforcementLevel enum
Domain/Model/ Credential entity
EventListener/ PSR-14 listeners (login form, banner)
Form/Element/ PasskeyInfoElement (FormEngine)
Middleware/ PSR-15 middleware (routes, interstitial)
Service/ Business logic services
UserSettings/ PasskeySettingsPanel (User Settings)
Login form injection
The passkey button is injected into the standard TYPO3 login
form via the Inject PSR-14 event listener.
It listens to Modify
and:
- Loads
PasskeyviaLogin. js PageRenderer:: add Js File () - Injects an inline script with
window.that providesNr Passkeys Be Config login,Options Url rp,Id origin, anddiscoverableto the JavaScriptEnabled
The JavaScript builds the passkey UI (button, error area,
hidden fields) dynamically via DOM manipulation and inserts it
into #typo3-. No Fluid partial or separate
template is needed.
The passkey management panel in User Settings also uses
load to load
Passkey as an ES module, which imports
TYPO3 native APIs (Ajax, Notification,
Modal, sudo, Document).
Banner injection
The Inject PSR-14 event listener listens to
After and loads
Passkey on every backend page. The JavaScript
fetches enforcement status via AJAX, checks
session for prior dismissal, and renders a rich
banner with title, passkey explanation, documentation link,
and administrator contact help text. The banner supports
TYPO3 v12/v13 (via .scaffold-) and v14
(via typo3- parent fallback).
Interstitial middleware
Passkey is a PSR-15 middleware that
intercepts backend requests for users whose effective
enforcement level is Required or Enforced. If the
user has no registered passkeys, it renders a full-page
interstitial prompting them to register.
The middleware exempts login, logout, AJAX, MFA, and passkey API routes. During the grace period the user can skip the interstitial (protected by a CSRF nonce). Once the grace period expires or the level is Enforced, skipping is disabled.
Authentication data flow
Important
$GLOBALS is null during the
TYPO3 auth service chain. Custom POST fields are
inaccessible. The only data available is $this->login
with keys status, uname, uident, and
uident_.
The passkey assertion and challenge token are packed into the
userident field as JSON:
{
"_type": "passkey",
"assertion": {
"id": "...",
"type": "public-key",
"response": {}
},
"challengeToken": "..."
}
The Passkey reads from
$this->login, detects the
_type: "passkey" marker, and extracts the assertion and
challenge token for verification.
Authentication service
Passkey extends TYPO3's
Abstract and is registered at
priority 80 (higher than Salted
at 50).
The service implements two methods:
get-- Checks if the login data contains a passkey payload (JSON withUser () _type: "passkey"). If it does, the user is looked up by username. If no passkey data is present, the request falls through to the next auth service.-
auth-- Returns:User () 200-- Authenticated, stop chain (passkey verified)100-- Not responsible (no passkey data, let next service handle it)0-- Authentication failed
Because TYPO3 authentication services are instantiated by the
service manager (not the DI container), dependencies are
obtained via General.
Public route middleware
Public is a PSR-15 middleware that allows
passkey login API endpoints
(/typo3/) to be accessed without an
authenticated backend session. Without it, TYPO3 would
redirect unauthenticated requests to the login page.
Domain model
The Credential class is a plain PHP value object (not
Extbase) with from/to for database
serialization.
Key fields:
credential-- WebAuthn credential identifier (binary)Id public-- COSE-encoded public key (binary blob)Key Cose sign-- Counter incremented on each use (clone detection)Count user-- SHA-256 hash of the user UID + encryption keyHandle aaguid-- Authenticator Attestation GUIDtransports-- JSON array of transport hints