• Skip to secondary menu
  • Skip to main content
  • Skip to primary sidebar
  • Home
  • Projects
  • Products
  • Themes
  • Tools
  • Request for Quote

Vengala Vinay

Having 12+ Years of Experience in Software Development

  • Home
  • WordPress
  • PHP
    • Codeigniter
  • Django
  • Magento
  • Selenium
  • Server
Home » How to implement custom REST API Controllers endpoints with token authentication in Gutenberg blocks

How to implement custom REST API Controllers endpoints with token authentication in Gutenberg blocks

Registering Custom REST API Endpoints in WordPress

To extend WordPress’s REST API with custom functionality, particularly for integration with Gutenberg blocks, we need to register new routes and endpoints. This involves leveraging the `register_rest_route` function, which allows us to define the namespace, route, and the callback function that will handle requests to that endpoint. For e-commerce scenarios, these endpoints might expose product data, process orders, or manage user-specific information.

The core of this process lies in hooking into the `rest_api_init` action. This action fires when the REST API is being initialized, providing the perfect opportunity to register our custom routes.

Implementing a Basic Endpoint with a Callback

Let’s start by creating a simple endpoint that returns a JSON response. We’ll define a namespace, a route, and a callback function. The callback function receives a `WP_REST_Request` object, which contains all the information about the incoming request.

add_action( 'rest_api_init', function () {
    register_rest_route( 'myplugin/v1', '/products', array(
        'methods' => WP_REST_Server::READABLE, // Equivalent to GET
        'callback' => 'myplugin_get_products_callback',
        'permission_callback' => '__return_true', // For now, allow all
    ) );
} );

function myplugin_get_products_callback( WP_REST_Request $request ) {
    // In a real scenario, fetch products from the database or an external service.
    $products = array(
        array(
            'id' => 1,
            'name' => 'Awesome Gadget',
            'price' => 99.99,
        ),
        array(
            'id' => 2,
            'name' => 'Super Widget',
            'price' => 49.50,
        ),
    );

    return new WP_REST_Response( $products, 200 );
}

In this example:

  • myplugin/v1 is our custom namespace and version. It’s crucial to use a unique namespace to avoid conflicts with other plugins or WordPress core.
  • /products is the specific route for this endpoint.
  • WP_REST_Server::READABLE specifies that this endpoint responds to GET requests. Other common methods include WP_REST_Server::CREATABLE (POST), WP_REST_Server::EDITABLE (POST, PUT, PATCH), and WP_REST_Server::DELETABLE (DELETE).
  • myplugin_get_products_callback is the PHP function that will be executed when a request hits this endpoint.
  • __return_true is a placeholder for the permission callback. We’ll address authentication and authorization in detail later.

Implementing Token-Based Authentication

For Gutenberg blocks, especially those interacting with sensitive data or performing actions on behalf of a user, robust authentication is paramount. WordPress’s REST API supports various authentication methods, but for custom integrations, token-based authentication is often preferred. This typically involves generating a unique token for a user or an application and requiring it in subsequent requests.

We’ll implement a simple token authentication mechanism. This involves:

  • Generating and storing tokens (e.g., as user meta).
  • Validating the token on incoming requests.
  • Associating the authenticated user with the request.

Generating and Storing Tokens

Tokens can be generated when a user logs in, or through a dedicated API endpoint for application integration. For user-specific tokens, storing them as user meta is a common practice. We’ll use `wp_generate_password` for token generation and `update_user_meta` to store it.

// Function to generate and store a token for a user
function myplugin_generate_user_token( $user_id ) {
    if ( ! $user_id ) {
        return false;
    }

    // Generate a secure, unique token
    $token = wp_generate_password( 64, false, false ); // 64 characters, no special chars, no numbers

    // Store the token as user meta
    update_user_meta( $user_id, 'myplugin_api_token', $token );

    return $token;
}

// Example: Generate token on user profile update (for demonstration)
add_action( 'show_user_profile', 'myplugin_display_token_field' );
add_action( 'edit_user_profile', 'myplugin_display_token_field' );

