使用 React 的 Spotify 隐式授权流程 - 用户登录
Spotify Implicit Grant Flow with React - user login
下面的这个 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;
但是,在重定向之前,我想描述以下页面或弹出窗口,其中包含已定义的范围和 'agree' 按钮:
根据 Implicit Flow 的 Spotify 文档,将用户重定向到 https://accounts.spotify.com/authorize?client_id=5fe01282e94241328a84e7c5cc169164&redirect_uri=http:%2F%2Fexample.com%2Fcallback&scope=user-read-private%20user-read-email&response_type=token&state=123
...performs a couple of actions:
The user is asked to authorize access within the scopes. The Spotify Accounts >service presents details of the scopes for which access is being sought.
If the user is not logged in, they are prompted to do so using their Spotify >username and password.
When the user is logged in, they are asked to authorize access to the data sets >defined in the scopes.
None 上面的操作发生在我调用 https://accounts.spotify.com/authorize?
时上面的代码中,我只是得到了令牌。
怎么了?
我找到了这个代码笔示例,其中提示用户登录页面:
Spotify Implicit Grant Auth Popup
如何将此功能添加到我上面的组件中?
注意:
一旦您使用一个电子邮件 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
方法,这对我来说是抽象的,可能会导致一些问题。
下面的这个 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;
但是,在重定向之前,我想描述以下页面或弹出窗口,其中包含已定义的范围和 'agree' 按钮:
根据 Implicit Flow 的 Spotify 文档,将用户重定向到 https://accounts.spotify.com/authorize?client_id=5fe01282e94241328a84e7c5cc169164&redirect_uri=http:%2F%2Fexample.com%2Fcallback&scope=user-read-private%20user-read-email&response_type=token&state=123
...performs a couple of actions:
The user is asked to authorize access within the scopes. The Spotify Accounts >service presents details of the scopes for which access is being sought. If the user is not logged in, they are prompted to do so using their Spotify >username and password. When the user is logged in, they are asked to authorize access to the data sets >defined in the scopes.
None 上面的操作发生在我调用 https://accounts.spotify.com/authorize?
时上面的代码中,我只是得到了令牌。
怎么了?
我找到了这个代码笔示例,其中提示用户登录页面:
Spotify Implicit Grant Auth Popup
如何将此功能添加到我上面的组件中?
注意: 一旦您使用一个电子邮件 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
方法,这对我来说是抽象的,可能会导致一些问题。