Using the WordPress REST API with JWT Authentication

Kevin Firko
Published:

The WordPress core now supports a new REST API as of version 4.8.x. Among a sea of new possibilities, one can now build a front-end for a website or app with a framework like React or Vue.js and use WordPress and its familiar admin dashboard to manage the back-end.

This guide covers adding JSON Web Tokens (JWT) authentication support with the JWT Authentication for WP REST API plugin, and sending requests to the API using Postman.

To access the REST interface of a WordPress-powered site append /wp-json/wp/v2/ to the URL. For example, this blog’s REST API is at: https://firxworx.com/wp-json/wp/v2/

Resources:

REST API Authentication

WordPress’ REST API only supports cookie authentication out-of-the-box. This is the same method that WordPress uses by default to authenticate users that use the login form. The idea is that theme and plugin developers can authenticate themselves, write javascript with the JS API, and be on their merry way.

Enabling remote applications with Basic Auth, OAuth, and/or JWT

To support remote applications, we need to add a new REST API authentication method using a plugin. Currently supported options are Basic Auth, OAuth, and JWT:

  • Basic Auth with a username and password is considered insecure and should only be used in development scenarios
  • OAuth is great but it can be a pain to authenticate
  • JWT is awesome and works great with front-end frameworks

JSON Web Tokens are an open industry standard: IETF RFC 7519

Adding JWT Authentication to the REST API

If your WordPress is accessible via the Internet, it is important to enable SSL/https before proceeding.

Start by installing the JWT Authentication for WP REST API plugin but don’t activate it just yet.

Next, ensure your web server supports the HTTP Authorization Header. If you are using a shared host, this is often disabled by default. To enable it, add the following to your WordPress’ .htaccess file:

RewriteEngine on
RewriteCond %{HTTP:Authorization} ^(.*)
RewriteRule ^(.*) - [E=HTTP_AUTHORIZATION:%1]

Edit your wp-config.php file and add the following lines before the comment that says “That’s all, stop editing!”:

define('JWT_AUTH_SECRET_KEY', 'really-secret-key-here');
define('JWT_AUTH_CORS_ENABLE', true);

Change really-secret-key-here in the above to a random string. If you need help obtaining some randomness, copy-and-paste some output from WordPress.org’s secret key service: https://api.wordpress.org/secret-key/1.1/salt/

JWT uses the secret JWT_AUTH_SECRET_KEY to sign JSON Web Tokens. If it is compromised, then your site’s security is compromised! Keep it safe!

The JWT_AUTH_CORS_ENABLE line activates CORs to enhance security. Refer to the plugin docs if you need to modify the default available headers: ‘Access-Control-Allow-Headers, Content-Type, Authorization’

Finally, activate the JWT Authentication for WP REST API plugin!

New endpoints for JWT authentication

The plugin will add the /jwt-auth/v1 namespace with two new endpoints:

  1. /wp-json/jwt-auth/v1/token (POST)
  2. /wp-json/jwt-auth/v1/token/validate (POST)

The first endpoint accepts POST requests that contain a username and password in the body.

  • If the credentials check out, the plugin will issue a 200 response containing a JSON object with: token, user_display_name, user_email and user_nicename.
  • If authentication fails, the response includes an object with the properties: code, data, and message.

The token included in the API’s response can then be included in the HTTP Authorization header of any subsequent requests to WordPress’ REST API. Front-end applications will need to store it somewhere, such as in a cookie or localstorage.

The second endpoint simply validates tokens. If sent a POST request with a valid token in the HTTP Auth header, it will return the following response:

{
  "code": "jwt_auth_valid_token",
  "data": {
    "status": 200
  }
}

You can confirm that JWT is now available by accessing https://yoursite.com/wp-json/ and inspecting the response body to see if the /jwt-auth/v1 and /jwt-auth/v1/token endpoints are available.

Sending requests to the REST API

Unauthenticated request

Start by making a request to the REST API that doesn’t require authentication. This “smoke check” helps confirm that the API is working and that your site’s permalinks are configured correctly.

