-
-
Notifications
You must be signed in to change notification settings - Fork 738
Description
What problem did you meet?
When users set up passkeys or an authenticator app as MFA, they have no practical fallback if something goes wrong:
Passkey problem β new device login:
A user registers a platform passkey (e.g., Windows Hello) on their laptop. Later they try to log in from their phone or a new computer. The passkey doesn't exist on that device. Logto demands MFA verification but the only configured factor is the passkey they can't use. They're locked out.
Authenticator app problem β lost device:
A user sets up TOTP with Google Authenticator on their phone. Their phone breaks, gets stolen, or they switch to a new phone. The authenticator app and its secrets are gone. Logto demands the TOTP code they can no longer generate. They're locked out.
The natural fallback is email verification β the user still has access to their email, and email is already verified as their sign-in identifier. But there's no way to enable email verification code as an MFA option for just these users.
Currently:
Enabling EmailVerificationCode in mfa.factors activates it globally for ALL users since every user has an email β even users who never opted into MFA at all
The Management API (POST /api/users/{userId}/mfa-verifications) only accepts Totp and BackupCode β not EmailVerificationCode
The only available fallback is backup codes, which require users to save 10 codes somewhere safe β most regular users won't do this, and it's a poor experience for non-technical owners (our user base)
Workarounds we tested (none worked):
We tried enabling EmailVerificationCode in mfa.factors with policy: "UserControlled", expecting that only users who have explicitly set up TOTP or WebAuthn would be prompted for MFA (with email as an additional option). However, Logto treats EmailVerificationCode as implicitly available for every user who has an email β which is all of them. The result is that ALL users are prompted for email verification on every login, regardless of whether they have opted into MFA.
This confirms that UserControlled policy only controls whether MFA is required, not which factors are available. Since email doesn't require explicit enrollment (unlike TOTP which requires scanning a QR code), it's automatically active for everyone the moment it's added to the global factors list.
Describe what you'd like Logto to have
Allow EmailVerificationCode as a valid type in POST /api/users/{userId}/mfa-verifications so that email MFA can be bound per-user via the Management API.
This would let us:
User sets up TOTP or passkeys via Account Center
We detect this and call the Management API to also enable email verification code MFA for that specific user
On next login, user sees their MFA options: authenticator app, passkey, and email verification code
If they lose their device or log in from a new machine, they pick email verification β problem solved
Users who never set up MFA are completely unaffected β no email prompt for them
This extends the same per-user binding capability that already exists for TOTP and BackupCode to EmailVerificationCode. No new infrastructure needed β just accepting an additional type in the existing endpoint.
Self-hosted OSS v1.37.1. Related to #7784.