feat: Passkey (WebAuthn) login (#1111)

* feat(auth): passkey (WebAuthn) login — server endpoints, schema + admin toggle

Add @simplewebauthn/server registration and primary (discoverable) login ceremonies under /api/auth/passkey, a webauthn_credentials + single-use webauthn_challenges schema (migration), the instance-wide passkey_login toggle (default off) enforced before auth by a guard, and require_mfa satisfaction via a verified passkey. RP ID/origin come only from server config (webauthn_rp_id/origins -> APP_URL), never request headers.

* feat(auth): passkey enrolment, login button + admin settings UI

PasskeysSection in account settings (add/rename/remove with a current-password step-up), a 'Sign in with a passkey' button on the login page, the admin enable + RP-ID/origins controls, and a per-user admin reset action.

* i18n(auth): passkey strings across all locales

Add login/settings/admin passkey keys to en and all 19 translated locales.
This commit is contained in:
Maurice
2026-06-05 18:54:13 +02:00
committed by GitHub
parent 247433fb2a
commit a876fb2634
83 changed files with 2421 additions and 8 deletions
+19
View File
@@ -365,5 +365,24 @@ const admin: TranslationStrings = {
'admin.addons.catalog.journey.name': 'Diario di viaggio',
'admin.addons.catalog.journey.description':
'Tracciamento viaggi e diario con check-in, foto e storie quotidiane',
'admin.passkey.title': 'Accesso con passkey',
'admin.passkey.cardHint':
'Consenti agli utenti di accedere con le passkey (WebAuthn). Disattivato per impostazione predefinita.',
'admin.passkey.login': 'Abilita accesso con passkey',
'admin.passkey.loginHint':
'Mostra un\'opzione "Accedi con una passkey" e consenti agli utenti di registrare le passkey nelle loro impostazioni.',
'admin.passkey.notConfigured':
'Nessun dominio WebAuthn è ancora risolto per questa installazione. Imposta APP_URL o il Relying Party ID qui sotto — le passkey restano nascoste fino ad allora.',
'admin.passkey.rpId': 'Relying Party ID (dominio)',
'admin.passkey.rpIdHint':
'Il dominio puro a cui le passkey sono associate, es. trek.example.org. Lascia vuoto per derivarlo da APP_URL. Modificarlo in seguito invalida le passkey esistenti.',
'admin.passkey.origins': 'Origini consentite',
'admin.passkey.originsHint':
'Origini complete separate da virgola, es. https://trek.example.org. Lascia vuoto per usare APP_URL.',
'admin.passkey.reset': 'Reimposta passkey',
'admin.passkey.resetHint':
"Rimuovi tutte le passkey di questo utente (es. in caso di dispositivo smarrito). Potrà comunque accedere con la sua password.",
'admin.passkey.resetConfirm': 'Rimuovere tutte le passkey di {name}?',
'admin.passkey.resetDone': 'Rimosse {count} passkey',
};
export default admin;