Deployment Scenarios 

Passkeys are bound to a specific domain (the Relying Party ID). This chapter explains how to configure the extension across different environments and how to handle common deployment patterns.

Single environment 

The simplest setup: one TYPO3 instance with one domain.

Leave rpId and origin empty (the default). The extension auto-detects both values from the incoming HTTP request. Each passkey is registered against the domain it was created on.

This works for:

  • A single production instance (e.g. cms.example.com)
  • A local DDEV site (e.g. mysite.ddev.site)

No additional configuration is needed.

Multi-environment (local / staging / production) 

A typical setup has three environments:

  • Local development: mysite.ddev.site (or mysite.local)
  • Staging: staging.example.com
  • Production: www.example.com

Environment-specific configuration 

Use TYPO3_CONTEXT to apply different settings per environment:

config/system/additional.php
// Production and Staging: enforce passkey-only login
if (str_starts_with((string)getenv('TYPO3_CONTEXT'), 'Production')) {
    $GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['nr_passkeys_be']['disablePasswordLogin'] = '1';
}

// Development: keep password login available for convenience
// (disablePasswordLogin defaults to '0', no override needed)
Copied!

This lets you enforce passkey-only login on production while keeping password login available locally for new users or quick access.

Database synchronisation 

When syncing the production database to staging or local (a common workflow), the passkey credential table will contain credentials bound to the production domain. These credentials will not work on a different domain.

Exclude the credential table from database syncs:

Example: exclude from mysqldump
mysqldump --ignore-table=mydb.tx_nrpasskeysbe_credential mydb > dump.sql
Copied!
Example: exclude in DDEV (via mysql-sync-db custom command)
ignore_tables:
  - tx_nrpasskeysbe_credential
Copied!

After importing a production database dump, users simply register fresh passkeys on the local or staging environment. If disablePasswordLogin is active but environment-specific (see above), password login is available on non-production environments for this initial registration.

Shared rpId across subdomains 

WebAuthn allows the rpId to be set to a registrable domain suffix. For example, setting rpId to example.com allows passkeys registered on staging.example.com to also work on www.example.com.

If you still need shared subdomains (e.g. staging and www), set rpId only on those environments and keep local development on auto-detect:

config/system/additional.php
if (str_starts_with((string)getenv('TYPO3_CONTEXT'), 'Production')) {
    $GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['nr_passkeys_be']['rpId'] = 'example.com';
    // Leave 'origin' empty -- it is auto-detected per subdomain.
    // Setting it explicitly would break verification on other subdomains.
}
// Development: rpId stays empty -> auto-detect -> mysite.ddev.site
Copied!

User onboarding 

Onboarding workflow with disablePasswordLogin 

When disablePasswordLogin is enabled, the extension enforces passkey-only login per user: password login is blocked only for users who have at least one registered passkey. Users without passkeys can still log in with a password.

This enables a smooth onboarding workflow:

  1. Admin creates a new backend user with a password (as usual in TYPO3).
  2. User logs in with their password for the first time.
  3. User registers a passkey in User Settings > Passkeys.
  4. From this point on, the user must use their passkey -- password login is no longer accepted for this account.

Recovery scenarios 

If a user loses access to their authenticator:

  1. An admin revokes the user's passkeys via the Admin API. Each revocation is recorded with the admin's UID and timestamp for audit purposes.
  2. Once all passkeys are revoked, password login becomes available again for that user (the per-user enforcement lifts when no active credentials remain).
  3. The user logs in with their password and registers a new passkey.

Containerized and multi-server deployments 

When running TYPO3 in Docker containers or behind a load balancer, the file-based cache backends lose state on container restart and are not shared across servers. This affects nonce replay protection and rate limiting.

See Multi-server cache backends for Redis configuration, and Reverse proxy and IP detection for rate limiting behind a load balancer.

Local development with DDEV 

DDEV sites (*.ddev.site) use HTTPS by default and are treated as secure contexts by browsers. Passkeys work out of the box.

ddev start
# Open https://mysite.ddev.site/typo3 -- passkeys work immediately
Copied!

For http://localhost (without HTTPS), most browsers also treat this as a secure context, so passkeys will work. However, custom local domains over plain HTTP (e.g. http://mysite.local) will not work -- WebAuthn requires a secure context.

See also Troubleshooting: HTTPS requirement.