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:

  1. eID verification -- JavaScript calls the eID endpoint (loginVerify or recoveryVerify). The server verifies the assertion or recovery code and stores the authenticated fe_user UID in a short-lived cache entry (nr_passkeys_fe_nonce cache, 2-minute TTL). The response includes a loginToken.
  2. felogin form submission -- JavaScript submits a standard logintype=login form to the current page, including the loginToken in a hidden field. TYPO3's normal FE authentication chain processes the request.
  3. Auth service -- PasskeyFrontendAuthenticationService (priority 80) reads the loginToken, looks up the user UID in the cache, and returns the fe_users row. 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
Copied!

Request challenge options for passkey login.

Request:

{
    "username": "johndoe"
}
Copied!

For discoverable login, omit username or send an empty body.

Response (200):

{
    "options": {
        "challenge": "...",
        "rpId": "example.com",
        "allowCredentials": []
    },
    "challengeToken": "..."
}
Copied!

POST /?eID=nr_passkeys_fe&action=loginVerify
Copied!

Verify a passkey assertion and issue a login token.

Request:

{
    "assertion": {
        "id": "...",
        "type": "public-key",
        "response": {
            "clientDataJSON": "...",
            "authenticatorData": "...",
            "signature": "...",
            "userHandle": "..."
        }
    },
    "challengeToken": "..."
}
Copied!

Response (200):

{
    "status": "ok",
    "feUserUid": 42,
    "loginToken": "abc123..."
}
Copied!

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
Copied!

Login using a one-time recovery code.

Request:

{
    "username": "johndoe",
    "code": "XXXX-XXXX"
}
Copied!

Response (200):

{
    "status": "ok",
    "feUserUid": 42,
    "loginToken": "abc123..."
}
Copied!

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
Copied!

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": "..."
}
Copied!

POST /?eID=nr_passkeys_fe&action=registrationVerify
Copied!

Verify an attestation and save the new credential.

Request:

{
    "attestation": {
        "id": "...",
        "type": "public-key",
        "response": {
            "clientDataJSON": "...",
            "attestationObject": "..."
        }
    },
    "challengeToken": "...",
    "name": "My MacBook"
}
Copied!

Response (200):

{
    "status": "ok",
    "credentialId": "..."
}
Copied!

Management endpoints (requires session) 

GET /?eID=nr_passkeys_fe&action=manageList
Copied!

Returns the list of passkeys for the current user.

POST /?eID=nr_passkeys_fe&action=manageRename
Copied!

Request: {"credentialId": "...", "name": "New Name"}

POST /?eID=nr_passkeys_fe&action=manageRemove
Copied!

Request: {"credentialId": "..."}

POST /?eID=nr_passkeys_fe&action=recoveryGenerate
Copied!

Generates a new set of 10 recovery codes. Returns the plaintext codes (shown once only).

Response (200):

{
    "status": "ok",
    "codes": ["XXXX-XXXX", "..."],
    "count": 10
}
Copied!

Enrollment status endpoints (requires session) 

GET /?eID=nr_passkeys_fe&action=enrollmentStatus
Copied!

Returns the current user's enrollment and enforcement status.

POST /?eID=nr_passkeys_fe&action=enrollmentSkip
Copied!

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

Backend AJAX routes 

The admin module calls these routes through TYPO3.settings.ajaxUrls. Each URL carries the route token TYPO3 issues to the backend session, and every action answers 403 unless the backend user is an administrator.

Route Method Parameters
nr_passkeys_fe_admin_list GET feUserUid (query argument)
nr_passkeys_fe_admin_remove POST feUserUid, credentialUid
nr_passkeys_fe_admin_revoke_all POST feUserUid
nr_passkeys_fe_admin_unlock POST feUserUid, username
nr_passkeys_fe_admin_update_enforcement POST groupUid, enforcement
nr_passkeys_fe_admin_reset_grace_period POST feUserUid

nr_passkeys_fe_admin_update_enforcement accepts off, encourage, required and enforced, answers 400 for any other value or a group uid that is not a plain positive integer, 404 for an unknown or deleted group, and 409 while the administrator works in a workspace without live editing (fe_groups is not versioned). It writes fe_groups.passkey_enforcement through DataHandler, so the change appears in the record history and the system log.

nr_passkeys_fe_admin_reset_grace_period sets fe_users.passkey_grace_period_start of the user to 0, so the next request the enrollment interstitial handles under required starts a new grace period. It answers 400 without a feUserUid and 404 for an unknown user.

Error responses 

All error responses follow this format:

{
    "error": "Human-readable error message"
}
Copied!

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