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

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