HEX
Server: Apache/2.4.58 (Ubuntu)
System: Linux bsx-1-dev 6.8.0-101-generic #101-Ubuntu SMP PREEMPT_DYNAMIC Mon Feb 9 10:15:05 UTC 2026 x86_64
User: www-data (33)
PHP: 8.3.6
Disabled: NONE
Upload Files
File: /var/www/html/wp-content/plugins/memberpress/app/controllers/MeprAuthenticatorCtrl.php
<?php

if (!defined('ABSPATH')) {
    die('You are not allowed to call this page directly.');
}

class MeprAuthenticatorCtrl extends MeprBaseCtrl
{
    /**
     * Load the hooks.
     *
     * @return void
     */
    public function load_hooks()
    {
        if (!defined('MEPR_AUTH_SERVICE_DOMAIN')) {
            define('MEPR_AUTH_SERVICE_DOMAIN', 'auth.caseproof.com');
        }
        define('MEPR_AUTH_SERVICE_URL', 'https://' . MEPR_AUTH_SERVICE_DOMAIN);

        add_action('admin_init', [$this, 'clear_connection_data']);
        add_action('init', [$this, 'process_connect']);
        add_action('init', [$this, 'process_disconnect']);
    }

    /**
     * Clear the connection data.
     *
     * @return void
     */
    public function clear_connection_data()
    {
        if (!isset($_GET['mp-clear-connection-data'])) {
            return;
        }

        // Admins only.
        if (!current_user_can('manage_options')) {
            return;
        }

        // If nonce is present and valid, perform the action.
        if (isset($_GET['_wpnonce']) && wp_verify_nonce(sanitize_text_field(wp_unslash($_GET['_wpnonce'])), 'mp-clear-connection-data')) {
            delete_option('mepr_authenticator_site_uuid');
            delete_option('mepr_authenticator_account_email');
            delete_option('mepr_authenticator_secret_token');
            wp_safe_redirect(admin_url());
            exit;
        }

        // Show confirmation page.
        $nonce_url = wp_nonce_url(admin_url('?mp-clear-connection-data=1'), 'mp-clear-connection-data');
        wp_die(
            '<h1>' . esc_html__('Clear Connection Data', 'memberpress') . '</h1>' .
            '<p>' . esc_html__('Are you sure you want to clear your MemberPress connection data? This will remove your site UUID, account email, and secret token.', 'memberpress') . '</p>' .
            '<p><a class="button button-primary" href="' . esc_url($nonce_url) . '">' . esc_html__('Yes, Clear Connection Data', 'memberpress') . '</a> ' .
            '<a class="button" href="' . esc_url(admin_url()) . '">' . esc_html__('Cancel', 'memberpress') . '</a></p>',
            esc_html__('Confirm Action', 'memberpress'),
            ['back_link' => false]
        );
    }

    // phpcs:disable Squiz.Commenting.FunctionCommentThrowTag.Missing
    /**
     * Process a Connect
     *
     * @return void
     */
    public function process_connect()
    {
        // Make sure we've entered our Authenticator process.
        if (! isset($_GET['mepr-connect']) || 'true' !== $_GET['mepr-connect']) {
            return;
        }

        // Validate the nonce on the WP side of things.
        if (! isset($_GET['nonce']) || ! wp_verify_nonce(sanitize_text_field(wp_unslash($_GET['nonce'])), 'mepr-connect')) {
            return;
        }

        // Make sure the user is authorized.
        if (! MeprUtils::is_mepr_admin()) {
            return;
        }

        $site_uuid = sanitize_text_field(wp_unslash($_GET['site_uuid'] ?? ''));
        $auth_code = sanitize_text_field(wp_unslash($_GET['auth_code'] ?? ''));

        // GET request to obtain token.
        $response = wp_remote_get(MEPR_AUTH_SERVICE_URL . "/api/tokens/{$site_uuid}", [
            'sslverify' => false,
            'headers'   => [
                'accept' => 'application/json',
            ],
            'body'      => [
                'auth_code' => $auth_code,
            ],
        ]);

        $body = json_decode(wp_remote_retrieve_body($response), true);

        if (isset($body['account_email']) && ! empty($body['account_email'])) {
            $email_saved = update_option('mepr_authenticator_account_email', sanitize_text_field($body['account_email']));
        }

        if (isset($body['secret_token']) && ! empty($body['secret_token'])) {
            $token_saved = update_option('mepr_authenticator_secret_token', sanitize_text_field($body['secret_token']));
        }

        if (isset($body['user_uuid']) && ! empty($body['user_uuid'])) {
            $user_uuid_saved = update_option('mepr_authenticator_user_uuid', sanitize_text_field($body['user_uuid']));
        }

        if ($site_uuid) {
            update_option('mepr_authenticator_site_uuid', $site_uuid);
        }

        if (isset($_GET['stripe_connect']) && 'true' === $_GET['stripe_connect'] && isset($_GET['method_id']) && ! empty($_GET['method_id'])) {
            wp_redirect(MeprStripeGateway::get_stripe_connect_url(sanitize_text_field(wp_unslash($_GET['method_id'])))); // phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect
            exit;
        }

        if (isset($_GET['square_connect']) && 'true' === $_GET['square_connect']) {
            $options           = MeprOptions::fetch();
            $payment_method_id = sanitize_text_field(wp_unslash($_GET['square_payment_method_id'] ?? ''));
            $environment       = sanitize_text_field(wp_unslash($_GET['square_environment'] ?? ''));
            $environment       = $environment === 'sandbox' ? 'sandbox' : 'production';
            $pm                = $options->payment_method($payment_method_id);

            try {
                if (!$pm instanceof MeprSquarePaymentsGateway) {
                    throw new Exception(__('Sorry, this only works with Square.', 'memberpress'));
                }

                wp_redirect($pm->connect_url($environment)); // phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect
                exit;
            } catch (Exception $e) {
                $args = [
                    'page'                       => 'memberpress-options',
                    'mepr-square-connect-status' => 'error',
                    'error'                      => $e->getMessage(),
                ];

                wp_safe_redirect(add_query_arg(array_map('rawurlencode', $args), admin_url('admin.php')));
                exit;
            }
        }

        $redirect_url = remove_query_arg(['mepr-connect', 'nonce', 'site_uuid', 'user_uuid', 'auth_code', 'license_key']);

        $license_key = isset($_GET['license_key']) ? sanitize_text_field(wp_unslash($_GET['license_key'])) : '';

        if (! empty($license_key) && class_exists('MeprUpdateCtrl')) {
            try {
                MeprUpdateCtrl::activate_license($license_key);
            } catch (Exception $e) {
                $redirect_url = add_query_arg('license_error', urlencode($e->getMessage()), $redirect_url);
            }
        }

        wp_safe_redirect($redirect_url);
        exit;
    }
    // phpcs:enable Squiz.Commenting.FunctionCommentThrowTag.Missing

