Learning Vue.js as a WordPress Developer Part 4 – Authenticated Rest API

This post is part of a series called “Learning Vue.js as a WordPress Developer”

With Vue.js you fetch new data in by making web requests to WordPress. Here is a really basic example from Part 2 which fetches data from a custom WordPress REST API endpoint.

<script>
new Vue({
	el: '#app',
	data: {
  		sites: [],
	},
	mounted() {
		axios.get('/wp-json/captaincore/v1/sites')
			.then(response => {
				this.sites = response.data;
			});
	},
});
</script>

This works great for fetching public information. What about fetching private information? Well you might think you could slap in a PHP function like wp_get_current_user() when responding to that request and do some checks to determine if that user has permission. However that’s not going to work. The wp_get_current_user() will just say the user isn’t logged in even if they are. That’s because the Rest API requires the use of nonces in addition to cookie based authentication.

Add nonces for authenticated Rest API requests.

The official docs say to use wp_localize_script to add the nonces, however I believe a more direct approach would be to use the wp_add_inline_script function. Here is example of what that would look like.

function captaincore_enqueue_scripts() {
    if ( is_user_logged_in() ) {
        $wpApiSettings = json_encode( array( 
            'root' => esc_url_raw( rest_url() ),
            'nonce' => wp_create_nonce( 'wp_rest' )
        ) );
        $wpApiSettings = "var wpApiSettings = ${wpApiSettings};";
        wp_register_script( 'captaincore-wp-api', '' );
        wp_enqueue_script( 'captaincore-wp-api' );
        wp_add_inline_script( 'captaincore-wp-api', $wpApiSettings );
    }
}
add_action( 'wp_enqueue_scripts', 'captaincore_enqueue_scripts' );

This will simply add the following inline script tag to all pages if logged in.

<script type='text/javascript'>
var wpApiSettings = {"root":"https:\/\/anchor.test\/wp-json\/","nonce":"1fc1972682"};
</script>

This new wpApiSettings Javascript variable can be passed within Vue.js as headers when making a request. Here is what that looks like when making a request using Axios.

axios.get(
    '/wp-json/captaincore/v1/sites', {
        headers: {'X-WP-Nonce':wpApiSettings.nonce}
    })
    .then(response => {
        this.sites = response.data;
    });

On the PHP side we can now see who is logged in and do all of fancy private logic 🔐.

// Custom endpoint for CaptainCore sites
register_rest_route(
    'captaincore/v1', '/sites/', array(
        'methods'       => 'GET',
        'callback'      => 'captaincore_sites_func',
        'show_in_index' => false
    )
);

function captaincore_sites_func( $request ) {
	// Functions like wp_get_current_user will now work properly here and in lower levels.

	$user       = wp_get_current_user();
	$role_check = in_array( 'administrator', $user->roles ) + in_array( 'editor', $user->roles );

	// Bail if not assigned a role
	if ( ! $role_check ) {
		return 'Error: Please log in.';
	}

	$sites = (new CaptainCore\Sites())->all();
	return $sites;
}