带有 React 的 Spotify 隐式授权流程 - 用户登录

Posted

技术标签:

【中文标题】带有 React 的 Spotify 隐式授权流程 - 用户登录【英文标题】:Spotify Implicit Grant Flow with React - user login 【发布时间】:2020-03-16 18:09:19 【问题描述】:

下面的这个 React 组件使用 Implicit Grant Flow 将应用程序连接到 Spotify,在为用户获取令牌后将应用程序重定向回我的客户端。

import React,  Component  from 'react';
import Credentials from './spotify-auth.js'
import './Spotify.css'


class SpotifyAuth extends Component   
  constructor (props) 
    super(props);
    this.state = 
      isAuthenticatedWithSpotify: false,
      menu: this.props.userId.menu
    ;
    this.state.handleRedirect = this.handleRedirect.bind(this);
  ;


  generateRandomString(length) 
    let text = '';
    const possible =
      'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
    for (let i = 0; i < length; i++) 
      text += possible.charAt(Math.floor(Math.random() * possible.length));
    
    return text;
     

  getHashParams() 
    const hashParams = ;
    const r = /([^&;=]+)=?([^&;]*)/g;
    const q = window.location.hash.substring(1);
    let e = r.exec(q);
    while (e) 
      hashParams[e[1]] = decodeURIComponent(e[2]);
      e = r.exec(q);
    
    return hashParams;
  

  componentDidMount() 
    //if (this.props.isAuthenticated) 
    const params = this.getHashParams();

    const access_token = params.access_token;
    const state = params.state;
    const storedState = localStorage.getItem(Credentials.stateKey);
    localStorage.setItem('spotifyAuthToken', access_token);
    localStorage.getItem('spotifyAuthToken');

    if (window.localStorage.getItem('authToken')) 
      this.setState( isAuthenticatedWithSpotify: true );
    ;
    if (access_token && (state == null || state !== storedState)) 
      alert('Click "ok" to finish authentication with Spotify');
     else 
      localStorage.removeItem(Credentials.stateKey);
    
    // DO STUFF WITH ACCEES TOKEN HERE
    this.props.onConnectWithSpotify(access_token); 
  ;

  handleRedirect(event) 
    event.preventDefault()
    this.props.createMessage('You linked your Spotify account!', 'success');

    const params = this.getHashParams();
    const access_token = params.access_token;
    console.log(access_token);

    const state = this.generateRandomString(16);
    localStorage.setItem(Credentials.stateKey, state);

    let url = 'https://accounts.spotify.com/authorize';
    url += '?response_type=token';
    url += '&client_id=' + encodeURIComponent(Credentials.client_id);
    url += '&scope=' + encodeURIComponent(Credentials.scope);
    url += '&redirect_uri=' + encodeURIComponent(Credentials.redirect_uri);
    url += '&state=' + encodeURIComponent(state);
    window.location = url; 
  ;

  render() 
      return (
        <div className="button_container">
            <h1 className="title is-4"><font color="#C86428">Welcome</font></h1>
            <div className="Line" /><br/>
              <button className="sp_button" onClick=(event) => this.handleRedirect(event)>
                <strong>LINK YOUR SPOTIFY ACCOUNT</strong>
              </button>
        </div>
      )
  

export default SpotifyAuth;

但是,在重定向之前,我想描述以下页面或弹出窗口,其中包含已定义的范围和“同意”按钮:



根据Implicit Flow 的 Spotify 文档,将用户重定向到 https://accounts.spotify.com/authorize?client_id=5fe01282e94241328a84e7c5cc169164&amp;redirect_uri=http:%2F%2Fexample.com%2Fcallback&amp;scope=user-read-private%20user-read-email&amp;response_type=token&amp;state=123

...执行几个动作:

要求用户授权范围内的访问。 Spotify Accounts > 服务提供了正在寻求访问权限的范围的详细信息。 如果用户未登录,系统会提示他们使用他们的 Spotify >用户名和密码登录。 当用户登录时,他们被要求授权访问>在范围中定义的数据集。

当我调用https://accounts.spotify.com/authorize? 时,上面的代码不会发生上述任何操作,我只是得到了令牌。

怎么了?


我找到了这个 codepen 示例,其中提示用户登录页面:

Spotify Implicit Grant Auth Popup

如何将此功能添加到上面的组件中?

【问题讨论】:

您是否拥有 Spotify 的样式表以便能够使用其样式,然后创建上述功能的组件,然后在点击时,您将调用一个函数而不是 handleRedirect,该调用就是上面的组件,并且在他们同意的情况下,它将完成调用 handleRedirect 的流程,如果这是您想要的,请告诉我 是的,这正是我想要的。我在哪里可以找到样式表? 通常,范围授权页面是 OAuth 提供者的责任,而不是客户端的责任。你有什么理由想在你的应用中复制它? 隐式流用于客户端身份验证,不是吗?没有特别的原因,我只想将范围作为弹出窗口 是的,流程不需要服务器,但登录和同意仍然是身份验证提供程序的一部分,而不是客户端。你会复制它。您还需要在客户端上存储同意,这样您就不会再次询问。还需要实施访问撤销。这些都是身份验证提供者的职责。 【参考方案1】:

