jwt-auth icon indicating copy to clipboard operation
jwt-auth copied to clipboard

Requesting a token does not work using Authorization header to pass credentials

Open wavedeck opened this issue 7 months ago • 3 comments

When trying to pass the credentials to /wp-json/jwt-auth/v1/token via "Authorization: Basic ....." header, the endpoint will not authenticate as $request->get_param() does not work with headers.

As per README.md, you are required to submit the credentials as parameters.

To generate token, submit a POST request to this endpoint. With username and password as the parameters.

It doesn't however specify to new users, that Basic Auth doesn't count as parameters, but POST Body or URL GET Params do. I'd suggest updating the README.md to guide the user in the right direction.

IMHO, the Plugin should however also respect the Authorization header, since it's the standard method for authenticating WordPress REST-API and many other third party APIs. This could be achieved by checking $username and $password for null and trying to parse the Authorization header, similar to how i did in my first comment

--- Original Issue ---

The jwt-auth plugin always returns an error while trying to authenticate using the /wp-json/jwt-auth/v1/token endpoint using the current WordPress version (currently 6.4.2)

{
    "success": false,
    "statusCode": 403,
    "code": "invalid_username",
    "message": "Error: Unknown username. Check again or try your email address.",
    "data": []
}

Upon further inspection, I've noticed that the get_token method, responsible for this endpoint uses $request->get_param() for both username and password. These calls to get_param will always return null since they are not responsible for parsing headers.

~As of WordPress 6.4.2, this plugin will not work.~

wavedeck avatar Jan 22 '24 22:01 wavedeck

After a while debugging the issue, i have came up with a solution to get this endpoint to function using Authorization header.

I've added another method to class-auth.php that parses the user and password from the Authorization header and replaced the calls to $request->get_param() with my method (this will break the current behavior of sending the credentials in the POST body, so using this method as a fallback would be a better approach):

public function parse_authorization_header(): array
{
    $headers = getallheaders();
    
    if (isset($headers['Authorization'])) {
        list($type, $data) = explode(" ", $headers['Authorization'], 2);
        if (strcasecmp($type, "Basic") == 0) {
            return explode(':', base64_decode($data));
        }
    }
    
    return array(null, null);
}

and in the get_token method i've replaced

$username = $request->get_param('username');
$password = $request->get_param( 'password' );

with

list($username, $password) = $this->parse_authorization_header();

~and the endpoint /wp-json/jwt-auth/v1/token will work again in WordPress 6.4.2~ This was a wrong assumption, that the plugin works using Basic Auth

wavedeck avatar Jan 22 '24 22:01 wavedeck

@wavedeck Why are you sending the username and password in the headers? The username and password should be spent in the body of the POST request...

dominic-ks avatar Jan 23 '24 09:01 dominic-ks

Thx! Sending the username and password in the body works. In that case, my issue is not an actual bug / incompatibility but rather misinformation and unclear docs. Since the default way, WordPress and many other APIs handle authentication is through "Authorization" header, and its a best practice to do so, i have assumed, that your endpoints work similarly.

The docs describe:

To generate token, submit a POST request to this endpoint. With username and password as the parameters.

Parameters can be a lot of things.. in the case of get_parameter() in WordPress the Body, Query Parameters or Params set by some Middleware in the request.

In that case I'd suggest to update the README.md to further specify sending the values in the POST Body to avoid further confusion. It cost me quite some time to figure out why this happened, since I first assumed something is wrong with my credentials.

Alternatively, it would be helpful to additionally parse the Authorization Header if the user credentials are null to prevent issues like mine from happening further.

wavedeck avatar Jan 23 '24 11:01 wavedeck