1

One of the main purposes of an API is to allow the integration of different services/systems.

Let's consider that the WordPress REST API can have both public and protected endpoints, where public endpoints do not require any form of authentication, and protected endpoints do.

  • Example of a public endpoint: GET https://main.loc/wp-json/wp/v2/posts
  • Example of a protected endpoint: POST https://main.loc/wp-json/wp/v2/posts

Internally, WordPress protects the POST endpoint like this:

WordPress 5.4: wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php:550

if ( ! current_user_can( $post_type->cap->create_posts ) ) {
    return new WP_Error(
        'rest_cannot_create',
        __( 'Sorry, you are not allowed to create posts as this user.' ),
        array( 'status' => rest_authorization_required_code() )
    );
}

Which ultimately means it's a cookie-based authentication method, through the chain current_user_can() -> _wp_get_current_user -> determine_current_user filter -> wp_validate_auth_cookie and wp_validate_logged_in_cookie hooked actions.

Those actions check if the request contains a valid authentication cookie, which is verified through the function, but it makes it difficult to generate them from an external system, because of two reasons:

  • The cookie name itself requires the knowledge of the value of COOKIEHASH, which for most websites will be a simple md5 of the site URL, but some can override this cookie hash to be something else by defining the constant to another value.
  • The cookie value, however, is the real challenge. It expects a value in this format: wordpress_logged_in_COOKIE_HASH=username|expiration|token|hmac

However, AFAIK there is no way to get a token outside the context of WordPress itself, so I can't possibly send an cookie-based authenticated request from a third party, only from within WordPress itself.

I understand there are initiatives like https://github.com/WP-API/jwt-auth to provide a JWT based authentication method. However, I as a plugin developer would like to avoid depending on another third-party plugin to provide authentication to my protected endpoints.

The only place I've ever seen something like what I would like to achieve is in WooCommerce. It hooks itself to the determine_current_user to add it's own authentication logic, on top of the default cookie-based one: https://github.com/woocommerce/woocommerce/blob/master/includes/class-wc-rest-authentication.php#L69-L90

Which adds Basic-Auth authentication for websites with HTTPS, and OAuth for HTTP.

For Basic Auth, it requires you to go to your dashboard WooCommerce -> Settings -> REST API -> Create a new key, and assign that key to an user. That will give you an Consumer key and Consumer secret that you can use to send authenticated requests:

Authenticated request: authenticated request

Same request, without authentication: same request, without authentication

My understanding, after all, is that WordPress 5.4 REST API therefore does not provide a way to send authenticated requests from external servers to protected endpoints out-of-the-box, and you can either go with a JWT third-party route, or build a robust solution to provide Basic Auth or OAuth authentication methods, like WooCommerce have done. Is that right?

0

Answering my own question.

For plugin developers, the directive is to use current_user_can() in your code as usual in the REST endpoints, as Core does.

WordPress 5.4 does not support authenticated requests originated from outside WordPress to the REST API yet. But your clients can use plugins such as Basic Auth, OAuth2 or JWT to add that functionality.

The REST team in Wordpress is working to add a built-in authentication method into Core, most likely, OAuth2. Now is May 01 2020. My blatant, probably wrong guess, is that this could be coming to Core in 12~ months or so.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.