eID API Reference
All frontend API endpoints are handled by the eID dispatcher at
/?eID=nr_passkeys_fe. The action query parameter selects the
controller action. Request bodies are JSON. All responses are JSON with
Content-Type: application/json.
Token-based login flow
Both passkey login and recovery code login use a two-phase flow:
- eID verification -- JavaScript calls the eID endpoint
(
loginVerifyorrecoveryVerify). The server verifies the assertion or recovery code and stores the authenticatedfe_userUID in a short-lived cache entry (nr_passkeys_fe_noncecache, 2-minute TTL). The response includes aloginToken. - felogin form submission -- JavaScript submits a standard
logintype=loginform to the current page, including theloginTokenin a hidden field. TYPO3's normal FE authentication chain processes the request. - Auth service --
PasskeyFrontendAuthenticationService(priority 80) reads theloginToken, looks up the user UID in the cache, and returns thefe_usersrow. The token is consumed (one-time use).
This ensures users get a proper TYPO3 frontend session with all middleware (enforcement interstitial, session regeneration) applied.
Authentication endpoints (public)
These endpoints do not require a frontend session.
POST /?eID=nr_passkeys_fe&action=loginOptions
Request challenge options for passkey login.
Request:
{
"username": "johndoe"
}
For discoverable login, omit username or send an empty body.
Response (200):
{
"options": {
"challenge": "...",
"rpId": "example.com",
"allowCredentials": []
},
"challengeToken": "..."
}
POST /?eID=nr_passkeys_fe&action=loginVerify
Verify a passkey assertion and issue a login token.
Request:
{
"assertion": {
"id": "...",
"type": "public-key",
"response": {
"clientDataJSON": "...",
"authenticatorData": "...",
"signature": "...",
"userHandle": "..."
}
},
"challengeToken": "..."
}
Response (200):
{
"status": "ok",
"feUserUid": 42,
"loginToken": "abc123..."
}
The loginToken is a one-time token valid for 2 minutes. The
JavaScript must submit it via a standard felogin form to complete the
login (see Token-based login flow above).
Recovery code endpoint (public)
POST /?eID=nr_passkeys_fe&action=recoveryVerify
Login using a one-time recovery code.
Request:
{
"username": "johndoe",
"code": "XXXX-XXXX"
}
Response (200):
{
"status": "ok",
"feUserUid": 42,
"loginToken": "abc123..."
}
The loginToken is consumed via felogin form submission, identical
to the passkey login flow.
Enrollment endpoints (requires session)
POST /?eID=nr_passkeys_fe&action=registrationOptions
Request challenge options for passkey enrollment. Requires an active frontend session.
Response (200):
{
"options": {
"challenge": "...",
"rp": {"id": "example.com", "name": "My Site"},
"user": {"id": "...", "name": "johndoe", "displayName": "John Doe"},
"pubKeyCredParams": [{"type": "public-key", "alg": -7}]
},
"challengeToken": "..."
}
POST /?eID=nr_passkeys_fe&action=registrationVerify
Verify an attestation and save the new credential.
Request:
{
"attestation": {
"id": "...",
"type": "public-key",
"response": {
"clientDataJSON": "...",
"attestationObject": "..."
}
},
"challengeToken": "...",
"name": "My MacBook"
}
Response (200):
{
"status": "ok",
"credentialId": "..."
}
Management endpoints (requires session)
GET /?eID=nr_passkeys_fe&action=manageList
Returns the list of passkeys for the current user.
POST /?eID=nr_passkeys_fe&action=manageRename
Request: {"credentialId": "...", "name": "New Name"}
POST /?eID=nr_passkeys_fe&action=manageRemove
Request: {"credentialId": "..."}
POST /?eID=nr_passkeys_fe&action=recoveryGenerate
Generates a new set of 10 recovery codes. Returns the plaintext codes (shown once only).
Response (200):
{
"status": "ok",
"codes": ["XXXX-XXXX", "..."],
"count": 10
}
Enrollment status endpoints (requires session)
GET /?eID=nr_passkeys_fe&action=enrollmentStatus
Returns the current user's enrollment and enforcement status.
POST /?eID=nr_passkeys_fe&action=enrollmentSkip
Skips the enrollment interstitial (only available during grace period
when enforcement level is required).
Action routing summary
The eID dispatcher routes the action query parameter to controllers:
| Action | Controller method | Auth |
|---|---|---|
loginOptions | LoginController::optionsAction | Public |
loginVerify | LoginController::verifyAction | Public |
recoveryVerify | RecoveryController::verifyAction | Public |
recoveryGenerate | RecoveryController::generateAction | Session |
registrationOptions | ManagementController::regOptions | Session |
registrationVerify | ManagementController::regVerify | Session |
manageList | ManagementController::listAction | Session |
manageRename | ManagementController::renameAction | Session |
manageRemove | ManagementController::removeAction | Session |
enrollmentStatus | EnrollmentController::statusAction | Session |
enrollmentSkip | EnrollmentController::skipAction | Session |
Error responses
All error responses follow this format:
{
"error": "Human-readable error message"
}
Common HTTP status codes:
| Code | Meaning |
|---|---|
| 400 | Invalid request (missing/malformed fields) |
| 401 | Not authenticated (session required) |
| 403 | Forbidden (insufficient privileges) |
| 429 | Rate limit exceeded |
| 500 | Internal server error |