Authorization Code Flow with PKCE: 400 Invalid Grant on Token Exchange

Is there a clean way to exchange the authorization code for an access token in a mobile SPA context when receiving a 400 Bad Request?

Background

Implementing Authorization Code flow with PKCE for an iOS app wrapping the Genesys Cloud Web Messaging SDK. Using code_challenge_method=S256.

Issue

The initial redirect to /oauth/authorize works. However, the subsequent POST to /oauth/token fails.

Error: POST /oauth/token returned 400 Bad Request
Response: {“error”:“invalid_grant”,“error_description”:“Authorization code has expired or been used”}

Troubleshooting

  • Verified code_verifier matches the hashed code_challenge.
  • Confirmed redirect_uri matches exactly.
  • Code is exchanged immediately after redirect.
  • Tried adding grant_type=authorization_code explicitly.

This has the hallmarks of a classic PKCE mismatch. You are likely hashing the code verifier incorrectly before sending it.

  1. Ensure your code_challenge is the Base64Url-encoded SHA256 hash of the original code_verifier.
  2. Verify the token endpoint is exactly https://login.mypurecloud.com/oauth/token.
curl -X POST https://login.mypurecloud.com/oauth/token \
 -H "Content-Type: application/x-www-form-urlencoded" \
 -d "grant_type=authorization_code&code=${AUTH_CODE}&redirect_uri=${REDIRECT_URI}&client_id=${CLIENT_ID}&code_verifier=${CODE_VERIFIER}"