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

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

Leveraging WordPress Settings API for Secure, Token-Authenticated Gutenberg Endpoints

For e-commerce platforms built on WordPress, extending functionality with custom Gutenberg blocks that interact with backend settings is a common requirement. This often involves creating dedicated API endpoints to manage these settings securely. This guide details how to implement custom WordPress Settings API endpoints that are protected by token authentication, ensuring only authorized requests can modify sensitive configurations.

Registering Custom Settings and Sanitization

Before creating API endpoints, we need to define the settings themselves using the WordPress Settings API. This involves registering a setting, a section, and a field. Crucially, we must also define sanitization callbacks to ensure data integrity and security.

Defining Settings Structure

Let’s assume we’re creating settings for a hypothetical “Advanced Shipping Options” plugin. We’ll need a general option for enabling/disabling the feature and a specific option for a custom shipping rate.

Registering the Setting, Section, and Field

This code snippet demonstrates how to register these settings. It’s best placed within an `admin_init` hook in your plugin’s main file or an included administration file.

add_action( 'admin_init', 'adv_shipping_register_settings' );

function adv_shipping_register_settings() {
    // Register the main setting group
    register_setting(
        'adv_shipping_options_group', // Option group
        'adv_shipping_settings',      // Option name (stored in wp_options table)
        'adv_shipping_sanitize_settings' // Sanitize callback
    );

    // Add settings section
    add_settings_section(
        'adv_shipping_main_section',             // ID
        __( 'Advanced Shipping Options', 'advanced-shipping' ), // Title
        'adv_shipping_section_callback',         // Callback for section description
        'adv_shipping_settings_page'             // Page slug where this section appears
    );

    // Add enable/disable setting field
    add_settings_field(
        'adv_shipping_enabled',                  // ID
        __( 'Enable Advanced Shipping', 'advanced-shipping' ), // Title
        'adv_shipping_render_enabled_field',     // Callback to render the field
        'adv_shipping_settings_page',            // Page slug
        'adv_shipping_main_section'              // Section ID
    );

    // Add custom shipping rate setting field
    add_settings_field(
        'adv_shipping_custom_rate',              // ID
        __( 'Custom Shipping Rate (%)', 'advanced-shipping' ), // Title
        'adv_shipping_render_custom_rate_field', // Callback to render the field
        'adv_shipping_settings_page',            // Page slug
        'adv_shipping_main_section'              // Section ID
    );
}

