【问题标题】:nodejs express + create-react-app oauth2.0 error 401 'Invalid access token' Spotify APInodejs express + create-react-app oauth2.0 error 401 'Invalid access token' Spotify API
【发布时间】:2021-08-12 15:13:48
【问题描述】:

我正在尝试构建一个允许我通过nodejs express 服务器从create-react-app 客户端调用Spotify 的API 的应用程序。我正在尝试使用Authorization Code Flow

它可以使用以下代码获取授权代码以生成 URL,完全在客户端(是否以及如何使用服务器端是另一个问题):

getSpotifyCodeUrl() {
  const authEndPoint = 'https://accounts.spotify.com/authorize'
  const clientId = CLIENT_ID;
  const responseType = 'code';
  const redirectUrl = 'http://localhost:3000/';
  // TODO: state for cross-site request forgery protection
  // cont state = '...';
  const scope = 'user-read-private user-read-email';
  return(
    authEndPoint +
    '?response_type=' + responseType +
    '&client_id=' + clientId +
    (scope ? '&scope=' + encodeURIComponent(scope) : '') +
    '&redirect_uri=' + encodeURIComponent(redirectUrl)
  )
}

用户只需单击上面生成的带有 href 的链接。

{!this.state.token ? <a className="btn btn--loginApp-link" href={this.getSpotifyCodeUrl()}>
  Login to Spotify
</a> : ""}

用户被重定向回来后,我使用以下函数从中提取授权码

componentDidMount() {
  this.setState({code: new URLSearchParams(window.location.search).get('code')});
}

我使用代码检索访问令牌。来自客户的电话:

getSpotifyAccessToken() {
  fetch('/auth?code=' + this.state.code)
    .then((res) => res.json())
    .then(data => {
      this.setState({token: data.token});
      localStorage.setItem('token', this.state.token);
    });
}

服务器上的 API 调用:

app.get("/auth", (req, res) => {
  let code = req.query.code;
  let authOptions = {
    url: 'https://accounts.spotify.com/api/token',
    form: {
      code: code,
      redirect_uri: 'http://localhost:3000/',
      grant_type: 'authorization_code'
    },
    headers: {
      'Authorization': 'Basic ' + (new Buffer.from(clientId + ':' + clientSecret).toString('base64'))
    },
    json: true
  };

  request.post(authOptions, function(error, response, body){
    if (!error && response.statusCode === 200) {
      token = body.access_token;
      res.json({ token: "Token: " + body.access_token});
    } else { 
      console.log("/auth response body")
      console.log(body) 
    } 
  });
});

奇怪的是我得到了一个令牌,但在我的服务器终端中也可以看到以下错误:

{ 错误:'invalid_grant', error_description: '无效的授权码' }

如果我随后尝试使用令牌从客户端发出(简单)请求:\

getSpotifyMe() {
  fetch('/me?token=' + this.state.token)
    .then((res) => res.json())
    .then(data => {
      console.log(data);
    });
}

以及对应的服务器调用:

app.get("/me", (req, res) => {
  let token = req.query.token;
  console.log("Token: " + token);
  let options = {
    url: 'https://api.spotify.com/v1/me',
    headers: { 'Authorization': 'Bearer ' + token },
    json: true
  }

  request.get(options, function(error, response, body) {
    console.log("/me request body");
    console.log(body);
    if (!error && response.statusCode === 200) {
      res.json(body);
    } else {
    } 
  })
})

这给了我一个 401 错误:

{ 错误:{状态:401,消息:'无效的访问令牌'} }

我已经尝试了一些东西。从客户那里打来电话,没有成功。刷新令牌,删除 cookie,从帐户授权,但没有成功。奇怪的是我可以使用在Spotify Web Console 中无效的令牌,执行与我在应用程序中尝试执行的完全相同的调用。

您知道我在哪里导致我的应用程序出现这些错误(invalid_grant 和 401)吗?我该如何解决?

【问题讨论】:

    标签: node.js express oauth-2.0 create-react-app spotify


    【解决方案1】:

    我最终更密切地关注the example on Spotify GitHub

    我将create-react-app 更改为简单地调用/login 服务器路由。不要像我尝试的那样使用 fetch,一旦服务器从不同的来源调用 Spotify API,你最终会出现跨域错误。由于某些不明显的原因,我不能使用href="/login",服务器根本没有响应,但这是另一个 SO 问题的结果。

    <a href="http://localhost:3001/login">Login to Spotify</a>
    

    服务器 index.js 现在只是授权代码流app.js,带有我自己的变量和一个小调整。

    • 我的redirect_urihttp://localhost:3001/callback,我在http://localhost:3000 重定向回我的客户create-react-app 时挣扎了很长时间,而且很累,这可能是身份验证代码和访问令牌IDK 出现问题的地方。您想直接进入 服务器端 回调。对用户来说也更加直观:一键点击并登录,中间没有混乱的重定向。
    • 在服务器上的/callback 路由中,当access_tokenrefresh_token 已成功检索或未成功检索时,这就是您想要重定向回您的客户端的时候,我是http://localhost:3000。当然是代币。该示例使用 URL 参数,我猜设置 cookie 也应该可以,但如果这是安全问题,则必须进行一些研究。

    我对应用程序的 CORS 子句做了一点小调整。除了来自我的客户端之外,服务器不需要传入请求,所以我在那里添加了{origin: 'http://localhost:3000'},以防万一。

     app.use(express.static(__dirname + '../client/build'))
        .use(cors({origin: 'http://localhost:3000'}))
        .use(cookieParser());  
    

    就是这样,就像一个魅力,我可以看到/v1/me 调用的响应正文进入服务器(示例代码中有一个控制台日志)并且令牌正在返回给客户端。

    【讨论】:

      猜你喜欢
      • 2018-07-16
      • 1970-01-01
      • 2018-09-11
      • 1970-01-01
      • 1970-01-01
      • 2018-05-20
      • 2020-01-05
      • 2021-03-28
      • 2013-07-22
      相关资源
      最近更新 更多