Epic Programmer
Epic Programmer

Reputation: 413

Oauth2 and PKCE: Code verifier is invalid

I am making a client to authorize via OAuth2 but I keep on getting an "Code verifier is invalid" error.

async function encode(input: string): Promise<string> {
  return btoa(string).replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '')
}

async function digestMessage(message) {
  const msgUint8 = new TextEncoder().encode(message);                           // encode as (utf-8) Uint8Array
  const hashBuffer = await crypto.subtle.digest('SHA-256', msgUint8);           // hash the message
  const hashArray = Array.from(new Uint8Array(hashBuffer));                     // convert buffer to byte array
  const hashHex = hashArray.map((b) => b.toString(16).padStart(2, '0')).join(''); // convert bytes to hex string
  return hashHex;
}

function generateRandomString (len?: number) {
  var arr = new Uint8Array((len || 40) / 2)
  window.crypto.getRandomValues(arr)
  return Array.from(arr, dec2hex).join('')
}

async function pkceChallengeFromVerifier(v: string) {
  let hash = await digestMessage(v)
  let challenge = await encode(hash)
  return challenge
}

No matter what verifier I put in, it keeps on giving me the error.

Upvotes: 0

Views: 833

Answers (1)

Epic Programmer
Epic Programmer

Reputation: 413

Do not convert it into a string! That changes the base64 output value entirely! Instead, base64 encode the raw hash buffer from crypto.subtle.digest.

Using this solution for getting the base64 of an array buffer:

async function hashSHA256(str: string): Promise<ArrayBuffer> {
  const utf8 = new TextEncoder().encode(str);
  const hashBuffer = await crypto.subtle.digest('SHA-256', utf8);
  return hashBuffer;
}

async function base64_arraybuffer(data: ArrayBuffer): Promise<string> {
  // Use a FileReader to generate a base64 data URI
  const base64url: string = await new Promise((r) => {
      const reader = new FileReader()
      reader.onload = () => r(reader.result as string)
      reader.readAsDataURL(new Blob([data]))
  })

  /*
  The result looks like
  "data:application/octet-stream;base64,<your base64 data>",
  so we split off the beginning:
  */
  return base64url.split(",", 2)[1]
}


async function encode(input: ArrayBuffer): Promise<string> {
  return (await base64_arraybuffer(input)).replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '')
}

async function pkceChallengeFromVerifier(v: string) {
  let hash = await hashSHA256(v)
  let challenge = encode(hash)
  return challenge
}

Also make sure that the input verifier string is within the length range of 43 and 128 characters long.

Upvotes: 0

Related Questions