I ran into a problem where my AJAX request fails with error code 401 - Unauthorized, while trying to get an OAuth2 (Okta) Token.
The preview tab shows an error as follows:
Browser requests to the token endpoint must use Proof Key for Code Exchange
Does this mean I can't use the client_credentials grant type for client side requests?
Here is my AJAX request:
$.ajax({
url: dataParsed.TokenServiceEndpoint,
type: "POST",
contentType: "application/x-www-form-urlencoded",
datatype: "application/json",
headers: {
"Authorization": "Basic " btoa(dataParsed.TokenServiceUser ":" dataParsed.TokenServicePassword)
},
data: {
"grant_type": "client_credentials"
}
}
A similar request works using postman, but I am not sure what the difference is between the two, or how to make it work through ajax.
Here is the request exported from Postman:
Content-Type: application/x-www-form-urlencoded
Authorization: Basic *************
User-Agent: PostmanRuntime/7.29.0
Accept: */*
Postman-Token: fd2c4617-a4bb-4d28-88bd-1aa84c2a7404
Host: *****
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Content-Length: 29
Cookie: JSESSIONID=3B62F82EE85BC8574756101635CC3B14
grant_type=client_credentials
HTTP/1.1 200 OK
Date: Fri, 18 Mar 2022 14:41:42 GMT
Server: nginx
Content-Type: application/json
x-okta-request-id: YjSaJqln-TB3pOn9kgwmsQAABSw
x-xss-protection: 0
p3p: CP="HONK"
x-rate-limit-limit: 20000
x-rate-limit-remaining: 19971
x-rate-limit-reset: 1647614537
cache-control: no-cache, no-store
pragma: no-cache
expires: 0
expect-ct: report-uri="*****", max-age=0
x-content-type-options: nosniff
Strict-Transport-Security: max-age=315360000; includeSubDomains
X-Robots-Tag: noindex,nofollow
set-cookie: sid=""; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Path=/
set-cookie: autolaunch_triggered=""; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Path=/
set-cookie: JSESSIONID=AA2D48FFC46A8AC0E1D139D038CE98AB; Path=/; Secure; HttpOnly
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked
CodePudding user response:
Trace your request with Fiddler, also client side client credentials is not supported by Okta from browser, has to be at server level. Check this - https://support.okta.com/help/s/article/Browser-requests-to-the-token-endpoint-must-use-Proof-Key-for-Code-Exchange?language=en_US
The reason I said to trace with Fiddler is so that you can confirm if origin header is being sent or not when using postman vs from ajax and therefore, confirm that you are running into the issue mentioned in the link I pasted.
CodePudding user response:
Client credentials grant is meant for server-to-server communication. It allows your client to authenticate at the Authorization Server to get an access token. This means that you need a confidential client to successfully use this flow - a client which holds a secret and can authenticate using that secret. A browser client (e.g. an SPA), can't hold secrets - anyone is able to read the secret from your code. That's why Okta doesn't let you use client credentials directly from the browser. The error response tells you that browser clients must use PKCE, and as PKCE is only possible in an authorization code flow, this implicitly means that Okta allows only authorization code flow from a browser client.
Postman is not a browser, that's why the request from postman works. It will also work if you run it from curl.
All in all, you shouldn't have to use client credentials from your browser. If you need it just for development purposes or figuring things out, you can run the flow from postman and paste the token into your frontend code. In a production code, you should use authorization code flow. You should remember, though, that this flow requires user interaction.