function myplugin_display_token_field( $user ) {
    $token = get_user_meta( $user->ID, 'myplugin_api_token', true );
    ?>
    <h3></h3>
    <table class="form-table">
        <tr>
            <th scope="row"></th>
            <td>
                <input type="text" readonly="readonly" value="" class="regular-text" />
                <p class="description"></p>
                <button type="button" id="myplugin-generate-token" class="button"></button>
            </td>
        </tr>
    </table>
    <script>
        jQuery(document).ready(function($) {
            $('#myplugin-generate-token').on('click', function() {
                var data = {
                    'action': 'myplugin_regenerate_token',
                    'user_id': ,
                    '_ajax_nonce': ''
                };
                $.post(ajaxurl, data, function(response) {
                    if (response.success) {
                        $('input[type="text"]').val(response.data.token);
                        alert('');
                    } else {
                        alert('');
                    }
                });
            });
        });
    </script>
    



This code adds a field to the user profile page displaying the current token and a button to generate a new one. The generation process uses AJAX to avoid page reloads.

Validating Tokens in REST API Endpoints

Now, we need to modify our endpoint registration to include a robust permission callback that checks for a valid token. The token is typically passed in the `Authorization` header as a Bearer token, or as a query parameter.

add_action( 'rest_api_init', function () {
    register_rest_route( 'myplugin/v1', '/products', array(
        'methods' => WP_REST_Server::READABLE,
        'callback' => 'myplugin_get_products_callback',
        'permission_callback' => 'myplugin_check_api_token',
    ) );

    // Example: Endpoint to retrieve the current user's token
    register_rest_route( 'myplugin/v1', '/me/token', array(
        'methods' => WP_REST_Server::READABLE,
        'callback' => 'myplugin_get_current_user_token',
        'permission_callback' => 'myplugin_check_api_token', // Requires authentication to get token
    ) );
} );

function myplugin_check_api_token( WP_REST_Request $request ) {
    $token = null;

    // 1. Check for token in Authorization header
    $auth_header = $request->get_header( 'Authorization' );
    if ( ! empty( $auth_header ) ) {
        if ( preg_match( '/Bearer\s(\S+)/', $auth_header, $matches ) ) {
            $token = $matches[1];
        }
    }

    // 2. If not in header, check for token in query parameters (less secure, use with caution)
    if ( empty( $token ) ) {
        $token = $request->get_param( 'api_token' );
    }

    if ( empty( $token ) ) {
        return new WP_Error( 'rest_not_logged_in', 'No authentication token provided.', array( 'status' => 401 ) );
    }

    // Find user by token
    $user_id = null;
    $users = get_users( array(
        'meta_key' => 'myplugin_api_token',
        'meta_value' => $token,
    ) );

    if ( ! empty( $users ) ) {
        $user_id = $users[0]->ID;
    }

    if ( ! $user_id ) {
        return new WP_Error( 'rest_invalid_token', 'Invalid authentication token.', array( 'status' => 401 ) );
    }

    // Set the authenticated user for the request
    wp_set_current_user( $user_id );

    // Return true if authentication is successful
    return true;
}

function myplugin_get_products_callback( WP_REST_Request $request ) {
    // The permission_callback has already authenticated the user.
    // We can now access the current user if needed.
    $current_user = wp_get_current_user();

    // In a real scenario, fetch products.
    $products = array(
        array(
            'id' => 1,
            'name' => 'Awesome Gadget',
            'price' => 99.99,
            'added_by' => $current_user->display_name, // Example of using authenticated user
        ),
        // ... more products
    );

    return new WP_REST_Response( $products, 200 );
}

function myplugin_get_current_user_token( WP_REST_Request $request ) {
    $current_user = wp_get_current_user();
    if ( ! $current_user->exists() ) {
        return new WP_Error( 'rest_not_authenticated', 'User not authenticated.', array( 'status' => 401 ) );
    }

    $token = get_user_meta( $current_user->ID, 'myplugin_api_token', true );

    return new WP_REST_Response( array( 'token' => $token ), 200 );
}

The `myplugin_check_api_token` function:

  • First attempts to retrieve the token from the Authorization: Bearer <token> header.
  • If not found, it falls back to checking for an api_token query parameter.
  • It then queries the user database for a user whose meta value for myplugin_api_token matches the provided token.
  • If a user is found, `wp_set_current_user` is called to make that user the current user for the request, allowing subsequent callbacks to access user information via `wp_get_current_user()`.
  • If no token is provided or the token is invalid, it returns a WP_Error with a 401 status code.

Integrating with Gutenberg Blocks

For Gutenberg blocks, the REST API endpoints are typically accessed using JavaScript. The block's JavaScript file will make `fetch` requests to your custom endpoints. Authentication is handled by including the API token in the request headers.

First, ensure your block's JavaScript is enqueued correctly, and that it has access to the API URL and potentially the user's token (if it's a user-specific block). You can pass data from PHP to JavaScript using `wp_localize_script`.

// In your plugin's main PHP file or block registration file
function myplugin_enqueue_block_scripts() {
    // Enqueue your block's JavaScript file
    wp_enqueue_script(
        'myplugin-block-editor-js',
        plugins_url( 'build/index.js', __FILE__ ), // Path to your compiled JS
        array( 'wp-blocks', 'wp-element', 'wp-editor', 'wp-components', 'wp-i18n' ),
        filemtime( plugin_dir_path( __FILE__ ) . 'build/index.js' )
    );

    // Localize script to pass data to JavaScript
    wp_localize_script( 'myplugin-block-editor-js', 'mypluginData', array(
        'api_url' => esc_url_raw( rest_url( 'myplugin/v1/' ) ),
        // You might fetch the user's token and pass it here, or have the JS fetch it.
        // Be cautious about exposing tokens directly if not necessary.
        // For user-specific blocks, consider fetching the token via a dedicated endpoint.
    ) );
}
add_action( 'enqueue_block_editor_assets', 'myplugin_enqueue_block_scripts' );

JavaScript Example for Fetching Data

Here's a simplified JavaScript example demonstrating how a Gutenberg block could fetch data from your custom endpoint. This assumes you have a way to get the user's token (e.g., by fetching it from the `/me/token` endpoint or if it's available globally).

// Assuming mypluginData.api_url is available from wp_localize_script
// And assuming you have a function to get the user's token, e.g., getUserToken()

const { registerBlockType } = wp.blocks;
const { Component } = wp.element;
const { apiFetch } = wp; // WordPress's built-in fetch wrapper

// Placeholder for getting the token. In a real app, this would involve
// fetching it from user meta via a dedicated REST endpoint or other secure means.
async function getUserToken() {
    // Example: Fetch token from a dedicated endpoint
    try {
        const response = await apiFetch({
            path: 'myplugin/v1/me/token',
            method: 'GET',
        });
        return response.token;
    } catch (error) {
        console.error('Error fetching user token:', error);
        return null;
    }
}

registerBlockType( 'myplugin/product-list', {
    title: 'Product List',
    icon: 'list-view',
    category: 'widgets',

    edit: class extends Component {
        constructor(props) {
            super(props);
            this.state = {
                products: [],
                isLoading: true,
                error: null,
                token: null,
            };
        }

        async componentDidMount() {
            const token = await getUserToken();
            if (!token) {
                this.setState({ error: 'Authentication token not found.', isLoading: false });
                return;
            }
            this.setState({ token: token });
            this.fetchProducts(token);
        }

        async fetchProducts(token) {
            try {
                const response = await fetch(mypluginData.api_url + 'products', {
                    method: 'GET',
                    headers: {
                        'Authorization': `Bearer ${token}`,
                        'Content-Type': 'application/json',
                    },
                });

                if (!response.ok) {
                    const errorData = await response.json();
                    throw new Error(errorData.message || `HTTP error! status: ${response.status}`);
                }

                const products = await response.json();
                this.setState({ products: products, isLoading: false });

            } catch (error) {
                console.error('Error fetching products:', error);
                this.setState({ error: error.message, isLoading: false });
            }
        }

        render() {
            const { products, isLoading, error } = this.state;

            if (isLoading) {
                return <div>Loading products...</div>;
            }

            if (error) {
                return <div style={{ color: 'red' }}>Error: {error}</div>;
            }

            if (products.length === 0) {
                return <div>No products found.</div>;
            }

            return (
                <ul>
                    {products.map(product => (
                        <li key={product.id}>
                            {product.name} - ${product.price}
                        </li>
                    ))}
                </ul>
            );
        }
    },

    save: () => {
        // The save function for dynamic blocks typically returns null
        // as the content is rendered server-side or fetched dynamically.
        return null;
    },
} );

In this JavaScript:

  • We use `apiFetch` (WordPress's wrapper around `fetch`) to get the user's token from our custom endpoint. This is a more secure way than passing it directly from PHP if the token isn't strictly necessary for the block's initial rendering.
  • The `fetchProducts` function makes a `GET` request to our `/products` endpoint, including the `Authorization` header with the Bearer token.
  • Error handling is included for network issues or API-level errors (e.g., invalid token, unauthorized access).
  • The `save` function for this block would typically return `null` if it's a dynamic block that renders its content on the server or fetches it client-side.

Security Considerations and Best Practices

When implementing custom API endpoints and token authentication, security must be a top priority:

  • Token Generation: Always use cryptographically secure methods for token generation (e.g., `wp_generate_password` with appropriate parameters).
  • Token Storage: Store tokens securely. User meta is generally acceptable for user-specific tokens, but ensure the database is protected. Avoid storing tokens in client-side JavaScript directly unless absolutely necessary and with strict expiration policies.
  • HTTPS: Always use HTTPS to protect tokens in transit.
  • Token Revocation: Provide a mechanism for users to revoke their API tokens (e.g., a "reset token" button on their profile).
  • Rate Limiting: Implement rate limiting on your API endpoints to prevent abuse and brute-force attacks.
  • Input Validation: Sanitize and validate all data received by your API endpoints, even if it's authenticated.
  • Principle of Least Privilege: The `permission_callback` should grant access only to users who genuinely need it for the specific endpoint. Avoid `__return_true` in production for sensitive operations.
  • Error Messages: Be careful not to reveal too much information in error messages that could aid attackers.

By following these guidelines, you can build secure and robust custom REST API endpoints for your WordPress e-commerce site, enabling powerful integrations with Gutenberg blocks and other applications.

Primary Sidebar

A little about the Author

Having 12+ Years of Experience in Software Development, Vinay is a principal software architect, senior systems engineer, and elite technical consultant. He specializes in bespoke PHP/WordPress development, high-performance Magento 2 & Shopify architectures, custom plugin/theme development from scratch, and legacy code modernization (including VB6, VB.NET, PyQt, and Crystal Reports). Known for solving complex database bottlenecks, speed optimization (Core Web Vitals), and advanced security code auditing, Vinay engineers production-ready systems designed to scale under heavy concurrent load conditions.



Chat on WhatsApp

Recent Posts

  • How to analyze and reduce CPU consumption of custom Singleton Registry Pattern event mediators
  • How to analyze and reduce CPU consumption of custom Factory Method design structures event mediators
  • WordPress Development Recipe: High-efficiency server-side rendering for Gutenberg blocks using Readonly classes
  • How to securely integrate SendGrid transactional mailer endpoints into WordPress custom plugins using Filesystem API
  • How to design secure Algolia Search API webhook listeners using signature validation and payload queues

Categories

  • apache (1)
  • Business & Monetization (390)
  • Centos (4)
  • Comparisons & Decision Making (55)
  • Debian (2)
  • Debugging & Troubleshooting (658)
  • Desktop Applications (14)
  • DevOps (7)
  • DevOps & Cloud Scaling (962)
  • Django (1)
  • Laravel (4)
  • Migration & Architecture (192)
  • Mobile Applications (24)
  • MySQL (1)
  • Performance & Optimization (872)
  • PHP (5)
  • PHP Development (42)
  • Plugins & Themes (244)
  • Programming Languages (9)
  • Python (20)
  • Ruby on Rails (1)
  • Security & Compliance (639)
  • SEO & Growth (492)
  • Server (23)
  • Ubuntu (9)
  • VB6 & VB.NET (8)
  • Web Applications & Frontend (19)
  • Web Assembly (Wasm) (2)
  • WordPress (22)
  • WordPress Plugin Development (114)
  • WordPress Plugin Development (123)
  • WordPress Plugin Development (330)
  • WordPress Theme Development (357)

Recent Posts

  • How to analyze and reduce CPU consumption of custom Singleton Registry Pattern event mediators
  • How to analyze and reduce CPU consumption of custom Factory Method design structures event mediators
  • WordPress Development Recipe: High-efficiency server-side rendering for Gutenberg blocks using Readonly classes

Top Categories

  • DevOps & Cloud Scaling (962)
  • Performance & Optimization (872)
  • Debugging & Troubleshooting (658)
  • Security & Compliance (639)
  • SEO & Growth (492)
  • Business & Monetization (390)

Our Products

  • ERP & LMS Systems (4)
  • Directories & Marketplaces (4)
  • Healthcare Portals (3)
  • Point of Sale (POS) (2)
  • E-Commerce Engines (2)

Our Services

  • E-Commerce Development (10)
  • WordPress Development (8)
  • Python & Desktop GUI (7)
  • General Consulting (7)
  • Legacy Modernization (5)
  • Mobile App Development (4)

Copyright © 2026 · Vinay Vengala