Trying to implement the Authorization Code flow with PKCE for a standalone SPA that integrates with CXone. The goal is to avoid storing client secrets in the frontend code. I’m using the standard PKCE challenge/verifier pattern.
Here is the flow:
- Generate code_verifier and code_challenge (SHA256, base64url encoded).
- Redirect user to
https://api.mynicecx.com/oauth2/v1/authorize?response_type=code&client_id=...&redirect_uri=...&code_challenge=...&code_challenge_method=S256 - User logs in, gets redirected back with
?code=AUTH_CODE - Exchange code for token via POST to
/oauth2/v1/token
Step 4 is failing. I get a 400 Bad Request. The response JSON is:
{
"status": 400,
"code": "invalid_grant",
"message": "Invalid authorization code"
}
I’ve checked the following:
client_idmatches the registered application exactly.redirect_urimatches the one in the CXone console settings (including the trailing slash).- The
codeparameter is passed correctly from the URL query string. - The
code_verifiersent in the POST body is the exact same string used to generate the challenge. - The request is made immediately after receiving the code. No significant time delay.
The POST body looks like this (using application/x-www-form-urlencoded):
grant_type=authorization_code&code=THE_CODE&redirect_uri=MY_URI&client_id=MY_CLIENT_ID&code_verifier=MY_VERIFIER
I’ve tried removing client_id from the body, thinking it might be handled differently, but the error persists. Is there a specific requirement for the token endpoint request format in CXone that differs from the standard OAuth2 spec? Or could the issue be with how the code_challenge is being calculated? The hash function seems correct, but I’m double-checking the base64url encoding (no padding).
Any ideas on what causes an invalid_grant if the code itself is fresh and valid?