Description
Our Zoom App’s in-client OAuth (zoomSdk.authorize() with PKCE) consistently fails the token exchange with invalid_grant, on every single attempt, with no variation. We’ve spent several hours ruling out every client/config-side cause we could find (see below) and are stuck on what’s left to check that would only be visible from Zoom’s backend.
Note: I searched the forum first and found the existing thread on the SDK v0.16.x “plain vs S256” PKCE default — our implementation already matches that fix (code_challenge === code_verifier), so that’s not the cause here.
How To Reproduce
Authentication method / app type: Zoom App, in-client OAuth (zoomSdk.authorize + onAuthorized), PKCE “plain” method, SDK v0.16.0. App is User-managed, Development status (not yet through Beta Test or Publish).
-
Steps:
- From inside a running meeting, call zoomSdk.authorize({codeChallenge, state})
- onAuthorized fires successfully with a code and state
- Server POSTs to
zoom.us/oauth/tokenwith:
Authorization: Basic base64(client_id:client_secret)
grant_type=authorization_code
code=<from onAuthorized>
redirect_uri=https://example.com/zoom/oauth/callback
code_verifier=<same value as codeChallenge>
-
Error: Every attempt (dozens, over several hours) returns HTTP 400
{"error":"invalid_grant","reason":"Invalid Grant"}, within 50-200ms of the code being issued — first attempt, never a retry.
Already ruled out:
- redirect_uri byte-exact match (confirmed via dashboard, exported app manifest, and direct request instrumentation) — no trailing slash, no encoding mismatch
- client_id confirmed identical between server config and Marketplace App Credentials
- client_secret regenerated and re-synced to rule out drift
- No custom OAuth/API base URL overrides in our code — the real zoom dot us / api dot zoom dot us hosts are used [it wouldn’t let me post with links]
- Required scopes present and approved: zoomapp:inmeeting, user:read:user, meeting:read:meeting
- All Zoom App SDK APIs enabled including authorize, onAuthorized, getUserContext
- code and code_verifier confirmed byte-exact (correct lengths, clean charset) via direct instrumentation
- Code reuse ruled out directly — three separate authorize() calls produced three distinct codes (compared via hash), each exchanged exactly once
- Tried the account’s “Shared Access Permissions” toggle both checked and unchecked, combined with both a lighter re-authorization and a full app remove+reinstall — all four combinations produce the identical error
Zoom Technical Support (chat + a human agent) said this is “configuration-related, not a product defect” but couldn’t identify what, and closed the case.
Has anyone seen invalid_grant persist through all of the above for an in-client Zoom Apps OAuth flow specifically? Is there a known constraint for apps still in Development status, or anything else Zoom-side that wouldn’t be visible from the app/client side?