Every gem that adds 2FA support to Devise (devise-webauthn, devise-otp, devise-two-factor, etc.) independently solves the same set of problems:
- Redirecting to a 2FA challenge page after password validation succeeds
- Preserving
remember_me across the two-step flow
- Preventing 2FA bypass through password reset (
PasswordsController#update calls sign_in directly)
Because each gem solves these independently – and with fundamentally different architectural choices (e.g. devise-two-factor replaces DatabaseAuthenticatable with a strategy that validates password and the OTP code, while devise-webauthn runs a separate Warden strategy) – there is no shared contract between them. An application that wants to offer users a choice between two second factors (e.g. WebAuthn and TOTP) cannot simply install two 2FA gems and have them cooperate out of the box.
I believe the Devise ecosystem would benefit from a standardized interface that extension gems can hook into instead of reimplementing the same patterns independently.
Every gem that adds 2FA support to Devise (
devise-webauthn,devise-otp,devise-two-factor, etc.) independently solves the same set of problems:remember_meacross the two-step flowPasswordsController#updatecallssign_indirectly)Because each gem solves these independently – and with fundamentally different architectural choices (e.g.
devise-two-factorreplacesDatabaseAuthenticatablewith a strategy that validates password and the OTP code, whiledevise-webauthnruns a separate Warden strategy) – there is no shared contract between them. An application that wants to offer users a choice between two second factors (e.g. WebAuthn and TOTP) cannot simply install two 2FA gems and have them cooperate out of the box.I believe the Devise ecosystem would benefit from a standardized interface that extension gems can hook into instead of reimplementing the same patterns independently.