Skip to content

Add email verification before activation#788

Open
faisalahammad wants to merge 2 commits intoWordPress:masterfrom
faisalahammad:fix/778-email-verification-before-activation
Open

Add email verification before activation#788
faisalahammad wants to merge 2 commits intoWordPress:masterfrom
faisalahammad:fix/778-email-verification-before-activation

Conversation

@faisalahammad
Copy link

@faisalahammad faisalahammad commented Feb 13, 2026

Description

This PR implements a verification step for the Email provider in the Two-Factor plugin.

Previously, users could enable Email 2FA without confirming ownership of the email address, which posed a risk of account lockout if the email was incorrect or inaccessible. This change aligns the Email provider's activation flow with the TOTP provider by requiring successful code verification before the provider can be enabled.

Changes

  • User Options UI:
    • The "Email" provider section now displays a "Verify your e-mail address" button for unverified users.
    • Clicking this button initiates an AJAX request to send a verification code.
    • A new input field allows the user to enter the received code.
    • Upon successful verification, the provider is enabled, and the UI updates to show the standard "Enabled" checkbox state.
  • REST API:
    • Added POST /two-factor/1.0/email: Handles sending verification codes and validating them.
    • Added DELETE /two-factor/1.0/email: Handles resetting the verification status (if needed).
  • Verification Logic:
    • Two_Factor_Email::is_available_for_user() now returns true only if the user has verified their email (checked via _two_factor_email_verified user meta).
  • Backwards Compatibility:
    • Users who already have the Email provider enabled are considered "legacy verified" and can continue using it without re-verification.
  • Data Integrity:
    • Added a pre_user_options_update hook to prevent the Email provider from being enabled via the standard profile form save unless the user is verified.

How to Test

New User (Fresh Setup)

  1. Navigate to Users > Profile.
  2. Scroll to the Two-Factor Options section.
  3. Ensure the "Email" option is not enabled.
  4. Click the "Verify your e-mail address" button.
  5. Check your email for a verification code.
  6. Enter the code in the input field and click "Verify".
  7. Observe that the page updates, and the "Email" checkbox is now checked and enabled.

Legacy User (Existing Setup)

  1. Log in as a user who already has Email 2FA enabled.
  2. Navigate to Users > Profile.
  3. Confirm that the "Email" checkbox remains checked and functional.
  4. Verify that no re-verification prompt is shown.

Screenshot

Email TOTP

Technical Details

  • Class: Two_Factor_Email
  • New Methods:
    • register_rest_routes()
    • rest_setup_email()
    • rest_delete_email()
    • pre_user_options_update()
  • Modified Methods:
    • user_options(): updated to render the verification UI.
    • is_available_for_user(): added verification check (with legacy fallback).
    • generate_and_email_token(): updated to accept an $action argument ('login' vs 'verification_setup') to send context-appropriate emails.
  • New Constants:
    • VERIFIED_META_KEY: _two_factor_email_verified

Checklist

  • Code follows the WordPress Coding Standards.
  • Unit tests have been added/updated.
  • Verified manual testing of the new flow.
  • Verified backwards compatibility for existing users.

Fixes #778

@github-actions
Copy link

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Co-authored-by: faisalahammad <faisalahammad@git.wordpress.org>
Co-authored-by: masteradhoc <masteradhoc@git.wordpress.org>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

@jeffpaul jeffpaul added this to the 0.16.0 milestone Feb 13, 2026
@jeffpaul jeffpaul requested a review from kasparsd February 13, 2026 16:21
@masteradhoc masteradhoc modified the milestones: 0.16.0, 0.17.0 Mar 2, 2026
Copy link
Collaborator

@georgestephanis georgestephanis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I want to see this go in, but I think to avoid merge conflicts it'll need to pause until #814 goes in, or that will need to pause for this. Or this can just start doing the newer external include. Either way.

Like the idea, but there's some extra changes in the PR that I don't think need to be in this PR? I'm looking at the distignore, and I'm not sure why it's changing from protected to public for the constructor ... possibly totally reasonable, I'm just trying to be thorough.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds an email-verification step for the Email two-factor provider by introducing a “verified” user-meta flag, a REST-driven verification flow in the profile UI, and guardrails to prevent enabling Email 2FA unless verified (with legacy compatibility).

Changes:

  • Add VERIFIED_META_KEY and gate is_available_for_user() on verification (while allowing legacy-enabled users).
  • Introduce Email provider REST endpoints to send/verify codes and to deactivate/reset verification state.
  • Expand unit tests for email contents, availability gating, and profile-save behavior; update .distignore.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 5 comments.

File Description
providers/class-two-factor-email.php Adds verification meta key, REST endpoints, updated email content handling, UI changes, and profile-save enforcement.
tests/providers/class-two-factor-email.php Adds/updates tests for verification-context emails, availability rules, and pre_user_options_update() behavior.
.distignore Ignores two-factor.zip from distribution exports.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Copy link
Collaborator

@masteradhoc masteradhoc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @faisalahammad
Thanks for your PR :) as we've just merged #814 would you mind seperating your code as well to the seperate files that the PR added?

@faisalahammad faisalahammad force-pushed the fix/778-email-verification-before-activation branch from abc10ef to 2515e10 Compare March 19, 2026 07:58
@faisalahammad
Copy link
Author

