2
\$\begingroup\$

I'd like to use basic auth with the WordPress API, but restrict the capability to a single IP address. I've updated the existing code from here: https://github.com/WP-API/Basic-Auth accordingly.

It seems to work fine but I would be interested in any comments about security or improvements that could be made.

class BasicAuth
{
    public static function addAction(): void
    {
        $instance = new static;
        add_filter('determine_current_user', [$instance, 'json_basic_auth_handler'], 20);
        add_filter('rest_authentication_errors', [$instance, 'json_basic_auth_error'], 20);
    }

    protected $result;

    /**
     * Based on: https://github.com/WP-API/Basic-Auth
     * @param int|bool $user
     * @return int|bool|null $user
     */
    function json_basic_auth_handler($user)
    {
        $this->result = null;
        // Don't authenticate twice
        if (!empty($user)) {
            return $user;
        }
        // Check that we're trying to authenticate
        if (!isset($_SERVER['PHP_AUTH_USER'])) {
            return $user;
        }
        // Check client IP
        if (!$this->checkIpPermitted()) {
            return $user;
        }
        $username = $_SERVER['PHP_AUTH_USER'];
        $password = $_SERVER['PHP_AUTH_PW'];
        /**
         * In multi-site, wp_authenticate_spam_check filter is run on authentication. This filter calls
         * get_currentuserinfo which in turn calls the determine_current_user filter. This leads to infinite
         * recursion and a stack overflow unless the current function is removed from the determine_current_user
         * filter during authentication.
         */
        remove_filter('determine_current_user', 'json_basic_auth_handler', 20);
        $user = wp_authenticate($username, $password); /** @var WP_User|WP_Error $ */
        add_filter('determine_current_user', 'json_basic_auth_handler', 20);
        // Authentication failure
        if (is_wp_error($user)) {
            $this->result = $user;
            return null;
        }
        // Authentication success
        $this->result = true;
        return $user->ID;
    }

    protected function checkIpPermitted(): bool
    {
        $options = get_option(OptionsPage::CONNECT_OPTIONS);
        $ip = $options['ip'] ?? '';
        if (($_SERVER['REMOTE_ADDR'] ?? null) !== $ip) {
            $this->result = new WP_Error('ip-blocked', 'Invalid request');
            return false;
        }
        return true;
    }

    function json_basic_auth_error($error)
    {
        // Passthrough other errors
        if (!empty($error)) {
            return $error;
        }
        // WP_Error if authentication error, null if authentication method wasn't used, true if authentication succeeded.
        return $this->result;
    }
}

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.