self::HOST, 'redirect_to' => $redirectTo ]); } self::$rendering = true; try { return $render(); } finally { self::$rendering = false; } } public static function addSignupFields($fields) { if (!self::isOurRequest()) { return $fields; } $customFields = AuthHandler::allCustomFields(); $supportsSelect = self::supportsSelectFields(); foreach (Helper::getCustomRegistrationFieldKeys() as $key) { if (!isset($customFields[$key])) { continue; } $field = $customFields[$key]; // FluentAuth versions without select support leave the field out of the form. if (($field['type'] ?? '') === 'select' && !$supportsSelect) { $field['type'] = 'text'; unset($field['options']); } $fields[$key] = $field; } return $fields; } public static function captureSignupData($formData) { // Older FluentAuth signup forms carry no marker, so their posts cannot be told apart. if (self::isOurRequest() || !self::hasBridge()) { self::$signupData = (array)$formData; } } public static function saveSignupMeta($userId) { if (self::$signupData === null || !$userId) { return; } (new AuthController())->addUserMetaData($userId, self::$signupData); self::$signupData = null; } public static function signupRole($role) { if (!self::isOurRequest()) { return $role; } /* * Filter user assignable role after signup * * @since v1.0.0 * @param string $setRole WordPress user role key */ return apply_filters('fluent_support/user_role', 'subscriber'); } /** * FluentAuth's signup posts its redirect as `__redirect_to` but reads `redirect_to`, * so without this a portal signup would land in wp-admin. * * @param string $redirectUrl * @return string */ public static function loginRedirectUrl($redirectUrl) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- FluentAuth verified the request before redirecting. if (!self::isOurRequest() || empty($_REQUEST['__redirect_to'])) { return $redirectUrl; } // phpcs:ignore WordPress.Security.NonceVerification.Recommended $target = sanitize_url(wp_unslash($_REQUEST['__redirect_to'])); return wp_validate_redirect($target, Helper::getPortalBaseUrl()); } /** * Whether this request is one of our forms: rendering it, or posting it back. * * @return bool */ private static function isOurRequest() { return self::$rendering || self::hasSignedMarker(); } /** * Whether the request carries the signed marker FluentAuth prints in every form it * renders for us. A visitor cannot forge it, unlike a plain request field. * * The marker is a nonce for the visitor who loaded the form. A signup logs the new * user in before FluentAuth applies its redirect filter, and the nonce no longer * verifies for that user, so the first answer for a signature is kept. * * @return bool */ public static function hasSignedMarker() { static $verified = []; if (!self::hasBridge()) { return false; } // phpcs:disable WordPress.Security.NonceVerification.Recommended -- the marker is itself a nonce, verified below. $markerField = \FluentAuth\App\Services\LoginBridge::MARKER_FIELD; if (empty($_REQUEST[$markerField]) || empty($_REQUEST[$markerField . '_sig'])) { return false; } if (sanitize_key(wp_unslash($_REQUEST[$markerField])) !== self::HOST) { return false; } $signature = sanitize_text_field(wp_unslash($_REQUEST[$markerField . '_sig'])); // phpcs:enable WordPress.Security.NonceVerification.Recommended if (!isset($verified[$signature])) { $verified[$signature] = (bool)wp_verify_nonce($signature, \FluentAuth\App\Services\LoginBridge::MARKER_ACTION . self::HOST); } return $verified[$signature]; } /** * LoginBridge ships from FluentAuth 3.0.0. The version is checked before the class, * because FluentAuth 2.x's autoloader requires a missing file instead of returning * false, so class_exists() alone would be a fatal error there. * * @return bool */ private static function hasBridge() { return defined('FLUENT_AUTH_VERSION') && version_compare(FLUENT_AUTH_VERSION, '3.0.0', '>=') && class_exists('\FluentAuth\App\Services\LoginBridge'); } /** * @return bool */ private static function supportsSelectFields() { $constant = '\FluentAuth\App\Hooks\Handlers\CustomAuthHandler::SIGNUP_FIELD_TYPES'; return defined($constant) && in_array('select', (array)constant($constant), true); } }