// Callback for section description (optional)
function adv_shipping_section_callback() {
    echo '

' . __( 'Configure advanced shipping rules and options.', 'advanced-shipping' ) . '

'; } // Callback to render the 'Enable' checkbox field function adv_shipping_render_enabled_field() { $options = get_option( 'adv_shipping_settings' ); $enabled = isset( $options['adv_shipping_enabled'] ) ? $options['adv_shipping_enabled'] : '0'; ?> />

Creating the Settings Page

To make these settings accessible in the WordPress admin, we need to create a menu page and render the settings form.

add_action( 'admin_menu', 'adv_shipping_add_admin_menu' );

function adv_shipping_add_admin_menu() {
    add_options_page(
        __( 'Advanced Shipping Settings', 'advanced-shipping' ), // Page title
        __( 'Advanced Shipping', 'advanced-shipping' ),          // Menu title
        'manage_options',                                       // Capability required
        'adv_shipping_settings_page',                           // Menu slug
        'adv_shipping_options_page_html'                        // Callback function to render the page
    );
}

function adv_shipping_options_page_html() {
    // Check user capabilities
    if ( ! current_user_can( 'manage_options' ) ) {
        return;
    }
    ?>
    

Implementing Token-Authenticated API Endpoints

For Gutenberg blocks to interact with these settings programmatically, we need REST API endpoints. These endpoints should be secured using token authentication to prevent unauthorized access.

Generating and Storing API Tokens

A common approach is to generate a unique token for each site or user that needs API access. This token can be stored in the WordPress options table or user meta. For simplicity, we'll store it in the `wp_options` table.

// Function to generate a secure token
function adv_shipping_generate_token() {
    return bin2hex( random_bytes( 32 ) ); // Generates a 64-character hex token
}

// Hook to create a token when the plugin is activated or settings are first saved
register_activation_hook( __FILE__, 'adv_shipping_create_api_token' ); // Assuming this is in your main plugin file

function adv_shipping_create_api_token() {
    if ( false === get_option( 'adv_shipping_api_token' ) ) {
        $token = adv_shipping_generate_token();
        add_option( 'adv_shipping_api_token', $token );
    }
}

// Function to retrieve the API token
function adv_shipping_get_api_token() {
    return get_option( 'adv_shipping_api_token' );
}

// Optional: Add a UI element to display/regenerate the token on the settings page
// This requires modifying adv_shipping_options_page_html and potentially adding new settings fields.
// For this example, we'll assume the token is generated on activation and not directly managed via UI.

Registering the REST API Endpoint

We'll use the `rest_api_init` hook to register our custom endpoint. This endpoint will handle GET requests to retrieve settings and POST requests to update them.

add_action( 'rest_api_init', 'adv_shipping_register_rest_routes' );

function adv_shipping_register_rest_routes() {
    $namespace = 'advanced-shipping/v1';
    $route = '/settings';

    register_rest_route( $namespace, $route, array(
        'methods'             => WP_REST_Server::ALLMETHODS, // Allow GET, POST, etc.
        'callback'            => 'adv_shipping_handle_settings_request',
        'permission_callback' => 'adv_shipping_verify_api_token',
        'args'                => array(
            'adv_shipping_enabled' => array(
                'required'          => false,
                'type'              => 'string', // Expecting '0' or '1'
                'sanitize_callback' => 'sanitize_text_field',
                'validate_callback' => function($param, $request, $key) {
                    return ($param === '0' || $param === '1');
                }
            ),
            'adv_shipping_custom_rate' => array(
                'required'          => false,
                'type'              => 'string',
                'sanitize_callback' => 'sanitize_text_field',
                'validate_callback' => function($param, $request, $key) {
                    return (is_numeric($param) || $param === '');
                }
            ),
        ),
    ) );
}

// Callback function to handle requests
function adv_shipping_handle_settings_request( WP_REST_Request $request ) {
    $current_settings = get_option( 'adv_shipping_settings', array() );

    // Handle GET request: retrieve settings
    if ( $request->get_method() === 'GET' ) {
        return new WP_REST_Response( $current_settings, 200 );
    }

    // Handle POST request: update settings
    if ( $request->get_method() === 'POST' ) {
        $new_data = $request->get_params();
        $updated_settings = $current_settings;

        if ( isset( $new_data['adv_shipping_enabled'] ) ) {
            $updated_settings['adv_shipping_enabled'] = $new_data['adv_shipping_enabled'];
        }
        if ( isset( $new_data['adv_shipping_custom_rate'] ) ) {
            $updated_settings['adv_shipping_custom_rate'] = $new_data['adv_shipping_custom_rate'];
        }

        // Sanitize and update the option
        // We'll re-use the existing sanitize callback for consistency,
        // but a dedicated one for API might be better for complex scenarios.
        $sanitized_settings = adv_shipping_sanitize_settings( $updated_settings );

        if ( update_option( 'adv_shipping_settings', $sanitized_settings ) ) {
            return new WP_REST_Response( array( 'success' => true, 'data' => $sanitized_settings ), 200 );
        } else {
            return new WP_Error( 'settings_update_failed', __( 'Failed to update settings.', 'advanced-shipping' ), array( 'status' => 500 ) );
        }
    }

    // Handle other methods if necessary, or return an error
    return new WP_Error( 'invalid_method', __( 'Invalid request method.', 'advanced-shipping' ), array( 'status' => 405 ) );
}

// Permission callback to verify the API token
function adv_shipping_verify_api_token() {
    $token_header = 'X-API-Token'; // Custom header for the token
    $request = \WP_REST_Server::get_instance()->get_request();
    $headers = $request->get_headers();

    if ( ! isset( $headers[ $token_header ] ) ) {
        return new WP_Error( 'rest_not_logged_in', __( 'API token is missing.', 'advanced-shipping' ), array( 'status' => 401 ) );
    }

    $provided_token = $headers[ $token_header ][0]; // Headers can be arrays
    $stored_token = adv_shipping_get_api_token();

    if ( empty( $stored_token ) ) {
        // This scenario should ideally not happen if token is generated on activation.
        // It implies the token wasn't generated or was deleted.
        return new WP_Error( 'rest_token_not_configured', __( 'API token not configured on the server.', 'advanced-shipping' ), array( 'status' => 500 ) );
    }

    if ( ! hash_equals( $stored_token, $provided_token ) ) {
        return new WP_Error( 'rest_invalid_token', __( 'Invalid API token.', 'advanced-shipping' ), array( 'status' => 403 ) );
    }

    // Token is valid
    return true;
}

Integrating with Gutenberg Blocks

In your Gutenberg block's JavaScript (or React component), you can now make authenticated requests to this endpoint. Ensure you retrieve the API token and include it in the request headers.

Example: Fetching Settings in a Block Editor Component

This example uses `fetch` API in JavaScript to get the settings. You'll need to pass the API token to your block's JavaScript, perhaps via `wp_localize_script` or by embedding it in the block's attributes if it's safe to do so (consider security implications).

// Assuming 'advShippingSettings' is an object localized to your script
// containing { apiUrl: '...', apiToken: '...' }

const { apiFetch } = wp; // Or use native fetch

async function fetchAdvancedShippingSettings() {
    const apiUrl = advShippingSettings.apiUrl; // e.g., '/wp-json/advanced-shipping/v1/settings'
    const apiToken = advShippingSettings.apiToken;

    try {
        const response = await fetch( apiUrl, {
            method: 'GET',
            headers: {
                'X-API-Token': apiToken,
                'Content-Type': 'application/json',
            },
        });

        if (!response.ok) {
            throw new Error(`HTTP error! status: ${response.status}`);
        }

        const settings = await response.json();
        console.log('Advanced Shipping Settings:', settings);
        return settings;

    } catch (error) {
        console.error('Error fetching advanced shipping settings:', error);
        return null;
    }
}

// Example usage within a Gutenberg block's edit function
// (This is a simplified conceptual example)
import { useState, useEffect } from '@wordpress/element';
import { TextControl } from '@wordpress/components';

function Edit( { attributes, setAttributes } ) {
    const [ settings, setSettings ] = useState( null );
    const [ isLoading, setIsLoading ] = useState( true );

    useEffect( () => {
        async function loadSettings() {
            const fetchedSettings = await fetchAdvancedShippingSettings();
            if (fetchedSettings) {
                setSettings( fetchedSettings );
                // Potentially pre-fill block attributes from fetched settings
                if (fetchedSettings.adv_shipping_enabled) {
                    setAttributes( { isAdvancedShippingEnabled: fetchedSettings.adv_shipping_enabled === '1' } );
                }
                if (fetchedSettings.adv_shipping_custom_rate) {
                    setAttributes( { customShippingRate: fetchedSettings.adv_shipping_custom_rate } );
                }
            }
            setIsLoading( false );
        }
        loadSettings();
    }, [] );

    const handleSettingChange = async ( key, value ) => {
        // Update local state immediately for responsiveness
        const updatedSettings = { ...settings, [key]: value };
        setSettings( updatedSettings );

        // Update block attributes if applicable
        if (key === 'adv_shipping_enabled') {
            setAttributes( { isAdvancedShippingEnabled: value === '1' } );
        }
        if (key === 'adv_shipping_custom_rate') {
            setAttributes( { customShippingRate: value } );
        }

        // Send update to the backend
        const apiUrl = advShippingSettings.apiUrl;
        const apiToken = advShippingSettings.apiToken;

        try {
            const response = await fetch( apiUrl, {
                method: 'POST',
                headers: {
                    'X-API-Token': apiToken,
                    'Content-Type': 'application/json',
                },
                body: JSON.stringify({ [key]: value }),
            });

            if (!response.ok) {
                throw new Error(`HTTP error! status: ${response.status}`);
            }
            const result = await response.json();
            console.log('Setting updated successfully:', result);
            // Optionally re-fetch to confirm, or trust the response
        } catch (error) {
            console.error('Error updating setting:', error);
            // Handle error, e.g., revert UI or show a message
        }
    };

    if (isLoading) {
        return 
Loading settings...
; } if (!settings) { return
Error loading settings.
; } return (
{/* Example: Displaying and updating the 'enabled' setting */} handleSettingChange( 'adv_shipping_enabled', value ? '1' : '0' ) } type="checkbox" // Note: TextControl might not be ideal for checkboxes, use ToggleControl or similar /> {/* Example: Displaying and updating the 'custom_rate' setting */} handleSettingChange( 'adv_shipping_custom_rate', value ) } /> {/* ... other block controls ... */}
); }

Security Considerations and Best Practices

  • Token Management: Never hardcode API tokens. Generate them securely and store them in the database. Consider options for token regeneration and revocation.
  • HTTPS: Always use HTTPS to protect tokens and data in transit.
  • Rate Limiting: Implement rate limiting on your API endpoints to prevent brute-force attacks.
  • Input Validation: While we've included basic validation and sanitization, always validate and sanitize all incoming data rigorously, especially when updating settings.
  • Least Privilege: Ensure the API token used has only the necessary permissions. For WordPress, this typically means the `manage_options` capability is implicitly checked by the REST API's authentication mechanisms if a user is logged in, but for token-based auth, you're defining your own layer.
  • Error Handling: Provide informative but not overly revealing error messages.
  • Endpoint Naming: Use clear and descriptive namespaces and routes for your API endpoints (e.g., `your-plugin-slug/v1/settings`).

By combining the robust WordPress Settings API with secure token-based REST API endpoints, you can build powerful and flexible Gutenberg blocks that reliably manage your e-commerce site's configurations.

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

  • Reducing database query bloat in Sage Roots modern environments layouts using custom lazy loaders
  • Performance Optimization: Tuning PHP-FPM and opcache pools for high-concurrency Firebase Realtime DB handlers
  • Reducing Largest Contentful Paint (LCP) by optimizing custom script enqueuing structures in legacy plugins
  • How to implement native Redis caching layers for high-volume custom taxonomy queries in Carbon Fields custom wrappers
  • Building secure B2B pricing grids with custom REST API Controllers endpoints and role overrides

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 (48)
  • 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 (182)
  • WordPress Plugin Development (197)
  • WordPress Plugin Development (330)
  • WordPress Theme Development (357)

Recent Posts

  • Reducing database query bloat in Sage Roots modern environments layouts using custom lazy loaders
  • Performance Optimization: Tuning PHP-FPM and opcache pools for high-concurrency Firebase Realtime DB handlers
  • Reducing Largest Contentful Paint (LCP) by optimizing custom script enqueuing structures in legacy plugins

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