Creating a code verifier and challenge for PKCE auth on Spotify API in ReactJS

Viewed 8287

I'm trying to add Spotify auth to my single page react application following the doc from their api.

So far this is how I generate the codes based on solutions I found online:

const generateVerifier = () => {
    return crypto.randomBytes(64).toString('hex');
}

const getChallenge = verifier => {
    return crypto.createHash('sha256')
        .update(verifier)
        .digest('base64')
        .replace(/\+/g, '-')
        .replace(/\//g, '_')
        .replace(/=/g, '')
}

An example of a pair of codes I created using that technique:

  • verifier: e8c3745e93a9c25ce5c2653ee36f5b4fa010b4f4df8dfbad7055f4d88551dd960fb5b7602cdfa61088951eac36429862946e86d20b15250a8f0159f1ad001605
  • challenge: CxF5ZvoXa6Cz6IcX3VyRHxMPRXYbv4PADxko3dwPF-I

An example of an old pair of codes I created:

  • verifier: 1jp6ku6-16xxjfi-1uteidc-9gjfso-1mcc0wn-tju0lh-tr2d8k-1auq4zk
  • challenge: SRvuz5GW2HhXzHs6b3O_wzJq4sWN0W2ma96QBx_Z77s

I then get a response from the API saying "code_verifier was incorrect." What am I doing wrong here?

2 Answers

Try following this guide for generating code for generating code challenge and verifier

Here are the important parts:

Generate Code Verifier

// GENERATING CODE VERIFIER
function dec2hex(dec) {
  return ("0" + dec.toString(16)).substr(-2);
}

function generateCodeVerifier() {
  var array = new Uint32Array(56 / 2);
  window.crypto.getRandomValues(array);
  return Array.from(array, dec2hex).join("");
}

Generate code challenge from code verifier

function sha256(plain) {
  // returns promise ArrayBuffer
  const encoder = new TextEncoder();
  const data = encoder.encode(plain);
  return window.crypto.subtle.digest("SHA-256", data);
}

function base64urlencode(a) {
  var str = "";
  var bytes = new Uint8Array(a);
  var len = bytes.byteLength;
  for (var i = 0; i < len; i++) {
    str += String.fromCharCode(bytes[i]);
  }
  return btoa(str)
    .replace(/\+/g, "-")
    .replace(/\//g, "_")
    .replace(/=+$/, "");
}

async function generateCodeChallengeFromVerifier(v) {
  var hashed = await sha256(v);
  var base64encoded = base64urlencode(hashed);
  return base64encoded;
}

Here's a working example

You can also check the validity of the codes here

I took this snippet from the passport oauth2 library to generate code verifier and code challenge.

const code_verifier = base64url(crypto.pseudoRandomBytes(32));

const code_challenge = crypto
     .createHash("sha256")
     .update(code_verifier)
     .digest();
Related