| @@ -56,11 +56,17 @@ | ||
| 56 | 56 | * @param array $formData |
| 57 | 57 | */ |
| 58 | 58 | do_action('fluent_support/before_signup_validation', $formData); |
| 59 | 59 | |
| 60 | - $checkRecaptchaAvailability = $this->isRecaptchaApplicable('signup_form'); | |
| 61 | - if ($checkRecaptchaAvailability && !$formData['_email_verification_hash']) { | |
| 62 | - $validateCaptcha = ReCaptchaHandler::validateRecaptcha($formData['g-recaptcha-response']); | |
| 60 | + // the verification code is only submitted on the second step, after the customer has | |
| 61 | + // already passed the captcha and received the email; that step validates the hash | |
| 62 | + // against an issued record below, so the captcha can be skipped there. The first | |
| 63 | + // submit (which sends the verification email) always has to pass the captcha. | |
| 64 | + $isVerificationStep = !empty($formData['_email_verification_token']); | |
| 65 | + | |
| 66 | + $checkRecaptchaAvailability = ReCaptchaHandler::isRecaptchaApplicable('signup_form'); | |
| 67 | + if ($checkRecaptchaAvailability && !$isVerificationStep) { | |
| 68 | + $validateCaptcha = ReCaptchaHandler::validateRecaptcha($formData['g-recaptcha-response'] ?? '', null, null, 'signup'); | |
| 63 | 69 | if (!$validateCaptcha) { |
| 64 | 70 | return $this->response([ |
| 65 | 71 | 'message' => __('Your recaptcha is not verified', 'fluent-support') |
| 66 | 72 | ], 422); |
| @@ -68,9 +74,9 @@ | ||
| 68 | 74 | } |
| 69 | 75 | |
| 70 | 76 | $this->validate($formData, $rules, $messages); |
| 71 | 77 | |
| 72 | - if (empty($formData['_email_verification_token'])) { | |
| 78 | + if (!$isVerificationStep) { | |
| 73 | 79 | $tokenHtml = EmailVerificationHandler::sendSignupEmailVerificationHtml($formData); |
| 74 | 80 | |
| 75 | 81 | return $this->response([ |
| 76 | 82 | 'verification_html' => $tokenHtml |
| @@ -76,9 +82,9 @@ | ||
| 76 | 82 | 'verification_html' => $tokenHtml |
| 77 | 83 | ]); |
| 78 | 84 | } else { |
| 79 | 85 | $token = $formData['_email_verification_token']; |
| 80 | - $verificationHash = $formData['_email_verification_hash']; | |
| 86 | + $verificationHash = $formData['_email_verification_hash'] ?? ''; | |
| 81 | 87 | |
| 82 | 88 | $logHashMeta = Meta::where('object_type', 'fs_login_hashes',) |
| 83 | 89 | ->where('key', $verificationHash) |
| 84 | 90 | ->first(); |
| @@ -112,10 +118,15 @@ | ||
| 112 | 118 | ], 422); |
| 113 | 119 | } |
| 114 | 120 | |
| 115 | 121 | // check if it got expired or not |
| 122 | + // valid_till is written via gmdate() on a current_time('timestamp') basis (always | |
| 123 | + // UTC-equivalent, since WP resets the runtime timezone to UTC on every request), so | |
| 124 | + // it must be parsed back as UTC here - otherwise strtotime() would silently reinterpret | |
| 125 | + // it under whatever timezone a plugin/theme may have switched to via | |
| 126 | + // date_default_timezone_set() without restoring it, causing false expiry (or non-expiry) | |
| 116 | 127 | $validTill = $logHash['valid_till'] ?? ''; |
| 117 | - if (($logHash['used_count'] ?? 0) > 5 || ($validTill && strtotime($validTill) < current_time('timestamp'))) { | |
| 128 | + if (($logHash['used_count'] ?? 0) > 5 || ($validTill && strtotime($validTill . ' UTC') < current_time('timestamp'))) { | |
| 118 | 129 | wp_send_json([ |
| 119 | 130 | 'message' => __('Your verification code has been expired. Please try again', 'fluent-support') |
| 120 | 131 | ], 422); |
| 121 | 132 | } |
| @@ -221,11 +232,11 @@ | ||
| 221 | 232 | } |
| 222 | 233 | |
| 223 | 234 | $data = $request->all(); |
| 224 | 235 | |
| 225 | - $checkRecaptchaAvailability = $this->isRecaptchaApplicable('login_form'); | |
| 236 | + $checkRecaptchaAvailability = ReCaptchaHandler::isRecaptchaApplicable('login_form'); | |
| 226 | 237 | if ($checkRecaptchaAvailability) { |
| 227 | - $validateCaptcha = ReCaptchaHandler::validateRecaptcha($data['g-recaptcha-response']); | |
| 238 | + $validateCaptcha = ReCaptchaHandler::validateRecaptcha($data['g-recaptcha-response'] ?? '', null, null, 'login'); | |
| 228 | 239 | |
| 229 | 240 | if (!$validateCaptcha) { |
| 230 | 241 | return $this->response([ |
| 231 | 242 | 'message' => __('Your recaptcha is not verified', 'fluent-support') |
| @@ -347,26 +358,8 @@ | ||
| 347 | 358 | set_transient($rateLimitKey, 1, 15 * MINUTE_IN_SECONDS); |
| 348 | 359 | } else { |
| 349 | 360 | set_transient($rateLimitKey, $attempts + 1, 15 * MINUTE_IN_SECONDS); |
| 350 | 361 | } |
| 351 | - } | |
| 352 | - | |
| 353 | - public function isRecaptchaApplicable($formName) | |
| 354 | - { | |
| 355 | - $reCaptchaSettingsData = Meta::where('object_type', '_fs_recaptcha_settings')->first(); | |
| 356 | - if(!isset($reCaptchaSettingsData->value)){ | |
| 357 | - return false; | |
| 358 | - } | |
| 359 | - $reCaptchaData = Helper::safeUnserialize($reCaptchaSettingsData->value, []); | |
| 360 | - if(!isset($reCaptchaData['is_enabled']) || !isset($reCaptchaData['formContainingReCaptcha'])){ | |
| 361 | - return false; | |
| 362 | - } | |
| 363 | - $isEnabled = filter_var($reCaptchaData['is_enabled'], FILTER_VALIDATE_BOOLEAN); | |
| 364 | - if (!$isEnabled) { | |
| 365 | - return false; | |
| 366 | - } | |
| 367 | - $formContainingReCaptcha = $reCaptchaData['formContainingReCaptcha']; | |
| 368 | - return 'yes' === $formContainingReCaptcha[$formName]; | |
| 369 | 362 | } |
| 370 | 363 | |
| 371 | 364 | private function nativeLoginHandler($user, $info, $redirectUrl = '') |
| 372 | 365 | { |