# Passkeys / WebAuthn (Zweiter Faktor)

TerraFlow nutzt [jbtronics/2fa-webauthn](https://github.com/jbtronics/2fa-webauthn) zusammen mit Scheb TwoFactorBundle. Admins können **TOTP** und **Passkeys** parallel einrichten; nach dem Passwort wählt die Anmeldeseite die Methode (oder es gibt nur eine aktive).

## Voraussetzungen

- **HTTPS** in Produktion (WebAuthn verlangt eine sichere Origin). Ausnahme: `localhost` / `127.0.0.1` für Entwicklung.
- **RP ID** entspricht standardmäßig dem Hostnamen der Anfrage (`rpID: null` in `config/packages/jbtronics_2fa_webauthn.yaml`). Die aufgerufene Domain muss dazu passen.
- **`localhost` und `127.0.0.1` sind unterschiedliche Origins.** Immer dieselbe URL nutzen, mit der der Passkey registriert wurde. Fehlende `rp.id` in der Server-JSON füllt `/public/js/tfa_webauthn.js` mit `window.location.hostname`, damit der Browser keinen `SecurityError: invalid domain` wirft.

## Konfiguration

- `config/packages/jbtronics_2fa_webauthn.yaml` – u. a. `rpName`, optional feste `rpID`.
- `config/packages/scheb_2fa.yaml` – TOTP bleibt wie bisher; WebAuthn wird über das jbtronics-Bundle registriert.

## Admin-UI

Unter **Profil → 2FA** (`/admin/security/two-factor`):

- Abschnitt **Authenticator-App (TOTP)**: wie bisher einrichten/deaktivieren.
- Abschnitt **Passkeys**: Liste registrierter Geräte, **Passkey hinzufügen** → `/admin/security/two-factor/passkeys/register` (POST mit WebAuthn-Response im Feld `_auth_code`), Entfernen per POST `/admin/security/two-factor/passkeys/{id}/delete`.

TOTP und Passkeys sind unabhängig: beides aus, nur eines, oder beides aktiv (nach dem Passwort wählt Scheb die verfügbare Methode bzw. der Nutzer über Provider-Links).

## Storefront-2FA

Das WebAuthn-Formular nutzt `@TerraFlowCMS/webauthn_2fa.html.twig` und `/js/tfa_webauthn.js` (Theme-Override wie bei TOTP unter `templates/default/`).

Gespeicherte Credentials liegen in der Tabelle `webauthn_key`; `User::getWebauthnKeys()` deserialisiert die JSON-Daten mit derselben WebAuthn-Serializer-Fabrik wie das Login. Die Spalte `credential_id` enthält den Identifier **Base64-kodiert** (`WebauthnKey`), weil WebAuthn-Rohbytes kein gültiges utf8mb4 sind und MySQL sonst `SQLSTATE[HY000]: 1366` wirft.

## Hinweise

- Nach `composer update` kann die Korrektur in `vendor/jbtronics/2fa-webauthn` (Namespace `PSRRequestHelper`) fehlen – bei Autoload-Fehl `use Jbtronics\TFAWebauthn\Services\Helpers\PSRRequestHelper` in `WebauthnAuthenticator.php` prüfen.
- Die Signatur-Prüfung aktualisiert intern den Authenticator-Counter; eine Persistenz des neuen Zählers in der Datenbank ist in der Basis-Integration nicht enthalten (Replay-Schutz kann je nach Bedarf nachgerüstet werden).

## Manuelle Tests

- iPhone/Safari: Passkey über iCloud-Schlüsselbund.
- Hardware-Key (FIDO2).
- Fallback: weiterhin TOTP, wenn beides aktiv ist.
