Skip to content

Add passkey 2FA providers for TwoFactorAuth#3

Draft
rhoerr wants to merge 13 commits intomainfrom
feature/admin-tfa-passkey
Draft

Add passkey 2FA providers for TwoFactorAuth#3
rhoerr wants to merge 13 commits intomainfrom
feature/admin-tfa-passkey

Conversation

@rhoerr
Copy link
Copy Markdown
Contributor

@rhoerr rhoerr commented Apr 7, 2026

Summary

  • Adds two new TFA providers (passkey and passkey_hardware) to Magento's TwoFactorAuth module, built entirely within MageOS_PasskeyAuth
  • Both providers share a single Engine class differentiated by authenticator policy (all vs hardware-only via authenticatorAttachment: cross-platform)
  • Admin WebAuthn ceremonies reuse existing SerializerFactory, CeremonyStepManagerProvider, and ChallengeManager via DI virtualType with admin-specific origins
  • Credentials stored in TFA's tfa_user_config table (single credential per admin user)
  • Domain mismatch detection prevents silent failures when admin URL changes — clear error message + security:tfa:passkey:reset-all CLI for bulk recovery
  • userVerification: required hardcoded for all admin passkey auth (biometric/PIN enforced)

Architecture

Shared services (no modification): SerializerFactory, ChallengeManager
Refactored: CeremonyStepManagerProvider now type-hints WebAuthnConfigInterface (extracted from Config) — admin virtualType injects AdminTfaConfig for admin-domain origins
New: Engine, Configure, Authenticate services under Model/AdminTfa/; 4 admin controllers; KnockoutJS frontend reusing passkey-core.js (moved to view/base/)

Security properties

Property Mechanism
Phishing resistance WebAuthn RP ID binding
Replay protection Single-use challenge tokens (5min TTL)
No shared secrets Only public key stored server-side
User verification userVerification: required in both registration and auth
Clone detection Sign counter regression logged
Hardware-only enforcement authenticatorAttachment: cross-platform on hardware provider
Domain change safety RP ID stored at registration; mismatch detected before ceremony

Test plan

  • Enable passkey provider in TFA settings, verify configure screen renders
  • Register a platform passkey (Touch ID / Windows Hello), verify activation succeeds
  • Log out and back in, verify passkey auth screen auto-triggers and grants access
  • Enable passkey_hardware provider, verify only hardware keys are offered (no platform authenticator)
  • Change admin base URL, verify auth attempt shows domain mismatch error
  • Run bin/magento security:tfa:passkey:reset-all and verify credentials are cleared
  • Verify existing security:tfa:reset <user> passkey works for single-user reset
  • Verify U2fKey provider continues to work independently
  • Run unit tests: 163 pass, 0 failures

Spec & plan

  • Spec: docs/superpowers/specs/2026-04-06-passkey-tfa-provider-design.md
  • Plan: docs/superpowers/plans/2026-04-06-passkey-tfa-provider.md

🤖 Generated with Claude Code

rhoerr and others added 13 commits April 6, 2026 23:32
Prepare for admin TFA passkey support by extracting a shared interface
from Config so CeremonyStepManagerProvider can work with both storefront
and admin configurations via DI virtualType.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…terface

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…pport

AdminTfaConfig implements WebAuthnConfigInterface using the admin store
base URL to derive RP ID and allowed origins, with 'required' user
verification and policy-driven hardware key enforcement. OriginValidator
guards ceremonies against admin domain changes since passkey registration.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implements Magento\TwoFactorAuth\Api\EngineInterface with config
validation, origin checking via OriginValidator, and delegation to
Authenticate for WebAuthn assertion verification. Includes placeholder
Authenticate stub (Task 5 will provide the full implementation) and
full unit test coverage.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds a CLI command for bulk-resetting passkey TFA credentials for all
admin users, supporting a --force flag to skip the confirmation prompt.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Temporary icons copied from U2F provider. Replace with proper passkey
icons before release.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Configure/ConfigurePost: fix parent::__construct() to pass
  ($context, $tokenVerifier) matching AbstractConfigureAction's
  actual signature, not ($context, $session, $tfa)
- Configure/ConfigurePost: rename isAllowed() to _isAllowed()
  so Magento's dispatch mechanism actually calls it
- Auth/AuthPost: extend AbstractAction (TFA's base with 403
  handling) instead of Action directly
- Engine: type-hint AuthenticateInterface instead of concrete
  Authenticate class

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Move passkey-core.js from view/frontend/web/js/ to view/base/web/js/
  so it resolves in both frontend and adminhtml areas via Magento's
  RequireJS module resolution. Frontend JS is NOT available in admin.
- Remove redundant adminhtml requirejs-config.js (self-mapping no-op).
- Fix configure.phtml and auth.phtml: merge layout jsLayout with
  controller config in a single x-magento-init block instead of
  double-initializing via data-mage-init + script tag.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant