Banging my head against the wall over here trying to get the PKCE Code Flow working for access tokens. It’s failing with 401 unauthorized (no details in response) when I try to submit the code and code_verifier to the token URI at https://www.warcraftlogs.com/oauth/token.
Here is my initial request to the authorization URI.
https://www.warcraftlogs.com/oauth/authorize?client_id=<client_id>&code_challenge=NzMwM2FlMTdhZDVmNjI0MzZiN2NlNTBkYTM3MjA5NjNlNzE5NDE3MjVmZGI5NzkzNGVjZDA5NjBmMmM4Yjg4Zg&code_challenge_method=S256&state=ECJhAEcmVvb0mrLXk3hYI9K8MzVo0IgjZgPUNv2DACJk_GPevvMazzRPmCXPCt1B&redirect_uri=http://localhost:59107&response_type=code
I authorize my application and am redirected to my listening app which serves up a page with some javascript to complete the rest of the flow. I succesfully pull the returned code from the URL fragment. Here is the POST request I make to the token URI.
POST /oauth/token HTTP/1.1
Host: www.warcraftlogs.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:82.0) Gecko/20100101 Firefox/82.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Referer: http://localhost:59107/?code=<big_long_code_here_from_url_fragment>&state=ECJhAEcmVvb0mrLXk3hYI9K8MzVo0IgjZgPUNv2DACJk_GPevvMazzRPmCXPCt1B
Content-type: application/x-www-form-urlencoded
Content-Length: 1255
Origin: http://localhost:59107
DNT: 1
Connection: keep-alive
Note that I’m setting the content-type header. But I’ve tried it without that set, or set to text/plain and that doesn’t work either.
Here is the data I send with the POST.
client_id=<client_id>&code_verifier=RUNKaEFFY21WdmIwbXJMWGszaFlJOUs4TXpWbzBJZ2paZ1BVTnYyREFDSmtfR1BldnZNYXp6UlBtQ1hQQ3QxQn5TNTI0TC5-Z0hKTHRmcX5ZS3V0Ri5MTF9ZTjNCZTFVT0xxbA&redirect_uri=http%3A%2F%2Flocalhost%3A59107&grant_type=authorization_code&code=<big_long_code_here_from_url_fragment>
Here is my actual javascript xmlhttprequest I perform.
var re_code = /\?code=(\w+)/;
var code = re_code.exec(location)
var requestToken = new XMLHttpRequest();
var urlToken = "https://www.warcraftlogs.com/oauth/token";
var paramsToken = "client_id=" + client_id + "&" +
"code_verifier=" + code_verifier + "&" +
"redirect_uri=" + encodeURIComponent("http://localhost:"+PORT) + "&" +
"grant_type=authorization_code&" +
"code=" + code[1];
requestToken.open("POST", urlToken, true);
requestToken.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
requestToken.send(paramsToken);
The documentation is unclear on how exactly to generate the code_verifier and code_challenge (the PHP and Javascript examples contradict each other), but a close reading of the RFC specification shows it should be done like so.
code_verifier = generateRandomStringOfLength(128)
code_challenge = sha256Hash(code_verifier)
Then, when sending each value in POST requests, base64 encode each.
code_verifier = base64Encode(code_verifier)
code_challenge = base64Encode(code_challenge)
I have tried many different orders of operation on when to use the base64 encoding (as I said, the PHP and Javascript examples differ from the RFC specifications) but nothing seems to work. Here is an example of my generated values I’m using, removing trailing ‘==’ and converting all ‘+’ to ‘-’ and ‘/’ to ‘_’ as the documents specify.
code_verifier: ECJhAEcmVvb0mrLXk3hYI9K8MzVo0IgjZgPUNv2DACJk_GPevvMazzRPmCXPCt1B~S524L.~gHJLtfq~YKutF.LL_YN3Be1UOLql
code_challenge: 7303ae17ad5f62436b7ce50da3720963e71941725fdb97934ecd0960f2c8b88f
and in base64
code_verifier: RUNKaEFFY21WdmIwbXJMWGszaFlJOUs4TXpWbzBJZ2paZ1BVTnYyREFDSmtfR1BldnZNYXp6UlBtQ1hQQ3QxQn5TNTI0TC5-Z0hKTHRmcX5ZS3V0Ri5MTF9ZTjNCZTFVT0xxbA
code_challenge: NzMwM2FlMTdhZDVmNjI0MzZiN2NlNTBkYTM3MjA5NjNlNzE5NDE3MjVmZGI5NzkzNGVjZDA5NjBmMmM4Yjg4Zg
Where are things going wrong that I’m always getting 401 unauthorized?