• Hello,

    I have a situation where I am attempting to combat nonces getting cached.

    To help with this, I setup an endpoint like so:

    function generate_nonce(WP_REST_Request $request) {
    $nonce = wp_create_nonce('wp_rest');

    $response = array(
    'nonce' => $nonce
    );

    return rest_ensure_response($response);
    }

    I am then calling this in Javascript to get a nonce, before trying to access another endpoint:

    <script>
    async function fetchNonce() {
    try {
    const response = await fetch('/wp-json/test/v1/nonce');
    if (!response.ok) {
    throw new Error('Network response was not ok ' + response.statusText);
    }
    const data = await response.json();

    console.log('Nonce:', data.nonce);

    return data.nonce;
    } catch (error) {
    console.error('Error fetching nonce:', error);
    return null;
    }
    }

    async function fetchAllPosts(nonce) {
    try {
    const response = await fetch('/wp-json/test/v1/posts', {
    method: 'GET',
    headers: {
    'X-WP-Nonce': nonce
    }
    });
    if (!response.ok) {
    throw new Error('Network response was not ok ' + response.statusText);
    }
    const data = await response.json();
    console.log('Posts:', data);
    } catch (error) {
    console.error('Error fetching posts:', error);
    }
    }

    async function executeFetches() {
    const nonce = await fetchNonce();
    if (nonce) {
    await fetchAllPosts(nonce);
    } else {
    console.error('Failed to fetch nonce.');
    }
    }

    document.addEventListener('DOMContentLoaded', () => {
    executeFetches();
    });
    </script>

    This is just to give an idea about the scenario. Getting all posts is just an example to demonstrate.

    Anyway.

    The test endpoints are like this:

    add_action('rest_api_init', 'my_custom_endpoints');

    function my_custom_endpoints() {
    register_rest_route('test/v1', '/nonce', array(
    'methods' => 'GET',
    'callback' => 'generate_nonce',
    'permission_callback' => '__return_true',
    ));

    // Endpoint for getting all posts
    register_rest_route('test/v1', '/posts', array(
    'methods' => 'GET',
    'callback' => 'get_all_posts',
    'permission_callback' => '__return_true',
    ));
    }

    This works all well and good for logged out user, as the nonce is valid as through some research it the wp_rest nonce uses user id by default for logged out user or some such?

    But as soon as a logged in user tries to generate a nonce through the endpoint and Javascript, error 403 is given even though a nonce is passed and gets created. Real headache causing and frustrating.

    Is there any direction this can be managed, or is it entirely a wrong direction?

    Thank you. ????

    Here is the get_all_posts as well if it provides some information:

    function get_all_posts(WP_REST_Request $request) {

    $nonce = $request->get_header('X-WP-Nonce');

    if (!wp_verify_nonce($nonce, 'wp_rest')) {
    return new WP_Error('rest_invalid_nonce', __('Invalid nonce'), array('status' => 403));
    }

    // Arguments for WP_Query to fetch all posts
    $args = array(
    'post_type' => 'post',
    'post_status' => 'publish',
    'posts_per_page' => -1, // Retrieve all posts
    );

    // Fetch the posts
    $query = new WP_Query($args);
    $posts = array();

    // Check if there are posts
    if ($query->have_posts()) {
    // Loop through posts and prepare data
    while ($query->have_posts()) {
    $query->the_post();
    $posts[] = array(
    'id' => get_the_ID(),
    'title' => get_the_title(),
    'content' => get_the_content(),
    'excerpt' => get_the_excerpt(),
    'date' => get_the_date(),
    'author' => get_the_author(),
    'link' => get_permalink()
    );
    }
    // Restore original Post Data
    wp_reset_postdata();
    }

    // Return the response as JSON
    return rest_ensure_response($posts);
    }
Viewing 2 replies - 1 through 2 (of 2 total)
  • Moderator bcworkz

    (@bcworkz)

    I think the fact that it at least works in some circumstances means this is a valid direction.

    You’re getting a server based 403 Forbidden response and not a Forbidden JSON response from the API, correct?

    Getting a server based 403 response for logged in users but not for non-logged in users suggests to me there may be some server or firewall configuration external to WP that needs adjustment. Not knowing anything of your server configuration, I’m guessing modSecurity needs adjustment.

    IIRC, non-logged in users are assigned user ID of 0.

    Thread Starter pressermannen

    (@pressermannen)

    if (!wp_verify_nonce($nonce, 'wp_rest')) {
    return new WP_Error('rest_invalid_nonce', __('Invalid nonce'), array('status' => 403));
    }

    This gives 403 so the nonce does not seem to be valid when a user is logged in.

    I can’t wrap my head around it.

    I am thinking along the lines of:

    • Logged in user calls Javascript that generates nonce
    • The REST API does not know the user is logged in and generates a nonce accordingly
    • The nonce comes back to the user
    • The nonce is used to call the other endpoint
    • The nonce is not valid because it was generate for a logged out user

    I may be off, but that is the only thing that comes to mind. No idea how to handle that though…

    And of course just navigating to the endpoint through the browser as a logged in user works without problems.

Viewing 2 replies - 1 through 2 (of 2 total)
  • You must be logged in to reply to this topic.