• 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 Rewrite API custom endpoints endpoints with token authentication in Gutenberg blocks

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

Leveraging WordPress Rewrite API for Secure Custom Endpoints

WordPress’s Rewrite API offers a powerful mechanism for creating custom endpoints that can be accessed via RESTful URLs. This is particularly useful for building dynamic Gutenberg blocks that require server-side data fetching or manipulation. However, exposing these endpoints without proper authentication is a significant security risk. This guide details how to implement custom endpoints with token-based authentication, ensuring only authorized requests can access your data.

Defining Custom Rewrite Rules and Endpoints

The first step is to register your custom rewrite rules and the corresponding callback functions that will handle the requests. We’ll use the add_rewrite_rule function, hooked into init, to achieve this. For this example, let’s assume we’re creating an endpoint to fetch user-specific data.

add_action( 'init', function() {
    // Register a custom rewrite rule for our endpoint
    // Example: yoursite.com/api/v1/user-data/(.*)/?$
    add_rewrite_rule(
        '^api/v1/user-data/(.+?)/?$',
        'index.php?custom_endpoint=user_data&user_id=$matches[1]',
        'top' // 'top' ensures this rule is checked before default WordPress rules
    );

    // Add our custom query variable
    add_filter( 'query_vars', function( $query_vars ) {
        $query_vars[] = 'custom_endpoint';
        $query_vars[] = 'user_id';
        return $query_vars;
    } );

    // Flush rewrite rules on plugin activation/deactivation or theme switch
    // This is crucial for the new rules to take effect.
    // In a real plugin, you'd hook this into activation/deactivation hooks.
    // For a theme, it might be on theme switch.
    // flush_rewrite_rules(); // Uncomment and run once, or manage via activation hooks.
});

In this code:

  • add_rewrite_rule: Defines the URL pattern (regex) and the query string it should map to. (.+?) captures the user ID.
  • index.php?custom_endpoint=user_data&user_id=$matches[1]: This is the internal WordPress query that will be triggered. We’re using a custom query variable custom_endpoint to identify our handler and user_id to pass the captured value.
  • add_filter( 'query_vars', ... ): Registers our custom query variables so WordPress recognizes them.
  • flush_rewrite_rules(): This function is essential. It needs to be called whenever rewrite rules are added or modified. In a production environment, this should be handled by plugin activation/deactivation hooks to avoid performance overhead on every page load.

Handling the Custom Endpoint Request

Now, we need a callback function to process the request when our custom endpoint is hit. We’ll hook into the template_redirect action, which fires before WordPress determines which template to load. This is a good place to intercept requests for our custom endpoints.

add_action( 'template_redirect', function() {
    // Get our custom query variables
    $custom_endpoint = get_query_var( 'custom_endpoint' );
    $user_id = get_query_var( 'user_id' );

    // Check if our custom endpoint is being accessed
    if ( $custom_endpoint === 'user_data' && ! empty( $user_id ) ) {
        // Authentication check will go here
        // For now, let's just return some dummy data
        $user_data = array(
            'id'    => absint( $user_id ),
            'name'  => 'Sample User',
            'email' => '[email protected]',
        );

        // Set the content type to JSON
        header( 'Content-Type: application/json' );
        // Output the JSON data
        echo json_encode( $user_data );

        // Stop further WordPress execution
        exit;
    }
});

This callback:

  • Retrieves the values of our custom query variables.
  • Checks if the custom_endpoint is user_data and if a user_id is present.
  • (Placeholder) Includes a comment for where the authentication logic will be implemented.
  • Sets the Content-Type header to application/json.
  • Encodes and outputs the data as JSON.
  • Uses exit; to prevent WordPress from loading its standard templates, ensuring only our JSON response is sent.

Implementing Token-Based Authentication

For secure API access, we need to implement authentication. A common and effective method is using token-based authentication. This involves generating a unique token for each user or for specific API access, and requiring this token to be sent with each request. We’ll use a custom meta field to store the user’s API token.

Generating and Storing API Tokens

You can generate tokens manually or programmatically. For simplicity, let’s assume you’ll add a custom field to the user profile page in the WordPress admin area to store the token. You’ll need to add this field using the show_user_profile and edit_user_profile actions, and save it using personal_options_update and edit_user_profile_update.

// Add field to user profile page
add_action( 'show_user_profile', 'my_show_extra_profile_fields' );
add_action( 'edit_user_profile', 'my_show_extra_profile_fields' );