Open up Postman to send a new GET request to https://your-site.com/wp-json/wp/v2/posts.

You should expect a successful response with a body that contains a JSON representation of your blog’s posts.

If you need to troubleshoot, check your permalink settings in the admin at: Settings -> Permalinks -> Common Settings. Make sure that your server supports URL rewriting (e.g. mod_rewrite for Apache) and that the API’s URL’s resolve correctly.

Authenticating with JWT

In Postman, prepare a new POST request to the jwt-auth/v1/token endpoint (e.g. https://your-site.com/wp-json/jwt-auth/v1/token). Navigate to the Body tab and:

  • ensure the type of request is form-data
  • specify the username and password fields with valid user credentials
  • Fire the request.

If authentication is successful, you’ll get a reply like the following (note: I truncated the token in my example so expect a much longer string).

{
    "token": "eyJ0eXAiOiJKV1QiLC_A_RIDICULOUSLY_LONG_STRING_Ky4Y",
    "user_email": "example@firxworx.com",
    "user_nicename": "example",
    "user_display_name": "Mr Example"
}

Save the token so that you can include it in subsequent requests to the API.

Using the JWT token in subsequent requests

Now send an API request where authentication is required: creating a post.

In Postman, prepare a new POST request to the URL https://your-site.com/wp-json/wp/v2/posts and set the following:

  • in the Headers tab, add the following key value pairs:
    • Content-Type + application/json
    • accept + application/json
    • Authorization + Bearer YOUR_TOKEN_HERE
  • in the Body tab, add the following key value pairs:
    • title + ‘your post title’
    • content + ‘your post body’
    • status + draft (or publish if you prefer)

The crucial entry is the Authorization header. It is imperative that the token be prefixed with the string Bearer followed by a space. Do not forget the space character!

A successful response body will include a JSON object with the new post id, date, and a bunch of other data about your new post.

If you get an error response, double check all of the key value pairs in the Headers and Body. Ensure that you copied your token exactly and in its entirety. You can use the JWT endpoints to validate your token or you could try re-authenticating to obtain a fresh token.

The following example shows how to create a post via the REST API using JS/ES with the fetch() API:

var token = YOUR_TOKEN_HERE;
fetch('https://your-site/wp-json/wp/v2/posts', {
    method: "POST",
    headers: {
        'Content-Type': 'application/json',
        'Accept': 'application/json',
        'Authorization': 'Bearer ' + token
    },
    body: JSON.stringify({
        title: 'Lorem ipsum',
        content: 'Lorem ipsum dolor sit amet.',
        status: 'draft'
    })
}).then(function(response){
    return response.json();
}).then(function(post){
    console.log(post);
});

Troubleshooting

If you find you can’t make authenticated requests, such as when you try to create a post, you may have an issue with the request’s Authorization header not being passed to PHP.

It turns out that many shared web hosts disable the Authorization header by default on Apache (or Apache-like) web servers. This can also be related to using fcgi_module instead of php5_module to support PHP.

A solution is to add the following directive to the top of your WordPress’ .htaccess file. If you are not on a shared hosting environment, other possible locations for this directive are in a VirtualHost configuration or apache server conf:

SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1

Note that some commenters have reported that the lowercase authorization worked for them vs. Authorization, so if you experience an issue, try the alternate capitalization.

This directive tells Apache to pass the Authorization header to php by setting an environment variable, such that it is available in PHP as $_SERVER['HTTP_AUTHORIZATION'].

If you update a conf file rather than your .htaccess file, don’t forget to restart apache for the setting to take effect!

If that approach fails, another solution to try is adding the following to the top of the .htaccess file in your WordPress’ root folder:

RewriteEngine on
RewriteCond %{HTTP:Authorization} ^(.*)
RewriteRule ^(.*) - [E=HTTP_AUTHORIZATION:%1]

This approach relies on mod_rewrite and may not be as appropriate for some cases because the $_SERVER['REDIRECT_HTTP_AUTHORIZATION'] variable is set for PHP instead of the standard $_SERVER['HTTP_AUTHORIZATION'].