注意: 一旦您使用一个电子邮件 ID 接受了范围,您将不会再次显示范围页面。范围在开始时只被询问一次。如果您想再次查看范围页面,则必须授权新的电子邮件 ID。

我创建了一个反应应用来感受您的代码是如何工作的。我运行了以下代码:

import React,  Component  from 'react';

export const authEndpoint = 'https://accounts.spotify.com/authorize';
class SpotifyAuth extends Component 
  constructor(props) 
    super(props);
    this.state = 
      isAuthenticatedWithSpotify: false
      // menu: this.props.userId.menu
    ;
    this.state.handleRedirect = this.handleRedirect.bind(this);
  

  generateRandomString(length) 
    let text = '';
    const possible =
      'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
    for (let i = 0; i < length; i++) 
      text += possible.charAt(Math.floor(Math.random() * possible.length));
    
    return text;
  

  getHashParams() 
    const hashParams = ;
    const r = /([^&;=]+)=?([^&;]*)/g;
    const q = window.location.hash.substring(1);
    let e = r.exec(q);
    while (e) 
      hashParams[e[1]] = decodeURIComponent(e[2]);
      e = r.exec(q);
    
    return hashParams;
  

  componentDidMount() 
    //if (this.props.isAuthenticated) 
    const params = this.getHashParams();

    const access_token = params.access_token;
    const state = params.state;
    const storedState = localStorage.getItem('stateKey');
    localStorage.setItem('spotifyAuthToken', access_token);
    localStorage.getItem('spotifyAuthToken');

    if (window.localStorage.getItem('authToken')) 
      this.setState( isAuthenticatedWithSpotify: true );
    
    if (access_token && (state == null || state !== storedState)) 
      alert('Click "ok" to finish authentication with Spotify');
     else 
      localStorage.removeItem('stateKey');
    
    console.log(access_token);
    // DO STUFF WITH ACCEES TOKEN HERE
    // this.props.onConnectWithSpotify(access_token);
  

  handleRedirect(event) 
    event.preventDefault();
    console.log('You linked your Spotify account!', 'success');

    const params = this.getHashParams();
    const access_token = params.access_token;
    console.log(access_token);

    const state = this.generateRandomString(16);
    localStorage.setItem('stateKey', state);

    // let url = 'https://accounts.spotify.com/authorize';
    // url += '?response_type=token';
    // url +=
    //   '&client_id=' + encodeURIComponent('f09fbf600009433dadce5836c57584c3');
    // url += '&scope=' + encodeURIComponent('user-top-read');
    // url += '&redirect_uri=' + encodeURIComponent('http://localhost:3000/abc');
    // url += '&state=' + encodeURIComponent(state);
    // url += '&show_dialog=true';
    let url =
      'https://accounts.spotify.com/authorize' +
      '?response_type=code' +
      '&client_id=f09fbf600009433dadce5836c57584c3' +
      '&scope=' +
      encodeURIComponent('user-read-private%20user-read-email') +
      '&redirect_uri=' +
      encodeURIComponent('http://localhost:3000/loginsuccess');
    window.location = url;
  

  render() 
    return (
      <div className="button_container">
        <h1 className="title is-4">
          <font color="#C86428">Welcome</font>
        </h1>
        <div className="Line" />
        <br />
        <button
          className="sp_button"
          onClick=(event) => this.handleRedirect(event)
        >
          <strong>LINK YOUR SPOTIFY ACCOUNT</strong>
        </button>
      </div>
    );
  


export default SpotifyAuth;

我相信当我硬编码 url 而不是从 Credential 获取值时,代码没有问题。我能够看到范围页面 如果您能验证保存在Credential 中的范围是否正确,那就更好了。

我还注释掉了 this.props.createMessage 方法,这对我来说是抽象的,可能会导致一些问题。

【讨论】:

只是添加到该范围意味着给予用户同意。 您还可以进入您的帐户设置并撤销您的应用程序的访问权限。之后您应该再次看到范围:spotify.com/uk/account/apps @CameronDowner 这也很有帮助,Cameron,感谢您提供的链接。

以上是关于带有 React 的 Spotify 隐式授权流程 - 用户登录的主要内容,如果未能解决你的问题,请参考以下文章

Spotify - 使用隐式流获取和更改用户数据

HashRouter 导致 Spotify 隐式授权流失败,因为回调 URL 无效

Spotify PKCE 授权流程返回“code_verifier 不正确”

Spotify 令牌调用后返回错误网站而不是错误或令牌

在 Docker 中使用 React 和 Nginx 授权 Spotify

从 iOS 授权流程中完全阻止 Spotify 注册