PKCE does not work but is obligatory while using sdk v0.16.x

Zoom Apps Configuration

  • using in-app-auth (both authorize and onAuthorize scopes enabled)
  • additionaly using user:read and to obtain basic user informations
  • vue.js client side

While using sdk v0.16.x, it’s obligatory to provide codeChallenge param to ‘authorize’ func. However, the PKCE flow does not seem to work.
I have fallowed steps described here: Zoom OAuth 2.0 Proof Key for Code Exchange (PKCE) Flow and used code samples from example app to generate code_challange and code_verifier but the result of performing POST to was:

    "reason": "Invalid authorization code [CODE_HERE]",
    "error": "invalid_grant"

I have also tested the flow outside zoom client by performing following requests with same result:

  1. GET /oauth/authorize to obtain the code
  1. POST /oauth/token
curl --location --request POST '' \
--header 'Authorization: Basic AUTH_TOKEN_HERE' \

code_challenge and code_verifier added intentionally so that can be verified
Above should be enough to reproduce the issue.

Additionally i have tested with the same requets but without PKCE and it worked perfertly fine.

Are there any particural app settings that need to be enabled to make the PKCE work?

Also, should PKCE be obligatory already? Looking at Announcements i’m a bit lost about the current status

When you test outside of the zoom client, you need to supply a code_challenge_method parameter or else it will default to plain. If you add a code_challenge_method=S256 to your step 1 request, then your step 2 request should work. Otherwise in your step 2, your code_verifier needs to be the same as the code_challenge in step 1 since the challenge method defaults to plain.

1 Like

@xschen thank you! That helped me a lot!

Looks like while in zoom-client it also uses ‘plain’ by defualt so code_verifier and code_challenge need to be ęqual as well.
Additionally when using s256, code_challenge should not be base64 encoded.

It’s defenetely worth updating this post: Zoom OAuth 2.0 Proof Key for Code Exchange (PKCE) Flow and example app to match defaults. Hopefull someone from zoom will take care of it :slight_smile:

1 Like

Glad that helped! Can you show me how you’ve been doing it with the in-client route? I actually haven’t been able to get it working myself regardless if the challenge method is plain or S256 (the default should be S256 according to an earlier post). No matter which method I choose, I always end up getting the same invalid grant error you referenced earlier. I’ve been wondering if it’s because if the user has already been authorized outside of the zoom client, going through the authorization process in client then just won’t work.

Actually i’m currenlty extreamly confused…

We are using two separate apps - one for testing/playing around and second is our production app that is already published. It is working fine for the testing app but fails for the production one. On windows it opens app authorization page (that should not be the case since the app was already authorized while adding to my apps) and then proceeds to app but the code generated there is invalid and i’m back on the invalid code/grant issue . On osx it’s stuck in authorize app page loop.

The way it works for testing app is by simply calling zoomsdk.authorize with same code_challange as the code_verifier in token request later. We are also making sure that redirect_uri passed to onAuthorize along the code is passed to our API and used when obtaining token. Given both production and testing app shared same configuration this looks more like some Zoom-side issues with auth flow.

Also, till yesterday we were using sdk v0.12.0 in which it was possible to trigget .authorize without PKCE and all was fine but suddenly, it stopped working - the onAuthorized event in not triggered anymore. When i realized we are behind by few versions i have udpated it to v0.16.5 from npm package and now we here.

@xschen small update here. I think i found the reason why our production had this authorization infinite loop on osx/invalid code/grant on widnows issue. We were missing FE url on the OAuth allow list. (we are using backend proxy as home url). Looks like the url that the app is on when zoomsdk.authorize is triggered must be listed as OAuth allow list. Maybe that will help you somehow

Yeah the issue is just odd on my end. My ultimate goal is to try and get the email of the logged in user. I was thinking I can go through the in-client authentication quietly behind the scenes when a user launches the app (shouldn’t prompt the user to authorize again since they’ve already installed the app from outside the client), get an access token, and then hit the /user/me endpoint to grab the user email. It’s also strange that your code_verifier and code_challenge is working when they’re the same since it explicitly states here: Authentication that in-client oauth only supports S256.

Anyways, I just tried setting the code_verifier and code_challenge to the same value and still no luck with the in-client flow. My code is relatively simple:

    state: someRandomState,
    codeChallenge: myCodeChallenge
}).then((ret) => {
}).catch((e) => {

zoomSdk.onAuthorized((event) => {
    var code = event.code
    var redirectUri = window.location.href 
        type: "POST",
        url: backendUrlForExchangingToken,
        data: {code: code, redirectUri: redirectUri}
        //completion code

I’ve double checked the redirectUri there is in my OAuth allow list and the backend method used for retrieving the token is the same one I use without issue for the out-of-client install. I’ve also tried switching the token exchange call to a POST method with the parameters in the body and still no luck.

Check if in your case window.location.href matches event.redirectUri If not, that could be the issue.

Ah I take everything back! There was a small bug in my token exchange code, and now it’s working when code_verifier and code_challenge is the same. Still odd that S256 is not enforced though even though the docs say that it should be. Hopefully there’ll be a notice when this eventually gets fixed in an upcoming release. Appreciate the help!

@xschen ,
Glad to hear you were able to resolve that bug on the token exchange. Can you share an example code_verifier and code_challenge values that are not enforcing S256 ? I am happy to test on my end and report this to engineers.

@donte.zoom Here’s what I’ve tried and what’s worked so far using the latest sdk from the CDN:

In-client OAuth route:

code_challenge = 9JFq4jw9ItYisLBRSpgIONZcS4DrpHbJQ3qNge0dQw4
code_verifier = arandomstring256
result: invalid grant

code_challenge = 9JFq4jw9ItYisLBRSpgIONZcS4DrpHbJQ3qNge0dQw4
code_verifier = 9JFq4jw9ItYisLBRSpgIONZcS4DrpHbJQ3qNge0dQw4
result: token retrieved

Out of client web-browser OAuth route:

code_challenge_method = S256 provided in URL:

code_challenge = 9JFq4jw9ItYisLBRSpgIONZcS4DrpHbJQ3qNge0dQw4
code_verifier = arandomstring256
result: token retrieved

code_challenge = 9JFq4jw9ItYisLBRSpgIONZcS4DrpHbJQ3qNge0dQw4
code_verifier = 9JFq4jw9ItYisLBRSpgIONZcS4DrpHbJQ3qNge0dQw4
result: invalid grant

No code_challenge_method specified in URL:

code_challenge = 9JFq4jw9ItYisLBRSpgIONZcS4DrpHbJQ3qNge0dQw4
code_verifier = arandomstring256
result: invalid grant

code_challenge = 9JFq4jw9ItYisLBRSpgIONZcS4DrpHbJQ3qNge0dQw4
code_verifier = 9JFq4jw9ItYisLBRSpgIONZcS4DrpHbJQ3qNge0dQw4
result: token retrieved

@xschen We have an example of how you can use PKCE with Express on Github that might help here:

Thanks for the example @MaxM. It looks like that particular portion of code is showing off how to generate an out-of-client install link where you can specify the code_challenge_method. That aspect of the authorization process has been working for us as well.

The confusion we were having is from the in-client oauth method where according to the docs here Authentication, specifically:


only S256 should be supported, but the opposite seems to be happening where the token exchange is only working when code_verifier = code_challenge.