    /**
     * Process a Disconnect
     *
     * @return void
     */
    public function process_disconnect()
    {

        // Make sure we've entered our Authenticator process.
        if (! isset($_GET['mepr-disconnect']) || 'true' !== $_GET['mepr-disconnect']) {
            return;
        }

        // Validate the nonce on the WP side of things.
        if (! isset($_GET['nonce']) || ! wp_verify_nonce(sanitize_text_field(wp_unslash($_GET['nonce'])), 'mepr-disconnect')) {
            return;
        }

        // Make sure the user is authorized.
        if (! MeprUtils::is_mepr_admin()) {
            return;
        }

        $site_email = get_option('mepr_authenticator_account_email');
        $site_uuid  = get_option('mepr_authenticator_site_uuid');

        MeprHooks::do_action('mepr_memberpress_com_pre_disconnect', $site_uuid, $site_email);

        // Create token payload.
        $payload = [
            'email'     => $site_email,
            'site_uuid' => $site_uuid,
        ];

        // Create JWT.
        $jwt = self::generate_jwt($payload);

        // DELETE request to obtain token.
        $response = wp_remote_request(MEPR_AUTH_SERVICE_URL . '/api/disconnect/memberpress', [
            'method'    => 'DELETE',
            'sslverify' => false,
            'headers'   => MeprUtils::jwt_header($jwt, MEPR_AUTH_SERVICE_DOMAIN),
        ]);

        $body = json_decode(wp_remote_retrieve_body($response), true);

        if (isset($body['disconnected']) && true === $body['disconnected']) {
            delete_option('mepr_authenticator_account_email');
            delete_option('mepr_authenticator_secret_token');
            delete_option('mepr_authenticator_site_uuid', $site_uuid);
        }

        wp_safe_redirect(remove_query_arg(['mepr-disconnect', 'nonce']));
        exit;
    }

    /**
     * Generates a JWT, signed by the stored secret token
     *
     * @param array  $payload Payload data.
     * @param string $secret  Used to sign the JWT.
     *
     * @return string
     */
    public static function generate_jwt($payload, $secret = false)
    {
        if (false === $secret) {
            $secret = get_option('mepr_authenticator_secret_token');
        }

        // Create token header.
        $header = [
            'typ' => 'JWT',
            'alg' => 'HS256',
        ];
        $header = json_encode($header);
        $header = self::base64url_encode($header);

        // Create token payload.
        $payload = json_encode($payload);
        $payload = self::base64url_encode($payload);

        // Create Signature Hash.
        $signature = hash_hmac('sha256', "{$header}.{$payload}", $secret);
        $signature = json_encode($signature);
        $signature = self::base64url_encode($signature);

        // Create JWT.
        $jwt = "{$header}.{$payload}.{$signature}";
        return $jwt;
    }

    /**
     * Ensure that the Base64 string is passed within URLs without any URL encoding
     *
     * @param string $value The value to encode.
     *
     * @return string
     */
    public static function base64url_encode($value)
    {
        return rtrim(strtr(base64_encode($value), '+/', '-_'), '=');
    }

    /**
     * Assembles a URL for connecting to our Authentication service
     *
     * @param boolean     $stripe_connect    Will add a query string that is used to redirect to Stripe Connect after returning from Auth service.
     * @param string|null $payment_method_id The payment method ID.
     * @param array       $additional_params Additional parameters to add to the URL.
     * @param string|null $return_url        The return URL.
     *
     * @return string
     */
    public static function get_auth_connect_url($stripe_connect = false, $payment_method_id = false, $additional_params = [], $return_url = null)
    {
        $return_url = is_null($return_url) ? admin_url('admin.php?page=memberpress-account-login', false) : $return_url;

        $connect_params = [
            'return_url' => urlencode(add_query_arg('mepr-connect', 'true', $return_url)),
            'nonce'      => wp_create_nonce('mepr-connect'),
        ];

        $site_uuid = get_option('mepr_authenticator_site_uuid');

        if ($site_uuid) {
            $connect_params['site_uuid'] = $site_uuid;
        }

        if (true === $stripe_connect && ! empty($payment_method_id)) {
            $connect_params['stripe_connect'] = 'true';
            $connect_params['method_id']      = $payment_method_id;
        }

        if (! empty($additional_params)) {
            $connect_params = array_merge($connect_params, $additional_params);
        }

        return add_query_arg($connect_params, MEPR_AUTH_SERVICE_URL . '/connect/memberpress');
    }
}