Getting a 400 Bad Request on the token exchange step. The error message is generic: invalid_grant.
Trying to implement the Authorization Code flow with PKCE for a single-page app that needs to authenticate users against our CXone org. The goal is to avoid storing client secrets in the frontend bundle.
The initial auth request to https://api.mynice.com/oauth2/v2/authorize works. I get redirected back with the code and state. The code looks valid. State matches.
Here is the payload I am sending to POST https://api.mynice.com/oauth2/v2/token:
{
"grant_type": "authorization_code",
"code": "AUTH_CODE_HERE",
"redirect_uri": "https://myapp.local/callback",
"client_id": "MY_CLIENT_ID",
"code_verifier": "VERIFIER_STRING"
}
The code_verifier was generated using the standard base64url encoding of a random byte array. I used the same crypto library to generate the code_challenge for the initial authorize request.
Checked the encoding manually. The challenge sent in the first request matches the verifier sent here when hashed with SHA-256.
Tried swapping the crypto library. Tried generating the verifier with a simpler random string. Same 400 error.
Is there a specific requirement for the code_verifier length or character set in CXone that isn’t in the standard OAuth2 spec? The docs just say “standard PKCE”.
Here is the generating the challenge for the first step:
async function generatePKCE() {
const verifier = generateRandomString(64);
const encoder = new TextEncoder();
const data = encoder.encode(verifier);
const digest = await crypto.subtle.digest('SHA-256', data);
const challenge = btoa(String.fromCharCode(...new Uint8Array(digest)))
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=/g, '');
return { verifier, challenge };
}
The verifier string is 43 characters long. The challenge is 43 characters.
Running into a wall here.