最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Spotify PKCE code_verifier was incorrect - Stack Overflow

programmeradmin2浏览0评论

I was excited to hear that I can now use the Spotify web API without having a backend application via PKCE. Unfortunately, I seem to have some sort of misunderstanding and have been unable to get it to work.

I am likely making some minor mistake along the way, but I did it once to no avail and I wiped the slate clean and tried again but still without luck. From this I gather that I must be misunderstanding the documentation.

I will explain what I am doing and hopefully someone here can point out what I'm missing or doing wrong. I'm assuming I have a fundamental conceptual misunderstanding.

I first generate a cryptographically random string using an npm package called crypto-random-string. I store that in the browser's local storage before using js-sha256 to hash it and then using another npm package called base64url to encode it.

    let verifier = cryptoRandomString({length: 50})
    window.localStorage.setItem('verifier', verifier)

    let params = {
      client_id: '[MY CLIENT ID]',
      response_type: 'code',
      redirect_uri: 'http://localhost:3000/callback',
      code_challenge_method: 'S256',
      code_challenge: base64url(sha256(verifier))
    }

    let endpoint = new URL('');
    endpoint.search = new URLSearchParams(params);

    window.location = endpoint.toString();

From here, I redirect to the /authorize endpoint with the proper url parameters. I have gotten this far successfully and then been redirected accordingly to my provided redirect_uri, where I grab the given code from the url parameters.

At this point, I try the fetch to the /api/token endpoint with the client_id, grant_type, the code I got from the url params, my redirect_uri, and the locally stored code_verifier.

    let params = new URLSearchParams(window.location.search);
    console.log(params.get('code'));

    let newParams = {
      client_id: '[MY CLIENT ID]',
      grant_type: 'authorization_code',
      code: params.get('code'),
      redirect_uri: 'http://localhost:3000/callback',
      code_verifier: window.localStorage.getItem('verifier')
    }

    let endpoint = new URL('');

    endpoint.search = new URLSearchParams(newParams);

    fetch(endpoint.toString(), {
      method: 'POST',
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded'
      }
    }).then(data => data.json()).then(console.log)

At this point, after both of my attempts I received the error:

{ error: "invalid_grant", error_description: "code_verifier was incorrect" }

Is there anything that I am obviously doing wrong? The error leads me to believe I'm doing something wrong as far as the actual generation of the code_verifier, but I am at a loss to what that issue may be.

I was excited to hear that I can now use the Spotify web API without having a backend application via PKCE. Unfortunately, I seem to have some sort of misunderstanding and have been unable to get it to work.

I am likely making some minor mistake along the way, but I did it once to no avail and I wiped the slate clean and tried again but still without luck. From this I gather that I must be misunderstanding the documentation.

I will explain what I am doing and hopefully someone here can point out what I'm missing or doing wrong. I'm assuming I have a fundamental conceptual misunderstanding.

I first generate a cryptographically random string using an npm package called crypto-random-string. I store that in the browser's local storage before using js-sha256 to hash it and then using another npm package called base64url to encode it.

    let verifier = cryptoRandomString({length: 50})
    window.localStorage.setItem('verifier', verifier)

    let params = {
      client_id: '[MY CLIENT ID]',
      response_type: 'code',
      redirect_uri: 'http://localhost:3000/callback',
      code_challenge_method: 'S256',
      code_challenge: base64url(sha256(verifier))
    }

    let endpoint = new URL('https://accounts.spotify./authorize');
    endpoint.search = new URLSearchParams(params);

    window.location = endpoint.toString();

From here, I redirect to the /authorize endpoint with the proper url parameters. I have gotten this far successfully and then been redirected accordingly to my provided redirect_uri, where I grab the given code from the url parameters.

At this point, I try the fetch to the /api/token endpoint with the client_id, grant_type, the code I got from the url params, my redirect_uri, and the locally stored code_verifier.

    let params = new URLSearchParams(window.location.search);
    console.log(params.get('code'));

    let newParams = {
      client_id: '[MY CLIENT ID]',
      grant_type: 'authorization_code',
      code: params.get('code'),
      redirect_uri: 'http://localhost:3000/callback',
      code_verifier: window.localStorage.getItem('verifier')
    }

    let endpoint = new URL('https://accounts.spotify./api/token');

    endpoint.search = new URLSearchParams(newParams);

    fetch(endpoint.toString(), {
      method: 'POST',
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded'
      }
    }).then(data => data.json()).then(console.log)

At this point, after both of my attempts I received the error:

{ error: "invalid_grant", error_description: "code_verifier was incorrect" }

Is there anything that I am obviously doing wrong? The error leads me to believe I'm doing something wrong as far as the actual generation of the code_verifier, but I am at a loss to what that issue may be.

Share Improve this question asked Jul 26, 2020 at 2:43 Ben DurhamBen Durham 921 silver badge10 bronze badges 5
  • Did you manage to solve this? Running into the exact same problem here. I'm pretty sure it's not the encoding but I can't tell what Spotify is doing on their end. – Christiaan Maks Commented Jul 27, 2020 at 21:50
  • Unfortunately no. I took some time off from this project because I felt like I exhausted my options. I'm going to try to look at it again with a set of fresh eyes in the near future and I'll be sure to update this thread if I make any progress. – Ben Durham Commented Jul 28, 2020 at 0:58
  • Okay. I'll let you know if I find anything. No clue what is going wrong though, I guess the documentation is too new and maybe slightly inaccurate somewhere. – Christiaan Maks Commented Jul 28, 2020 at 7:49
  • 1 I also asked on the Spotify developer forums here – Christiaan Maks Commented Jul 28, 2020 at 8:17
  • Great. Thank you. Part of the reason I believe it is a problem on my end is because here in the first reply, a person claimed to have done a "proof of concept" with this flow. – Ben Durham Commented Jul 28, 2020 at 23:14
Add a ment  | 

2 Answers 2

Reset to default 7

Someone on the Spotify forum pointed me to this answer. Not sure why exactly, but doing the encoding the following way does work:

    async function sha256(plain) {
      const encoder = new TextEncoder()
      const data = encoder.encode(plain)
    
      return window.crypto.subtle.digest('SHA-256', data)
    }
    
    function base64urlencode(a){
      return btoa(String.fromCharCode.apply(null, new Uint8Array(a))
        .replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '')
    }
    
    const hashed = await sha256(verifyCode)
    const codeChallenge = base64urlencode(hashed)

Previous answers and ments, in addition to OP, has documented most information needed so I will only add what helped me:

The verifier itself most be encoded as a Base64-URL.

Pseduo-code (as I myself code in C#):

verifier = Base64UrlEncode(GetRandomString(length: 50))
challenge = Base64UrlEncode(HashWithSha256(verifier))
发布评论

评论列表(0)

  1. 暂无评论