PKCE code_verifier mismatch on /oauth2/token with Node.js SPA

Running into a wall with the Authorization Code + PKCE flow for our new Node.js SPA. The redirect back from Genesys Cloud works fine, getting the code and state in the URL params. But when I hit /oauth2/token to exchange that code, I get a 400 Bad Request with error: "invalid_grant" and error_description: "Code verifier mismatch".

I’m generating the code_verifier and code_challenge client-side using crypto before the auth request, then storing the verifier in sessionStorage. On the callback, I pull it out and send it in the POST body. Here’s the fetch call:

const response = await fetch('https://api.mypurecloud.com/oauth2/token', {
 method: 'POST',
 headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
 body: new URLSearchParams({
 grant_type: 'authorization_code',
 code: urlParams.get('code'),
 redirect_uri: window.location.origin,
 client_id: CLIENT_ID,
 code_verifier: sessionStorage.getItem('code_verifier')
 })
});

I’ve double-checked the challenge method is S256. The verifier looks identical in the logs before sending and in storage. Is there a timing issue where the verifier gets wiped or encoded weirdly? Or does the Genesys token endpoint expect the verifier to be sent in a different way than standard OAuth2 specs?

const challenge = crypto.createHash(‘sha256’).update(verifier).digest(‘base64’).replace(/=/g, ‘’).replace(/+/g, ‘-’).replace(///g, ‘_’);


Make sure that base64 string matches what you sent in the initial authorize request. The standard base64 isn't enough. You gotta strip the padding and swap the characters. That's usually the culprit. Check your encoding logic.