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 /wp-json/wp/v2/posts
- Example of a protected endpoint:
POST /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 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: .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:
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?
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/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/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:
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?
Share Improve this question edited Oct 7, 2021 at 7:34 CommunityBot 1 asked Apr 28, 2020 at 18:13 Lucas BustamanteLucas Bustamante 2,3481 gold badge28 silver badges43 bronze badges1 Answer
Reset to default 2Answering 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.
2021 update
REST API authentication with Application Passwords was added in WordPress core on 5.6!
https://wordpress/support/wordpress-version/version-5-6/#rest-api-authentication-with-application-passwords