Spotify PKCE code_verifier不正确

时间:2020-07-26 02:43:38

标签: javascript oauth-2.0 pkce

我很高兴听到我现在可以使用Spotify Web API,而无需通过PKCE进行后端应用程序。不幸的是,我似乎有点误会,无法使它正常工作。

在此过程中,我可能会犯一些小错误,但是我没有做过一次,但是擦了擦石板,再次尝试,但仍然没有运气。由此看来,我一定误会了documentation

我将解释我在做什么,希望这里的人可以指出我所缺少或做错的事情。我假设我有一个基本的概念误解。

我首先使用一个称为crypto-random-string的npm包生成一个加密随机字符串。我先将其存储在浏览器的本地存储中,然后再使用js-sha256对其进行哈希处理,然后再使用另一个名为base64url的npm软件包对其进行编码。

    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.com/authorize');
    endpoint.search = new URLSearchParams(params);

    window.location = endpoint.toString();

从这里,我使用适当的url参数重定向到/ authorize端点。我已经成功地做到了这一点,然后相应地重定向到了我提供的redirect_uri,在这里我从url参数中获取给定的代码。

这时,我尝试使用client_id,grant_type,从url参数获取的代码,我的redirect_uri和本地存储的code_verifier提取到/ api / token端点。

    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.com/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)

在这一点上,在我两次尝试之后,我都收到错误消息:

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

我明显有做错什么吗?该错误使我相信,就实际生成的code_verifier而言,我做错了事,但我对该问题可能不知所措。

2 个答案:

答案 0 :(得分:2)

Spotify论坛上的某人向我指出了this的答案。不知道为什么会这样,但是可以通过以下方式进行编码:

    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)

答案 1 :(得分:0)

以前的答案和评论,除了 OP,已经记录了大部分需要的信息,所以我只会添加对我有帮助的:

验证器本身大多被编码为 Base64-URL。

伪代码(就像我自己用 C# 编写的代码):

verifier = Base64UrlEncode(GetRandomString(length: 50))
challenge = Base64UrlEncode(HashWithSha256(verifier))