WooCommerce returns an HTTP 401 error if you are not using HTTPS or the Authorization header is not parsed correctly by your webserver.
Receiving HTTP 401 during authentication
WooCommerce provides an extensive REST API with which you can automate your WordPress WooCommerce shop. WooCommerce uses a Consumer Key and Consumer Secret as credentials. After you have generated both, you might receive the error
HTTP/1.1 401 Unauthorized
{
"code": "woocommerce_rest_cannot_view",
"message": "Sorry, you cannot list resources.",
"data": {
"status": 401
}
}
when calling the REST endpoint with curl
or wget
. Your previous CLI command might look like the following code:
curl -i -u ${CONSUMER_KEY}:${CONSUMER_SECRET} "http://127.0.0.1/wordpress/wp-json/wc/v3/products"
# or
curl -i "http://127.0.0.1/wordpress/wp-json/wc/v3/products?consumer_key=${CONSUMER_KEY}&consumer_secret=${CONSUMER_SECRET}"
Solutions for fixing the HTTP 401 error
Check Consumer Key and Consumer Secret
The most obvious fix is to check that the Consumer Key and Consumer Secret are correctly pasted.
- Each of these must have a length of 38 bytes (or ASCII characters).
- The Consumer Key starts with the prefix ck_
- The Consumer Secret starts with the prefix cs_
If your Consumer Secret contains a null-byte, newline or carriage return byte at the end of the string, you will receive the error Consumer secret is invalid instead:
HTTP/1.1 401 Unauthorized
{
"code": "woocommerce_rest_authentication_error",
"message": "Consumer secret is invalid.",
"data": {
"status": 401
}
}
HTTPS is an requirement for non-OAuth signed requests
WooCommerce’s authentication procedure is defined in woocommerce/includes/wc-rest-authentication.php
. The method authenticate($user_id)
requires that you use SSL/TLS, if you are not using OAuth 1.0a:
if ( is_ssl() ) {
$user_id = $this->perform_basic_authentication();
}
OAuth 1.0a requires a signing of your HTTP request, which you are probably not doing when using curl.
Using consumer_key and consumer_secret as GET parameters
When calling the API with
curl -i "http://127.0.0.1/wordpress/wp-json/wc/v3/products?consumer_key=${CONSUMER_KEY}&consumer_secret=${CONSUMER_SECRET}"
we have to let WordPress know that HTTPS is enabled, even if it is not.
There are mulitple solutions, all involve setting the PHP environment variable $_SERVER['HTTPS']
to on. Geting an X.509 certificate for your local environment and accessing your WooCommerce instance through HTTPS might be tricky.
But you can set the environment variable in the .htaccess file when using Apache with
SetEnv HTTPS on
or you could change WordPress index.php to set the variable:
/**
* Tells WordPress to load the WordPress theme and output it.
*
* @var bool
*/
define( 'WP_USE_THEMES', true );
$_SERVER['HTTPS'] = 'on';
/** Loads the WordPress Environment and Template */
require __DIR__ . '/wp-blog-header.php';
Both solutions do only work for your command line/API usage. As soon as you are accessing the frontend through your web browser, WordPress will try to redirect you to HTTPS.
Inside your .htaccess
file you could go with setting up RewriteRule
s or SetEnvIf
conditionals to check the requested route and the current user agent. But a better way is to check the Authorization header.
Using the Authorization header
From a developer’s point of view, this should be the way to go anyway. The HTTP Authorization header is designed for exactly this: Providing authentication credentials from the browser to the webserver.
When using
curl -i -u ${CONSUMER_KEY}:${CONSUMER_SECRET} "http://127.0.0.1/wordpress/wp-json/wc/v3/products"
curl
passes the credentials as an HTTP Authorization request header to the webserver. This is the base64-encoded sample for my local test environment:
Authorization: Basic Y2tfYjY2Nzc1NzZiMmI2NzIxODJiMTVhNGYwM2Y4NmNlYjliOGMxYzgxNzpjc19mNzA3ZDA4ZDEwMzM4NDRkODEwNTZlMDYxY2QxOTgzODY3NTQ2MjNk
We are now looking if the Authorization header is present in the client’s HTTP request. If this is the case, we will set the HTTPS environment variable. This can be done in your .htaccess
file with
SetEnvIf Authorization (.+) HTTPS=on
This does only work if your PHP environment supports the getallheaders()
function. If you are using PHP-FPM before 7.2.0, you have to to use the HTTP_AUTHORIZATION header like
# Set HTTP_AUTHORIZATION header if Authorization is passed by the client
SetEnvIf Authorization (.+) HTTP_AUTHORIZATION=$1
For nginx you would have to use
fastcgi_pass_request_headers on;
fastcgi_pass_header Authorization;
This passes the Authorization header to PHP-FPM. In the end, it will populate the environment variables PHP_AUTH_USER and PHP_AUTH_PW. Both environment variables are also used as fallback in WooCommerce’s REST authentication workflow.
See also the method WC_REST_Authentication::get_authorization_header()
.
Accessing your WordPress instance through your web browser still works because for the normal login endpoints at /wp-login.php
and /wp-admin
Form Authentication is used.