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\$

0

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.