PluginProbe
The WP Remote WordPress Plugin / 6.76
The WP Remote WordPress Plugin v6.76
6.76 6.72 6.69 6.65 6.62 6.48 6.47 4.87 4.97 5.05 5.09 5.16 5.22 5.24 5.25 5.38 5.41 5.42 5.45 5.47 5.53 5.56 5.65 5.68 5.72 All 54 releases
wpremote / wp_2fa / email_otp_login.php

email_otp_login.php in The WP Remote WordPress Plugin 6.76, at wp_2fa/email_otp_login.php

89 lines 2.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if (!defined('ABSPATH')) exit;
3 if (!class_exists('WPRWP2FAEmailOTPLogin')) :
4
5 /**
6 * Wires email sign-in codes into the login POST.
7 *
8 * Core has already accepted the username and password by the time this runs, and
9 * it does so again on the submission that carries the code, so every step is
10 * behind valid credentials.
11 */
12 class WPRWP2FAEmailOTPLogin {
13 const REJECTED_MESSAGE = 'That sign-in code is not valid. Request a new code and try again.';
14 const UNAVAILABLE_MESSAGE = 'A sign-in code could not be prepared. Please try again later.';
15 const DELIVERY_MESSAGE = 'A sign-in code could not be sent. Please try again later.';
16 const THROTTLED_MESSAGE = 'Please wait %s before requesting another code.';
17
18 public static function authenticate($user) {
19 $resend = WPRHelper::getRawParam('POST', 'twofa_resend') === '1';
20 $code = WPRHelper::getRawParam('POST', 'twofa_code');
21
22 if (!$resend && is_string($code) && trim($code) !== '') {
23 if (WPRWP2FAEmailOTP::redeem($user, $code)) {
24 return $user;
25 }
26
27 return new WP_Error('invalid_2fa_code', self::REJECTED_MESSAGE);
28 }
29
30 return self::challenge($user);
31 }
32
33 private static function challenge($user) {
34 if (!is_string($user->user_email) || !is_email($user->user_email)) {
35 WPRWP2FA::sendFailure(WPRWP2FA::CONFIG_MESSAGE);
36 }
37
38 $wait = WPRWP2FAEmailOTP::secondsUntilNextSend($user->ID);
39 if ($wait > 0) {
40 // A code they can still use beats a new one they are not allowed yet.
41 if (WPRWP2FAEmailOTP::hasLiveCode($user->ID)) {
42 return self::askForCode($user, $wait, false);
43 }
44
45 // Not a WP_Error: our own send limit must not register as a failed
46 // login with the firewall's lockout counter.
47 WPRWP2FA::sendFailure(sprintf(self::THROTTLED_MESSAGE, WPRWP2FA::humanWait($wait)), array('resend_after' => $wait));
48 }
49
50 $code = WPRWP2FAEmailOTP::issue($user);
51 if ($code === null) {
52 WPRWP2FA::sendFailure(self::UNAVAILABLE_MESSAGE);
53 }
54
55 if (!WPRWP2FAEmailOTPSender::send($user, $code, WPRWP2FAEmailOTP::LIFETIME)) {
56 // Nothing was delivered, so leave no code behind that could be guessed.
57 WPRWP2FAEmailOTP::discard($user->ID);
58 WPRWP2FA::sendFailure(self::DELIVERY_MESSAGE);
59 }
60
61 return self::askForCode($user, WPRWP2FAEmailOTP::secondsUntilNextSend($user->ID), true);
62 }
63
64 private static function askForCode($user, $resend_after, $code_sent) {
65 wp_send_json_success(array(
66 'twofa_enabled' => true,
67 'twofa_method' => 'email_otp',
68 'masked_destination' => self::maskEmail($user->user_email),
69 'resend_after' => $resend_after,
70 'code_sent' => $code_sent
71 ));
72 exit;
73 }
74
75 private static function maskEmail($email) {
76 $parts = explode('@', $email, 2);
77 if (count($parts) !== 2) {
78 return '';
79 }
80
81 $labels = explode('.', $parts[1]);
82 $host = array_shift($labels);
83 $suffix = count($labels) > 0 ? '.' . implode('.', $labels) : '';
84
85 return substr($parts[0], 0, 1) . '***@' . substr($host, 0, 1) . '***' . $suffix;
86 }
87 }
88 endif;
89