PluginProbe ʕ •ᴥ•ʔ
WP 2FA – Two-factor authentication for WordPress / 4.0.0
WP 2FA – Two-factor authentication for WordPress v4.0.0
4.0.0 1.7.1 2.0.0 2.0.1 2.1.0 2.2.0 2.2.1 2.3.0 2.4.0 2.4.1 2.4.2 2.5.0 2.6.0 2.6.1 2.6.2 2.6.3 2.6.4 2.7.0 2.8.0 2.9.0 2.9.1 2.9.2 2.9.3 3.0.0 3.0.1 3.1.0 3.1.1 3.1.1.2 trunk 1.2.0 1.3.0 1.4.0 1.4.1 1.4.2 1.5.0 1.5.1 1.5.2 1.6.0 1.6.1 1.6.2 1.7.0
wp-2fa / includes / classes / Admin / Methods / class-email.php
wp-2fa / includes / classes / Admin / Methods Last commit date
Traits 1 day ago passkeys 1 day ago class-backup-codes.php 1 day ago class-email-wizard-steps.php 1 day ago class-email.php 1 day ago class-totp-wizard-steps.php 1 day ago class-totp.php 1 day ago index.php 1 day ago
class-email.php
541 lines
1 <?php
2 /**
3 * Responsible for WP2FA user's email method manipulation.
4 *
5 * @package wp2fa
6 * @subpackage methods
7 *
8 * @copyright 2026 Melapress
9 * @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
10 *
11 * @see https://wordpress.org/plugins/wp-2fa/
12 *
13 * @since 2.6.0
14 */
15
16 declare(strict_types=1);
17
18 namespace WP2FA\Methods;
19
20 use WP2FA\WP2FA;
21 use WP2FA\Admin\Setup_Wizard;
22 use WP2FA\Utils\Settings_Utils;
23 use WP2FA\Admin\Settings_Builder;
24 use WP2FA\Admin\Helpers\User_Helper;
25 use WP2FA\Admin\Controllers\Settings;
26 use WP2FA\Authenticator\Authentication;
27 use WP2FA\Admin\Methods\Traits\Providers;
28 use WP2FA\Admin\Controllers\API\API_Login;
29 use WP2FA\Admin\Methods\Traits\WhiteLabel;
30 use WP2FA\Methods\Wizards\Email_Wizard_Steps;
31 use WP2FA\Admin\Methods\Traits\Settings_Trait;
32 use WP2FA\Admin\SettingsPages\Settings_Page_White_Label;
33
34 /**
35 * Class for handling email codes.
36 *
37 * @since 2.6.0
38 *
39 * @package WP2FA
40 */
41 if ( ! class_exists( '\WP2FA\Methods\Email' ) ) {
42 /**
43 * Email code class, for handling email method code generation and such.
44 *
45 * @since 2.6.0
46 */
47 class Email {
48
49 use Providers;
50 use WhiteLabel;
51 use Settings_Trait;
52
53 /**
54 * The name of the method.
55 *
56 * @var string
57 *
58 * @since 2.6.0
59 */
60 public const METHOD_NAME = 'email';
61
62 /**
63 * The internal name of the method.
64 *
65 * @var string
66 *
67 * @since 2.5.0
68 */
69 public const METHOD_INTERNAL_NAME = self::METHOD_NAME;
70
71 /**
72 * The name of the method stored in the policy
73 *
74 * @var string
75 *
76 * @since 2.6.0
77 */
78 public const POLICY_SETTINGS_NAME = 'enable_' . self::METHOD_NAME;
79
80 /**
81 * Is the mail enabled
82 *
83 * @since 2.6.0
84 *
85 * @var bool
86 */
87 private static $enabled = null;
88
89 /**
90 * Is the mail enforced
91 *
92 * @since 3.0.0
93 *
94 * @var bool
95 */
96 private static $email_enforced = null;
97
98 /**
99 * Inits the class and sets the filters.
100 *
101 * @return void
102 *
103 * @since 2.6.0
104 */
105 public static function init() {
106
107 self::always_init();
108
109 \add_filter( WP_2FA_PREFIX . 'providers_translated_names', array( __CLASS__, 'provider_name_translated' ) );
110
111 \add_filter( WP_2FA_PREFIX . 'default_settings', array( __CLASS__, 'add_default_settings' ) );
112
113 \add_filter( WP_2FA_PREFIX . 'loop_settings', array( __CLASS__, 'settings_loop' ), 10, 1 );
114
115 \add_filter( WP_2FA_PREFIX . 'no_method_enabled', array( __CLASS__, 'return_default_selection' ), 10, 1 );
116
117 // add the HOTP methods to the list of available methods if enabled.
118 \add_filter(
119 WP_2FA_PREFIX . 'available_2fa_methods',
120 function ( $available_methods, $role ) {
121 if ( ! empty( Settings_Utils::get_setting_role( $role, self::POLICY_SETTINGS_NAME ) ) ) {
122 array_push( $available_methods, self::METHOD_NAME );
123 }
124
125 return $available_methods;
126 },
127 10,
128 2
129 );
130
131 // \add_action( WP_2FA_PREFIX . 'before_settings_save_roles', array( __CLASS__, 'set_email_settings_when_enforced' ) );
132
133 // \add_action( WP_2FA_PREFIX . 'before_settings_save', array( __CLASS__, 'set_email_settings_when_enforced' ) );
134
135 \add_filter( WP_2FA_PREFIX . 'white_label_default_settings', array( __CLASS__, 'add_whitelabel_settings' ) );
136
137 \add_action( WP_2FA_PREFIX . 'validate_login_api', array( __CLASS__, 'api_login_validate' ), 10, 3 );
138
139 \add_action( WP_2FA_PREFIX . 'white_label_wizard_options', array( __CLASS__, 'white_label_option_labels' ) );
140
141 Email_Wizard_Steps::init();
142
143 \add_filter( WP_2FA_PREFIX . 'providers_settings_labels', array( __CLASS__, 'provider_settings_labels' ) );
144 }
145
146 /**
147 * Adds the OOB settings in the white label default settings array
148 *
149 * @param array $labels - array with white label default settings.
150 *
151 * @return array
152 *
153 * @since 3.1.1.2
154 */
155 public static function provider_settings_labels( array $labels ): array {
156 \ob_start();
157 ?>
158 <div class="form-group settings-row">
159 <?php
160 Settings_Builder::build_option(
161 array(
162 'text' => \esc_html__( 'Email option label', 'wp-2fa' ),
163 'type' => 'settings-label',
164 )
165 );
166 ?>
167 <div class="settings-control ">
168 <?php
169 Settings_Builder::build_option(
170 array(
171 'id' => 'email-option-label',
172 'type' => 'text',
173 'placeholder' => \esc_html__( 'Provider\'s name', 'wp-2fa' ),
174 'class' => 'form-input',
175 'option_name' => 'wp_2fa_white_label[email-option-label]',
176 'default' => \esc_html( WP2FA::get_wp2fa_white_label_setting( 'email-option-label', true ) ),
177 )
178 );
179 ?>
180 </div>
181 </div>
182 <div class="form-group settings-row">
183 <div class="settings-label-group">
184 <?php
185 Settings_Builder::build_option(
186 array(
187 'text' => \esc_html__( 'Email option hint', 'wp-2fa' ),
188 'type' => 'settings-label',
189 )
190 );
191
192 Settings_Builder::build_option(
193 array(
194 'text' => \esc_html__( 'Add a helpful tip that users will see when configuring Email (HOTP) as their 2FA method. This text is displayed via a help icon during setup. Leave this field empty to hide the help icon.', 'wp-2fa' ),
195 'class' => 'description-settings-card',
196 'id' => 'general-settings-tab',
197 'type' => 'description',
198 )
199 );
200 ?>
201 </div>
202 <div class="settings-control ">
203 <?php
204 Settings_Builder::build_option(
205 array(
206 'id' => 'email-option-label-hint',
207 'type' => 'editor',
208 'placeholder' => \esc_html__( 'Enter custom message', 'wp-2fa' ),
209 'option_name' => 'wp_2fa_white_label[email-option-label-hint]',
210 'default' => WP2FA::get_wp2fa_white_label_setting( 'email-option-label-hint', true ),
211 )
212 );
213 ?>
214 </div>
215 </div>
216 <div class="form-group settings-row">
217 <div class="settings-label-group">
218 <?php
219 Settings_Builder::build_option(
220 array(
221 'text' => \esc_html(
222 \wp_sprintf(
223 // translators: Method option label.
224 __( '%s help text', 'wp-2fa' ),
225 WP2FA::get_wp2fa_white_label_setting( 'email-option-label', true )
226 )
227 ),
228 'type' => 'settings-label',
229 )
230 );
231
232 Settings_Builder::build_option(
233 array(
234 'text' => \wp_sprintf(
235 // translators: Method option label.
236 \esc_html__( 'This message is shown to users when configuring %s.', 'wp-2fa' ),
237 WP2FA::get_wp2fa_white_label_setting( 'email-option-label', true )
238 ),
239 'class' => 'description-settings-card',
240 'id' => 'general-settings-tab',
241 'type' => 'description',
242 )
243 );
244 ?>
245 </div>
246 <div class="settings-control">
247 <?php
248 Settings_Builder::build_option(
249 array(
250 'id' => 'method_help_hotp_intro',
251 'type' => 'editor',
252 'placeholder' => \esc_html__( 'Enter custom message', 'wp-2fa' ),
253 'option_name' => 'wp_2fa_white_label[method_help_hotp_intro]',
254 'default' => WP2FA::get_wp2fa_white_label_setting( 'method_help_hotp_intro', true ),
255 )
256 );
257 ?>
258 </div>
259 </div>
260 <div class="form-group settings-row">
261 <div class="settings-label-group">
262 <?php
263 Settings_Builder::build_option(
264 array(
265 'text' => \esc_html__( 'Link via email help text', 'wp-2fa' ),
266 'type' => 'settings-label',
267 )
268 );
269
270 Settings_Builder::build_option(
271 array(
272 'text' => \esc_html__( 'This message is shown to users in the final step before email verification when using the "Link via email" method. It should explain that they need to check their email and, if required, whitelist the sender address.', 'wp-2fa' ),
273 'class' => 'description-settings-card',
274 'id' => 'method-help-hotp-help-desc',
275 'type' => 'description',
276 )
277 );
278 ?>
279 </div>
280 <div class="settings-control">
281 <?php
282 Settings_Builder::build_option(
283 array(
284 'id' => 'method_help_hotp_help',
285 'type' => 'editor',
286 'placeholder' => \esc_html__( 'Enter custom message', 'wp-2fa' ),
287 'option_name' => 'wp_2fa_white_label[method_help_hotp_help]',
288 'default' => WP2FA::get_wp2fa_white_label_setting( 'method_help_hotp_help', true ),
289 )
290 );
291 ?>
292 </div>
293 </div>
294 <div class="form-group settings-row">
295 <div class="settings-label-group">
296 <?php
297 Settings_Builder::build_option(
298 array(
299 'text' => \esc_html__( 'One-time code via email help text', 'wp-2fa' ),
300 'type' => 'settings-label',
301 )
302 );
303
304 Settings_Builder::build_option(
305 array(
306 'text' => \esc_html__( 'This message is shown to users in the final step before email verification when using the "Code via email (HOTP)" method. It should explain that they need to check their email and, if required, whitelist the sender address.', 'wp-2fa' ),
307 'class' => 'description-settings-card',
308 'id' => 'method-help-hotp-help-email-desc',
309 'type' => 'description',
310 )
311 );
312 ?>
313 </div>
314 <div class="settings-control">
315 <?php
316 Settings_Builder::build_option(
317 array(
318 'id' => 'method_help_hotp_help_email',
319 'type' => 'editor',
320 'placeholder' => \esc_html__( 'Enter custom message', 'wp-2fa' ),
321 'option_name' => 'wp_2fa_white_label[method_help_hotp_help_email]',
322 'default' => WP2FA::get_wp2fa_white_label_setting( 'method_help_hotp_help_email', true ),
323 )
324 );
325 ?>
326 </div>
327 </div>
328 <div class="form-group settings-row">
329 <div class="settings-label-group">
330 <?php
331 Settings_Builder::build_option(
332 array(
333 'text' => \esc_html(
334 \wp_sprintf(
335 // translators: Method option label.
336 __( '%s pre-submission text', 'wp-2fa' ),
337 WP2FA::get_wp2fa_white_label_setting( 'email-option-label', true )
338 )
339 ),
340 'type' => 'settings-label',
341 )
342 );
343
344 Settings_Builder::build_option(
345 array(
346 'text' => \esc_html(
347 \wp_sprintf(
348 // translators: Method option label.
349 \esc_html__( 'This message is shown to users prior to configuring %s method.', 'wp-2fa' ),
350 WP2FA::get_wp2fa_white_label_setting( 'email-option-label', true )
351 )
352 ),
353 'class' => 'description-settings-card',
354 'id' => 'general-settings-tab',
355 'type' => 'description',
356 )
357 );
358 ?>
359 </div>
360 <div class="settings-control">
361 <?php
362 Settings_Builder::build_option(
363 array(
364 'id' => 'method_verification_hotp_pre',
365 'type' => 'editor',
366 'placeholder' => \esc_html__( 'Enter custom message', 'wp-2fa' ),
367 'option_name' => 'wp_2fa_white_label[method_verification_hotp_pre]',
368 'default' => WP2FA::get_wp2fa_white_label_setting( 'method_verification_hotp_pre', true ),
369 )
370 );
371 ?>
372 </div>
373 </div>
374
375
376 <?php
377 $content = \ob_get_clean();
378
379 $labels[ self::METHOD_NAME ] = array(
380 'provider_name' => \esc_html( WP2FA::get_wp2fa_white_label_setting( 'email-option-label', true ) ),
381 'content' => $content,
382 );
383
384 return $labels;
385 }
386
387 /**
388 * Checks the provided user and token and validates them. Returns true if valid, false otherwise.
389 *
390 * @param array $valid - The current validation value.
391 * @param integer $user_id - The user ID to check for.
392 * @param string|null $token - The token to validate against user provided.
393 *
394 * @return array
395 *
396 * @since 3.0.0
397 */
398 public static function api_login_validate( array $valid, int $user_id, ?string $token ): array {
399
400 if ( ! Settings::is_provider_enabled_for_role( User_Helper::get_user_role( $user_id ), self::METHOD_NAME ) ) {
401 return $valid;
402 }
403
404 if ( self::METHOD_NAME !== User_Helper::get_enabled_method_for_user( $user_id ) ) {
405 return $valid;
406 }
407
408 if ( ! is_array( $valid ) || ! isset( $valid['valid'] ) ) {
409 $valid['valid'] = false;
410 }
411
412 // If the login is valid, return it as it is.
413 if ( true === $valid['valid'] ) {
414 return $valid;
415 }
416
417 if ( ! isset( $token ) || empty( $token ) ) {
418 return $valid;
419 }
420
421 // Sanitize the token to ensure it is safe to use.
422 $sanitized_token = \sanitize_text_field( $token );
423
424 $is_valid = Authentication::validate_token( User_Helper::get_user( $user_id ), $sanitized_token );
425
426 if ( ! $is_valid ) {
427 $valid[ self::METHOD_NAME ]['error'] = \esc_html__( 'ERROR: Invalid verification code.', 'wp-2fa' );
428 if ( API_Login::check_number_of_attempts( User_Helper::get_user( $user_id ) ) ) {
429
430 if ( empty( WP2FA::get_wp2fa_general_setting( 'brute_force_disable' ) ) ) {
431 Setup_Wizard::send_authentication_setup_email( $user_id, 'nominated_email_address' );
432 if ( empty( WP2FA::get_wp2fa_general_setting( 'brute_force_disable' ) ) ) {
433 User_Helper::remove_meta( WP_2FA_PREFIX . 'code_sent' );
434 }
435
436 $valid[ self::METHOD_NAME ]['error'] .= \esc_html__( ' For security reasons you have been sent a new code via email. Please use this new code to log in.', 'wp-2fa' );
437 }
438 }
439 }
440
441 $valid['valid'] = $is_valid;
442
443 return $valid;
444 }
445
446 /**
447 * Returns the translated name of the provider
448 *
449 * @return string
450 *
451 * @since 2.6.0
452 */
453 public static function get_translated_name(): string {
454 return \esc_html__( 'HOTP (Email)', 'wp-2fa' );
455 }
456
457 /**
458 * Adds the extension default settings to the main plugin settings
459 *
460 * @param array $default_settings - array with plugin default settings.
461 *
462 * @return array
463 *
464 * @since 2.6.0
465 */
466 public static function add_default_settings( array $default_settings ) {
467 $default_settings[ self::POLICY_SETTINGS_NAME ] = self::POLICY_SETTINGS_NAME;
468 $default_settings['specify-email_hotp'] = false;
469
470 // $default_settings['enforce-email_hotp'] = false;
471
472 return $default_settings;
473 }
474
475 /**
476 * Add extension settings to the loop array
477 *
478 * @param array $loop_settings - Currently available settings array.
479 *
480 * @return array
481 *
482 * @since 2.6.0
483 */
484 public static function settings_loop( array $loop_settings ) {
485 array_push( $loop_settings, self::POLICY_SETTINGS_NAME );
486 array_push( $loop_settings, 'specify-email_hotp' );
487 // array_push( $loop_settings, 'enforce-email_hotp' );
488
489
490 return $loop_settings;
491 }
492
493 /**
494 * Fills up the White Label settings array with the method defaults.
495 *
496 * @param array $default_settings - The array with the collected white label settings.
497 *
498 * @return array
499 *
500 * @since 3.0.0
501 */
502 public static function add_whitelabel_settings( array $default_settings ): array {
503 $default_settings['email-option-label'] = \__( 'One-time code via email', 'wp-2fa' );
504 $default_settings['email-option-label-hint'] = '';
505 $default_settings['method_help_hotp_intro'] = '<h3>' . \__( 'Setting up HOTP ({email-option-label})', 'wp-2fa' ) . '</h3><p>' . \__( 'Please select the email address where the one-time code should be sent:', 'wp-2fa' ) . '</p>';
506 $default_settings['method_help_hotp_help'] = \__( 'To complete the 2FA configuration you will be sent a one-time code over email, therefore you should have access to the mailbox of this email address. If you do not receive the email with the one-time code please check your spam folder and contact your administrator', 'wp-2fa' );
507 $default_settings['method_help_hotp_help_email'] = '<b>' . \__( 'IMPORTANT', 'wp-2fa' ) . '</b><p>' . \__( 'To ensure you always receive the one-time code whitelist the email address from which the codes are sent. This is {from_email}', 'wp-2fa' ) . '</p>';
508 $default_settings['method_verification_hotp_pre'] = '<h3>' . \__( 'Almost there…', 'wp-2fa' ) . '</h3><p>' . \__( 'Please type in the one-time code sent to your email address to finalize the setup', 'wp-2fa' ) . '</p>';
509 $default_settings['hotp_reconfigure_intro'] = '<h3>' . \__( '{reconfigure_or_configure_capitalized} one-time code over email method', 'wp-2fa' ) . '</h3><p>' . \__( 'Click the below button to {reconfigure_or_configure} the email address where the one-time code should be sent.', 'wp-2fa' ) . '</p>';
510
511 return $default_settings;
512 }
513
514 /**
515 * Shows the Method option label and hint in the White Label settings.
516 *
517 * @return void
518 *
519 * @since 2.9.0
520 */
521 public static function white_label_option_labels() {
522 ?>
523 <strong class="description"><?php \esc_html_e( 'Email option label', 'wp-2fa' ); ?></strong>
524 <br><br>
525 <fieldset>
526 <input type="text" id="email-option-label" name="wp_2fa_white_label[email-option-label]" class="large-text" value="<?php echo \esc_attr( WP2FA::get_wp2fa_white_label_setting( 'email-option-label', true ) ); ?>">
527 </fieldset>
528 <br>
529 <strong class="description"><?php \esc_html_e( 'Email option hint', 'wp-2fa' ); ?></strong>
530 <br>
531 <fieldset>
532 <?php
533 echo Settings_Page_White_Label::create_standard_editor( 'email-option-label-hint' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
534 ?>
535 </fieldset>
536 <br>
537 <?php
538 }
539 }
540 }
541