Get a free advice now!

    Pick the topic

    Developer OutsourcingWeb developingApp developingDigital MarketingeCommerce systemseEntertainment systems

    Thank you for your message. It has been sent.

    Tags

    Woo API – how to whitelist endpoints

    Woo API – how to whitelist endpoints

    CHALLENGE: restrict access to WooCommerce built-in endpoints

    SOLUTION: use the ‘rest_authentication_errors’ and ‘rest_endpoints’ filters

    Special access permissions are needed to access the WooCommerce API (a pair of keys: Consumer key and secret). We can also restrict access type (Read/Write). A person who knows the keys has access to all API routes. Sometimes, it is recommended to restrict access and whitelist only really needed endpoints.

    WooCommerce routes

    To find out which routes are currently available, we can use the /wp-json/wc/v3 API endpoint. The response will return all routes in JSON format. To have better visualization, we can use the ‘WP API SwaggerUI’ plugin that will show the /wc/v3 list using permalink: /rest-api/docs/ . Each route can have a different method: GET, POST, PUT, PATCH or DELETE.

    How to block the WooCommerce API route?

    There are 2 filters that can be used to blacklist / whitelist the Woo Route. The first filter, ‘rest_endpoints’, disables route definition. The route will be blocked and become invisible on the list. The second filter, ‘rest_authentication_errors’, makes it possible to block route on authentication. The route will be visible on the list, but blocked when trying to use it.

    Another difference is that the ‘rest_authentication_errors’ filter is not used in internal API calls in PHP (new WP_REST_Request()). I imagine a setup where we use different endpoints internally by doing internal API calls, but blacklisting them for external usage. If that’s the case, then ‘rest_authentication_errors’ can be used.

    Whitelist REST endpoints

    We will prepare a PHP class that will give us full control over which endpoints are accessible for users with API keys. The configuration is done using private arrays written at the beginning of the class. The first step is to blacklist all routes that start with ‘/wc/v3’, so all routes are currently blocked.

    $whitelisted_routes is a list of route names that should be whitelisted. Keep in mind that all routes that “start with” those strings will be accessible. If we need even more control, to whitelist only a particular method (exact match) we will be using $whitelisted_exact_match. We can define route name and method, and whitelist only a specific one.

    Here is a PHP function for whitelisting WooCommerce API routes. With the current configuration all endpoints that start with /wc/v3/customers and /wc/v3/payment_gateways will be available. In addition, /wc/v3/orders [GET] /wc/v3/orders/{id} [GET] will also be available as exact match.

    <?php
    // functions.php
    /**
     * Whitelist WP Rest API Endpoints
     */
    class CT_whitelist_api_endpoints{
        // Please note that item /wp-json/route/ will also whitelist: /wp-json/route/.*
        // block all woocommerce endpoints
        private $blocked_routes = array(
            '/wc/v3/'
        );
        // some exceptions (all routes that "starts with" will be whitelisted)
        private $whitelisted_routes = array(
            '/wc/v3/customers',
            '/wc/v3/payment_gateways',
        );
        // whitelist exact match for endpoint name
        private $whitelisted_exact_match = array(
            array('route' => '/wc/v3/orders', 'method' => 'GET'),
            array('route' => '/wc/v3/orders/(?P<id>[\d]+)', 'method' => 'GET'),
        );
        function __construct() {
            // disable routes definition, internal api calls will be not working
            add_filter( 'rest_endpoints', array($this,'ct_rest_endpoints') );
            // alternative solution, endpoint will be blocked on authentication, all internal api calls still will be working
            add_filter( 'rest_authentication_errors', array($this,'ct_rest_authentication_errors'));
        }
        /**
         * @param $result
         * @return bool|mixed|WP_Error
         * Whitelist WP Rest API endpoints, block other endpoints
         */
        public function ct_rest_authentication_errors($result)
        {
            // If a previous authentication check was applied,
            // pass that result along without modification.
            if (true === $result || is_wp_error($result)) {
                return $result;
            }
            if (!empty($result)) {
                return $result;
            }
            // full path (with parameters)
            $current_route = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '/';
            // clean up
            $current_route = str_replace('/wp-json','',$current_route);
            // allow all by default
            $route_allowed = true;
            // now block some endpoints
            foreach ($this->blocked_routes as $blocked_route) {
                if (substr($current_route, 0, strlen($blocked_route)) === $blocked_route) {
                    $route_allowed = false;
                    break;
                }
            }
            // exceptions
            foreach ($this->whitelisted_routes as $whitelisted_route) {
                if (substr($current_route, 0, strlen($whitelisted_route)) === $whitelisted_route) {
                    $route_allowed = true;
                    break;
                }
            }
            // exception for: exact match
            $current_route_base =  strtok($current_route, '?');
            $current_route_base = rtrim($current_route_base, '/');
            $exact_match = array_keys(array_column($this->whitelisted_exact_match, 'route'), $current_route_base);
            if(!empty($exact_match)){
                $route_allowed = true;
            }
            if ($route_allowed) {
                return $result;
            };
            return new WP_Error('rest_cannot_access', __('This WP endpoint is disabled', 'disable-json-api'), array('status' => rest_authorization_required_code()));
        }
        /**
         * Disable some WP Rest API Routes Endpoints
         * Add some exceptions
         */
        public function ct_rest_endpoints($endpoints){
            if(isset($endpoints['/wc/v3/coupons/batch'])){
                // debugging this particular filter
                // var_dump($endpoints['/wc/v3/coupons/batch']);
                // exit;
            };
            $block_route = false;
            foreach( $endpoints as $route => $endpoint ){
                // block items
                // debugging route names
                // var_dump($route);
                if( $this->striposarray( $route, $this->blocked_routes ) ){
                    $block_route = true;
                }
                // add exceptions
                if( $this->striposarray( $route, $this->whitelisted_routes ) ){
                    $block_route = false;
                }
                // add exception for full match
                $exact_match_array_keys = array_keys(array_column($this->whitelisted_exact_match, 'route'), $route);
                if(!empty($exact_match_array_keys)){
                    // whitelist entire endpoint (all methods)
                    $block_route = false;
                    foreach($endpoint as $item_key => $item_elem) {
                        // block methods that are not whitelisted
                        if (isset($item_elem['methods'])) {
                            $block_method = true;
                            foreach($exact_match_array_keys as $exact_match_key ){
                                // tricky.. method can be: string(16) "POST, PUT, PATCH" or just "POST"
                                if (strpos($item_elem['methods'], $this->whitelisted_exact_match[$exact_match_key]['method']) !== false) {
                                    $block_method = false;
                                    break;
                                }
                            }
                            if($block_method){
                                unset($endpoints[ $route ][$item_key]);
                            }
                        }
                    }
                }
                if($block_route){
                    unset( $endpoints[ $route ] );
                }
            }
            return $endpoints;
        }
        private function striposarray($haystack, $needle, $offset=0) {
            if(!is_array($needle)) {
                $needle = array($needle);
            }
            foreach($needle as $query) {
                if(stripos($haystack, $query, $offset) !== false){
                    // stop on first true result
                    return true;
                }
            }
            return false;
        }
    }
    $ctWhitelisted = new CT_whitelist_api_endpoints();

    WordPress API security

    That’s it. The function will control access to our API. By default, all built-in WooCommerce routes are blacklisted and secured. We give access only to selected endpoints. Here is a screenshot from Swagger documentation listing a confirmation that our code is working correctly.

    A site with a list of rows that include

    That’s it for this tutorial. Make sure to follow us for other useful tips and guidelines.

    Comments
    0 response

    Add comment

    Your email address will not be published. Required fields are marked *

    Popular news

    Digital marketing without third-party cookies – new rules
    • Technology
    • Trends

    Digital marketing without third-party cookies – new rules

    February 21, 2024 by createIT
    eCommerce healthcheck
    • Services
    • Trends

    eCommerce healthcheck

    January 24, 2024 by createIT
    Live Visitor Count in WooCommerce with SSE
    • Dev Tips and Tricks

    Live Visitor Count in WooCommerce with SSE

    December 12, 2023 by createIT
    Calculate shipping costs programmatically in WooCommerce
    • Dev Tips and Tricks

    Calculate shipping costs programmatically in WooCommerce

    December 11, 2023 by createIT
    Designing a cookie consent modal certified by TCF IAB
    • Dev Tips and Tricks

    Designing a cookie consent modal certified by TCF IAB

    December 7, 2023 by createIT
    Understanding the IAB’s Global Vendor List (GVL)
    • Dev Tips and Tricks

    Understanding the IAB’s Global Vendor List (GVL)

    December 6, 2023 by createIT
    Effortlessly transform JSON data into a clear schema
    • Uncategorized

    Effortlessly transform JSON data into a clear schema

    December 5, 2023 by createIT

    Support – Tips and Tricks
    All tips in one place, and the database keeps growing. Stay up to date and optimize your work!

    Contact us