How to authenticate WordPress user with JWT token

In this article you’ll learn how to issue JWT authentication token with AAM 5.2 or higher and use it in the subsequent HTTP requests to your WordPress website. You can get the shared postman collection for this article here.

Per IETF description, JSON Web Token (JWT) is a compact, URL-safe means of representing claims to be transferred between two parties. I assume that you know enough about JWT so let’s focus only on how to obtain and use it with the sequence of following steps.

1. Enable JWT Authentication. By default the JWT Authentication feature is disabled however you can enable it on the Settings Area with JWT Authentication option.

2. Configure JWT feature with ConfigPress (optional). Go to AAM Settings Area and on the ConfigPress tab define following configurations:

  • authentication.jwt.secret (Since AAM v5.3.4). Define the secret key that is used to issue the JWT token. The default key is SECURE_AUTH_KEY value that is defined in the website wp-config.php file;
  • authentication.jwt.expires. Define how long in seconds, the issued JWT token should be considered as valid. The default value is 24 hours;
  • authentication.jwt.container (Since AAM v5.4.3). Define the where AAM should look for the JWT token for each HTTP request. The default value is ‘header’ however you can also specify ‘cookie’ and in this way AAM will set a cookie with issued JWT token;
  • authentication.jwt.algorithm (Since AAM v5.4.3). Define the algorithm that is used to sign the JWT token. The default value is ‘HS256’ however you can also specify ‘HS384’, ‘HS512’ and ‘RS256’.

AAM JWT Secret Key

3. Issue JWT token with /aam/v1/authenticate POST request. First of all, make sure that you have WordPress REST API enabled. Then you should be able to hit a custom endpoint /aam/v1/authenticate with HTTP POST request with username/password in the HTTP payload. Below check raw HTTP request sample and JavaScript sample code.

POST /wp-json/aam/v1/authenticate HTTP/1.1
Host: example.org
Content-Type: application/json
Accept: application/json
Cache-Control: no-cache

{
	"username": "admin",
	"password": "somestrongpassword"
}
var data = JSON.stringify({
  "username": "admin",
  "password": "somestrongpassword"
});

var xhr = new XMLHttpRequest();
xhr.withCredentials = true;

xhr.addEventListener("readystatechange", function () {
  if (this.readyState === 4) {
    console.log(this.responseText);
  }
});

xhr.open("POST", "https://example.org/wp-json/aam/v1/authenticate");
xhr.setRequestHeader("Content-Type", "application/json");
xhr.setRequestHeader("Accept", "application/json");
xhr.setRequestHeader("Cache-Control", "no-cache");

xhr.send(data);

Note! Your application is responsible for storing and managing received JWT token as well as any error messages.

4. Parse response and store JWT token. There are two possible HTTP responses. On success you’ll get HTTP status code 200 (OK) with the token, expiration time and the entire WP_User object.

{
    "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1MjE0NjUzODgsImV4cCI6MTUyMTU1MTc4OCwidXNlcklkIjoxfQ.H_k43Ml4TxSLhc9a6qeI0kHxoXXjVh43IeLPSIGkAoM",
    "token_expires": 1521551788,
    "user": {
        "data": {
            "ID": "1",
            "user_login": "admin",
            "user_pass": "xxxxxxxxxxxx", // masked in this example
            "user_nicename": "admin",
            "user_email": "xxxxxxx", // masked in this example
            "user_url": "",
            "user_registered": "2016-06-10 12:23:24",
            "user_activation_key": "",
            "user_status": "0",
            "display_name": "Vasyl Martyniuk"
        },
        "ID": 1,
        "caps": {
            "administrator": true,
            // ...
        },
        "cap_key": "wp_capabilities",
        "roles": {
            "0": "administrator",
        },
        "allcaps": {
            "switch_themes": true,
            "edit_themes": true,
            "activate_plugins": true,
            // ...
        },
        "filter": null
    }
}

On failure, you’ll get HTTP 403 (Forbidden) response with the list of errors:

{
    "errors": {
        "incorrect_password": [
            "ERROR: The password you entered for the username admin is incorrect. Lost your password?"
        ]
    },
    "error_data": []
}

AAM JWT token generation process can be customized in 2 different places. If you want to modify the list of JWT claims, then use aam-jwt-claims-filter filter. In case you need to modify the HTTP response for successful or failed scenarios, use aam-jwt-response-filter filter.

5. Include JWT token in subsequent HTTP request. All subsequent requests that require user authentication may include Authentication header with Bearer JWT token. However if you chose ‘cookie’ container with authentication.jwt.container (see step 2), then make sure that your HTTP request includes the ‘aam-jwt’ cookie.

Note! AAM does not use standard Authorization header as it is skipped by most Apache servers. Instead of doing all these crazy hacks in the .htaccess or Apache configs, you can simply use Authentication header.

GET /wp-json/wp/v2/posts/1 HTTP/1.1
Host: example.org
Authentication: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1MjE0NTI5NDQsImV4cCI6MTUyMTUzOTM0NCwidXNlcklkIjoxfQ.uTAmX11NAYZ1qddRzwDW3iFa--sIqkps9j35LG0CP1o
Cache-Control: no-cache

In case you need to use a different header for the JWT token, use aam-authentication-header-filter filter that should return valid JTW token in response.

Conclusion

WP RESTful API is the future for the WordPress CMS and definitely having reliable way to authenticate user is the first thing on the list. With the help of the free AAM plugin you do not have to worry about the technical aspect of the JWT issuer and validator. Instead you can focus on building awesome frontend or server-side applications that integrate with your WordPress website.

Get notified about important updates and new features (no more than one email per month).