I have updated the PR to address all the feedback:

  • Rebased on master: Aligned the branch with the recent PR Move inline JS to external script files #814 merge.
  • Code Separation: Moved the newly added inline JS for the Email provider into providers/js/email-admin.js and enqueued it properly, maintaining consistency with the new architecture.
  • Constructor Visibility: Reverted Two_Factor_Email::__construct() back to protected to preserve the singleton pattern.
  • .distignore: Reverted the exclusion of two-factor.zip.
  • PHPCS / Nonce: Added a phpcs:ignore for $_POST manipulation in pre_user_options_update(), as nonce validation is handled by core on the profile page.
  • Translators Comments: Fixed the duplicate translater comment block during the rebase.
  • Test State Leak: Fixed the leakage of $_SERVER['REMOTE_ADDR'] by restoring variables at the end of the test.
  • REST API Tests: Added comprehensive REST API tests covering permissions, empty code, invalid code, successful verification, and deleting setup in tests/providers/class-two-factor-email-rest-api.php.

Ready for another review!

@masteradhoc
Copy link
Collaborator

@faisalahammad could you please:

  • update your branch to get the latest changes from two-factor?
  • remove your changes from readme.txt and readme.md
  • fix the lint errors noted here and here?

this would make it a lot easier to review the PR properly :)

@faisalahammad faisalahammad force-pushed the fix/778-email-verification-before-activation branch from 4bab1d9 to 412d2fe Compare March 20, 2026 06:17
@faisalahammad
Copy link
Author

Hi @masteradhoc,

I’ve updated the branch based on your feedback:

  1. Rebased: The branch is now rebased on the latest upstream/master.
  2. Reverted Readme/Package: I’ve removed all changes to readme.md, readme.txt, and package.json to keep this strictly focused on email verification.
  3. Fixed PHPStan item: Added safety checks in class-two-factor-core.php so is_wp_error() catches $available_providers correctly before conducting diff operations.

Could you please check and let me know if everything good to merge?

image

Implements a verification step for the Email provider. Users must verify their
email address before the Email 2FA method can be enabled. Legacy users who
already have Email 2FA enabled are unaffected.

Changes:
- Add REST API endpoints for email verification (POST/DELETE /two-factor/1.0/email)
- Add VERIFIED_META_KEY to track verified email addresses
- Update is_available_for_user() to require verification (with legacy fallback)
- Add pre_user_options_update() to prevent enabling without verification
- Add email-admin.js for verification UI interactions
- Add is_wp_error() guards for get_available_providers_for_user() calls
- Add comprehensive REST API and unit tests

Fixes WordPress#778
@faisalahammad faisalahammad force-pushed the fix/778-email-verification-before-activation branch from 412d2fe to a0f6973 Compare March 20, 2026 06:23
@faisalahammad
Copy link
Author

Hi @masteradhoc,

Thanks for the feedback! I've updated the PR with the following changes:

  1. Rebased: The branch is now completely rebased onto the latest two-factor/master, dropping unrelated edits.
  2. Reverted Readme/Package changes: I’ve removed all changes to readme.md, readme.txt, and package.json to keep this strictly focused on the email verification feature.
  3. Fixed errors & Linting: Fixed the PHPStan error noted by reviewing the is_wp_error() checks in class-two-factor-core.php. Also reverted the package.json JS lint scope changes, so the pre-existing JS lint errors from upstream are no longer failing the CI build here.

Everything should be green now. Could you please re-review when you have a chance?

Copy link
Collaborator

@masteradhoc masteradhoc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

early feedback. @nimesh-xecurify could you check the tests added in this PR if they are fine?

/**
* Enqueue scripts for email provider.
*
* @since 0.10.0
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* @since 0.10.0
* @since 0.16.0

as this is a new function, use proper version number
if we can get this ready it will be in 0.16.0 else in 0.17.0

- Fix enqueue_assets() @SInCE version: 0.10.0 → 0.16.0
- Add missing @SInCE 0.16.0 to register_rest_routes()
- Add missing @SInCE 0.16.0 to rest_setup_email()
- Add missing @SInCE 0.16.0 to rest_delete_email()
- Add missing @SInCE 0.16.0 to pre_user_options_update()
- Fix test_user_two_factor_rest_setup_email_valid_code: replace undefined is_provider_enabled_for_user() with in_array check
- Fix test_user_can_delete_email_verification: set verified meta before enabling provider
- Fix test_admin_can_delete_email_for_others: add enable_provider_for_user call
- Fix test_generate_and_email_token_login_context_correct_args: match assertions to actual email body text
- Fix test_other_sessions_destroyed_when_enabling_2fa: add verified meta before enabling Email 2FA
- Fix test_user_options (backup codes): update assertion for wp_scripts data
@faisalahammad
Copy link
Author

faisalahammad commented Mar 20, 2026

Hi @masteradhoc,

Thanks for the thorough review! I've pushed a fix addressing all your feedback:

@ since tag fixes:

  • enqueue_assets(): Changed @since 0.10.0@since 0.16.0
  • register_rest_routes(): Added @since 0.16.0
  • rest_setup_email(): Added @since 0.16.0
  • rest_delete_email(): Added @since 0.16.0
  • pre_user_options_update(): Added @since 0.16.0

CI test failures fixed:

  • test_user_two_factor_rest_setup_email_valid_code: Replaced undefined is_provider_enabled_for_user() with in_array() check
  • test_user_can_delete_email_verification: Fixed setup order (verified meta before enabling provider)
  • test_admin_can_delete_email_for_others: Added missing enable_provider_for_user() call
  • test_generate_and_email_token_login_context_correct_args: Updated assertions to match actual email body text
  • test_other_sessions_destroyed_when_enabling_2fa: Added verified meta before enabling Email 2FA
  • test_user_options (backup codes): Updated assertion to check wp_scripts() localized data

Could you please re-review when you get a chance?

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.

Require verification before activating Email TOTP

5 participants