Problem
Trying to wire up the Authorization Code flow with PKCE for a new single-page app. The frontend handles the initial redirect, but I’m routing the token exchange through a Python backend service first. The code challenge generation looks right. The POST to the token endpoint keeps bouncing back though.
Code
import requests
import secrets
import base64
import hashlib
def generate_pkce():
verifier = secrets.token_urlsafe(64)
challenge_bytes = hashlib.sha256(verifier.encode('ascii')).digest()
challenge = base64.urlsafe_b64encode(challenge_bytes).decode('utf-8').replace('=', '')
return verifier, challenge
verifier, challenge = generate_pkce()
auth_url = f"https://api.mypurecloud.com//authorize?response_type=code&client_id={CLIENT_ID}&redirect_uri={REDIRECT}&code_challenge={challenge}&code_challenge_method=S256"
# ... frontend redirects with code ...
token_resp = requests.post("https://api.mypurecloud.com//token", data={
"grant_type": "authorization_code",
"code": "RECEIVED_CODE",
"redirect_uri": REDIRECT,
"client_id": CLIENT_ID,
"code_verifier": verifier
})
print(token_resp.status_code, token_resp.json())
Error
Getting a 400 Bad Request every single time. The payload returns {"error":"invalid_grant","error_description":"The authorization code has expired or is invalid"}. Timestamps check out. The auth code is definitely fresh. Swapped code_challenge_method=S256 to plain just to rule out hashing issues. It’s still throwing the same 400. The Python SDK handles this automatically, but the raw requests call keeps failing. Don’t see any extra headers being sent.
Question
Is there a specific header requirement for the token exchange that the requests lib is missing? Verifier encoding might be off or a scope parameter is missing. The docs are pretty thin on the exact request body format for hybrid setups. Not seeing anything in the changelog about this either.