function my_show_extra_profile_fields( $user ) {
    $api_token = get_user_meta( $user->ID, 'api_token', true );
    ?>
    

<?php esc_html_e( 'API Settings', 'textdomain' ); ?>

<table class="form-table"> <tr> <th><label for="api_token"><?php esc_html_e( 'API Token', 'textdomain' ); ?></label></th> <td> <input type="text" name="api_token" id="api_token" value="<?php echo esc_attr( $api_token ); ?>" class="regular-text" readonly="readonly" /> <p class="description"><?php esc_html_e( 'This is your API access token. Keep it secure.', 'textdomain' ); ?></p> <button type="button" id="generate_new_token"><?php esc_html_e( 'Generate New Token', 'textdomain' ); ?></button> </td> </tr> </table> <script type="text/javascript"> jQuery(document).ready(function($) { $('#generate_new_token').on('click', function(e) { e.preventDefault(); // In a real scenario, this would make an AJAX call to generate a new token // For now, we'll just alert the user. alert('Token generation logic would go here. You would typically send an AJAX request to a secure endpoint.'); // Example: // $.post(ajaxurl, { action: 'generate_new_api_token' }, function(response) { // if (response.success) { // $('#api_token').val(response.data.token); // } else { // alert('Error generating token.'); // } // }); }); }); </script> 'Unauthorized' ), 403 ); } $user_id = isset( $_POST['user_id'] ) ? intval( $_POST['user_id'] ) : 0; if ( ! $user_id ) { wp_send_json_error( array( 'message' => 'Invalid user ID' ), 400 ); } // Generate a secure token $new_token = wp_generate_password( 64, false ); // Generates a 64-character random string // Save the new token update_user_meta( $user_id, 'api_token', $new_token ); wp_send_json_success( array( 'token' => $new_token ) ); });

This code snippet:

  • Adds an “API Settings” section to the user profile page.
  • Displays a read-only input field for the API token.
  • Includes a “Generate New Token” button. In a production scenario, this button would trigger an AJAX request to a handler that generates a secure token (e.g., using wp_generate_password) and saves it via update_user_meta.
  • The AJAX handler (wp_ajax_generate_new_api_token) demonstrates how to securely generate and save a new token.

Validating the API Token

Now, let’s integrate the token validation into our template_redirect callback. The token is typically sent in the Authorization header (e.g., Authorization: Bearer YOUR_API_TOKEN) or as a query parameter. Using the Authorization header is generally more secure.

add_action( 'template_redirect', function() {
    $custom_endpoint = get_query_var( 'custom_endpoint' );
    $user_id = get_query_var( 'user_id' );

    if ( $custom_endpoint === 'user_data' && ! empty( $user_id ) ) {

        // --- Authentication Check ---
        $provided_token = '';

        // 1. Check Authorization header
        if ( isset( $_SERVER['HTTP_AUTHORIZATION'] ) ) {
            $auth_header = $_SERVER['HTTP_AUTHORIZATION'];
            // Expecting "Bearer YOUR_TOKEN"
            if ( preg_match( '/Bearer\s+(.+)/i', $auth_header, $matches ) ) {
                $provided_token = $matches[1];
            }
        }
        // 2. Fallback: Check for token in query parameters (less secure, use with caution)
        // elseif ( isset( $_GET['api_token'] ) ) {
        //     $provided_token = sanitize_text_field( $_GET['api_token'] );
        // }

        if ( empty( $provided_token ) ) {
            header( 'HTTP/1.1 401 Unauthorized' );
            echo json_encode( array( 'error' => 'Authentication token is missing.' ) );
            exit;
        }

        // Retrieve the stored token for the requested user
        $stored_token = get_user_meta( absint( $user_id ), 'api_token', true );

        // Verify the token
        if ( empty( $stored_token ) || ! hash_equals( $stored_token, $provided_token ) ) {
            header( 'HTTP/1.1 401 Unauthorized' );
            echo json_encode( array( 'error' => 'Invalid authentication token.' ) );
            exit;
        }
        // --- End Authentication Check ---

        // If authentication passes, proceed to fetch data
        $user_data = array(
            'id'    => absint( $user_id ),
            'name'  => 'Sample User', // In a real app, fetch this from WP_User object
            'email' => '[email protected]', // In a real app, fetch this from WP_User object
        );

        header( 'Content-Type: application/json' );
        echo json_encode( $user_data );
        exit;
    }
});

Key additions in this section:

  • The code now attempts to retrieve the token from the Authorization header, specifically looking for the Bearer scheme.
  • A fallback to checking $_GET['api_token'] is commented out but can be enabled if necessary, though it’s less secure.
  • If no token is provided, a 401 Unauthorized HTTP response is sent.
  • It retrieves the user’s stored API token using get_user_meta.
  • hash_equals() is used for secure, timing-attack-resistant comparison of the provided token against the stored token.
  • If the tokens do not match, another 401 Unauthorized response is sent.
  • Only if authentication is successful does the code proceed to fetch and return the user data.

Integrating with Gutenberg Blocks

To use this custom endpoint from a Gutenberg block, you’ll typically make an AJAX request from the block’s JavaScript. The block editor provides a global wp.apiFetch utility, which is a wrapper around the Fetch API and is aware of WordPress’s REST API. However, for custom endpoints not registered with the REST API, you’ll use the standard Fetch API or a library like Axios.

// Example: Registering a script for your block
add_action( 'enqueue_block_editor_assets', function() {
    wp_enqueue_script(
        'my-custom-block-editor-script',
        get_template_directory_uri() . '/path/to/your/block-editor.js', // Adjust path
        array( 'wp-blocks', 'wp-element', 'wp-editor', 'wp-api-fetch' ),
        filemtime( get_template_directory() . '/path/to/your/block-editor.js' ) // Adjust path
    );
});

And in your block-editor.js file:

// Assuming you have a block component that needs to fetch data
// For example, within a React component used by your Gutenberg block

import apiFetch from '@wordpress/api-fetch'; // For standard WP REST API endpoints
// For custom endpoints, we'll use the native fetch API

const userIdToFetch = 1; // Example user ID
const apiToken = 'YOUR_USER_API_TOKEN'; // This should be dynamically retrieved or securely managed

async function fetchUserData(userId, token) {
    const endpointUrl = `/api/v1/user-data/${userId}/`; // Your custom endpoint URL

    try {
        const response = await fetch(endpointUrl, {
            method: 'GET',
            headers: {
                'Authorization': `Bearer ${token}`,
                'Content-Type': 'application/json',
            },
        });

        if (!response.ok) {
            // Handle errors, e.g., 401 Unauthorized, 404 Not Found
            const errorData = await response.json();
            console.error('API Error:', response.status, errorData);
            throw new Error(`HTTP error! status: ${response.status}`);
        }

        const data = await response.json();
        console.log('User Data:', data);
        return data;

    } catch (error) {
        console.error('Fetch Error:', error);
        // Handle network errors or other exceptions
        return null;
    }
}

// Example usage within a block component's lifecycle method or event handler:
// componentDidMount() {
//     fetchUserData(userIdToFetch, apiToken)
//         .then(data => {
//             if (data) {
//                 this.setState({ userData: data });
//             }
//         });
// }

In the JavaScript:

  • We define the URL for our custom endpoint.
  • We use the native fetch API.
  • Crucially, we set the Authorization header with the Bearer token.
  • Error handling is included to catch non-OK HTTP responses (like 401s) and network errors.
  • The response is parsed as JSON.

Important Security Note: Hardcoding API tokens directly in JavaScript is a major security vulnerability. In a real-world application, you would need a secure mechanism to:

  • Retrieve the API token for the currently logged-in user on the frontend (e.g., via a nonce-protected AJAX call to a custom endpoint that returns the token, or by embedding it securely in the page data if appropriate for your use case).
  • For Gutenberg blocks, consider using wp.data.select('core').getCurrentUser().id to get the current user ID and then making a secure AJAX call to fetch their token.
  • Alternatively, if the block is for administrators only, you might embed the token in the script’s data during enqueuing, but this still requires careful consideration of who can access the admin area.

Advanced Considerations and Best Practices

  • Rate Limiting: Implement rate limiting on your custom endpoints to prevent abuse and brute-force attacks. This can be done at the server level (e.g., via Nginx) or within your PHP code.
  • HTTPS: Always use HTTPS to encrypt communication and protect tokens in transit.
  • Token Expiration and Revocation: For enhanced security, consider implementing token expiration and providing a mechanism for users to revoke their tokens. This would involve storing an expiration timestamp with the token and adding a “revoke token” feature.
  • Logging: Log authentication attempts (both successful and failed) to monitor for suspicious activity.
  • Error Handling: Provide informative but not overly revealing error messages.
  • Nonces: For AJAX requests initiated from the WordPress admin or frontend that modify data, always use WordPress nonces to verify the request’s origin and prevent CSRF attacks. While our GET request example doesn’t strictly need a nonce for fetching data, any POST/PUT/DELETE operations would.
  • REST API vs. Rewrite API: For standard CRUD operations on WordPress objects, the built-in REST API is often a better choice as it’s well-documented and has built-in security features. The Rewrite API is best suited for highly custom endpoints or when you need complete control over the URL structure and request handling.

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

  • Advanced Diagnostics: Locating slow Command Query Responsibility Segregation (CQRS) query bottlenecks in WooCommerce custom checkout pipelines
  • How to design a modular Domain-driven architecture (DDD) blocks architecture for enterprise-level custom plugins
  • Step-by-Step Guide to building a custom real-time audit dashboard block for Gutenberg using Svelte standalone templates
  • Designing audit logs for enterprise WordPress setups tracking internal user modifications to custom product catalogs
  • Troubleshooting broken WP-Cron schedules in production when using modern Classic Core PHP wrappers

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 (43)
  • 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 (137)
  • WordPress Plugin Development (149)
  • WordPress Plugin Development (330)
  • WordPress Theme Development (357)

Recent Posts

  • Advanced Diagnostics: Locating slow Command Query Responsibility Segregation (CQRS) query bottlenecks in WooCommerce custom checkout pipelines
  • How to design a modular Domain-driven architecture (DDD) blocks architecture for enterprise-level custom plugins
  • Step-by-Step Guide to building a custom real-time audit dashboard block for Gutenberg using Svelte standalone templates

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