Restrict access to WordPress REST API

Is there a way that I can restrict access to url calls made to WP REST API? I am using WP REST API to create AJAX feeds that can be accessed through the URL. They are formatted like this: http://example.com/wp-json/posts?type=post&filter[posts_per_page]=10

The problem is that anyone can add /wp-json/posts?type=post&filter[posts_per_page]=10 to the end of my URL and retrieve a feed of this information. I want to turn this off when users are not logged into WordPress doing something like this:

Read More
if ( !is_user_logged_in()) {
    // Turn off REST API feed
}

Or, I would like to add some kind of authentication that needs to be added to mask the api.

I found something like this online but I have not had any luck getting it to work. I added it to a custom plugin. Unfortunately I am still able to access the feed when not logged in.

add_action( 'init', function() {
    global $wp_post_types;
    $wp_post_types['post']->show_in_rest = is_user_logged_in();
}, 20 );

I am worried that there is no way to make a connection between activating the API and making the HTTP request on the front end. Am I thinking about this wrong? Has anyone run into this problem?

Thanks!

Related posts

3 comments

  1. The problem and blessing of WordPress is that it allows too much flexibility, particularly when the platform provides a clean method: Require Authentication for all requests

    You can require authentication for all REST API requests by adding an
    is_user_logged_in check to the rest_authentication_errors filter.

    Note: The incoming callback parameter can be either null, a WP_Error,
    or a boolean. The type of the parameter indicates the state of
    authentication:

    • null: no authentication check has yet been performed, and the hook callback may apply custom authentication logic.
    • boolean: indicates a previous authentication method check was performed. Boolean true indicates the request was successfully
      authenticated, and boolean false indicates authentication failed.
    • WP_Error: Some kind of error was encountered.
    add_filter( 'rest_authentication_errors', function( $result ) {
        // If a previous authentication check was applied,
        // pass that result along without modification.
        if ( true === $result || is_wp_error( $result ) ) {
            return $result;
        }
    
        // No authentication has been performed yet.
        // Return an error if user is not logged in.
        if ( ! is_user_logged_in() ) {
            return new WP_Error(
                'rest_not_logged_in',
                __( 'You are not currently logged in.' ),
                array( 'status' => 401 )
            );
        }
    
        // Our custom authentication check should have no effect
        // on logged-in requests
        return $result;
    });
    

    To be fair, this is hidden in the frequently asked questions.

    Edit: To exclude jwt-auth

    global $wp;
    
    // No authentication has been performed yet.
    // Return an error if user is not logged in and not trying to login.
    if ( ! is_user_logged_in() && $wp->request !== 'wp-json/jwt-auth/v1/token' ) {
        return new WP_Error(
            'rest_not_logged_in',
            __( 'You are not currently logged in.' ),
            array( 'status' => 401 )
        );
    }
    
  2. This will remove all REST API endpoints for WordPress and Woocommerce for not logged in users:

    function myplugin_removes_api_endpoints_for_not_logged_in() {
    
        if ( ! is_user_logged_in() ) {
    
            // Removes WordpPress endpoints:
            remove_action( 'rest_api_init', 'create_initial_rest_routes', 99 );
    
            // Removes Woocommerce endpoints
            if ( function_exists('WC') )
                remove_action( 'rest_api_init', array( WC()->api, 'register_rest_routes' ), 10 );
        }
    
    } add_action('init', 'myplugin_removes_api_endpoints_for_not_logged_in');
    
  3. This will block the entire REST API for anyone not logged in:

    function no_valid_user_no_rest($user) {
        if (!$user) {
            add_filter('rest_enabled', '__return_false');
            add_filter('rest_jsonp_enabled', '__return_false');
        }
        return $user;
    }
    add_filter('determine_current_user', 'no_valid_user_no_rest', 50);
    

    Standard caveat that this just turns off REST, nothing else. Make sure it has enough priority to come after other determine_current_user filters. Did not test on multi site.

    You can add other tests to the conditional as well, if you want to filter by URL or other factors.

Comments are closed.