skip to Main Content

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

2

Answers


  1. 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.

    Login or Signup to reply.
  2. 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.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search