Фон
Моему приложению для работы в доке требуется авторизация пользователя для получения его данных из Spotify.
Для этого я использую код авторизации, для которого требуется сервер- обмен данными между серверами, то есть: я делаю запросы к службам учетных записей Spotify с внутреннего сервера, а не с моего Клиента.
Примеры получения токена доступа можно найти повсюду, но я не нашел любой пример того, как получить другой токен доступа после истечения первого срока действия, используя refre sh токен.
Схема кода авторизации:
Мой сервер
Чтобы реализовать этот поток, я создал внутреннюю службу узла за nginx proxy
, например:
dev.conf
server {
listen 8888;
location / {
proxy_pass http://spotify-server:8888;
proxy_redirect default;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
proxy_read_timeout 86400s;
proxy_send_timeout 86400s;
}
}
Структура на хосте:
client/...
src/...
AuthorizationCode.jsx
nginx/...
dev.conf
web/...
spotify-server/
server.js
public/
index.html
Я все настроил и работаю на стадии (3) выше.
Таким образом, я могу получить токен доступа и токен refre sh, который будет использоваться через 60 минут.
Frontend
Я вызываю свою страницу перенаправления из Spotify на моем React
внешнем интерфейсе:
AuthorizationCode.jsx
onClick={() => window.location = 'http://localhost:8888'
Это отправляет авторизацию обратно на /callback
на сервере, который, в свою очередь, перенаправляет меня с бэкэнда на мой интерфейс по адресу http://localhost/#, с обоими токенами:
componentDidMount() {
const params = this.getHashParams();
const access_token = params.access_token
const refresh_token = params.refresh_token
localStorage.setItem('spotifyAuthToken', access_token);
localStorage.getItem('spotifyAuthToken');
}
Вышеописанное работает.
Код сервера:
Теперь это мой сервер. js код:
var app = express();
app.use(express.static(__dirname + '/public'))
.use(cors())
.use(cookieParser());
app.get('/login', function(req, res) {
var state = generateRandomString(16);
res.cookie(stateKey, state);
// your application requests authorization -------------> STEP 1
var scope = Credentials.scope;
res.redirect('https://accounts.spotify.com/authorize?' +
querystring.stringify({
response_type: 'code',
client_id: client_id,
scope: scope,
redirect_uri: redirect_uri,
state: state
}));
});
app.get('/callback', function(req, res) {
// your application requests refresh and access tokens
// after checking the state parameter
var code = req.query.code || null;
var state = req.query.state || null;
var storedState = req.cookies ? req.cookies[stateKey] : null;
if (state === null || state !== storedState) {
res.redirect('/#' +
querystring.stringify({
error: 'state_mismatch'
}));
} else {
res.clearCookie(stateKey);
var authOptions = {
url: 'https://accounts.spotify.com/api/token', //--------------> STEP 2
form: {
code: code,
redirect_uri: redirect_uri,
grant_type: 'authorization_code'
},
headers: {
'Authorization': 'Basic ' + (new Buffer(client_id + ':' + client_secret).toString('base64'))
},
json: true
};
request.post(authOptions, function(error, response, body) {
if (!error && response.statusCode === 200) {
var access_token = body.access_token,
refresh_token = body.refresh_token;
var options = {
url: 'https://api.spotify.com/v1/me',
headers: { 'Authorization': 'Bearer ' + access_token },
json: true
};
// use the access token to access the Spotify Web API
request.get(options, function(error, response, body) {
console.log(body);
});
// we can also pass the token to the browser to make requests from there
res.redirect('http://localhost/#' + //--------------> STEP 2
querystring.stringify({
access_token: access_token,
refresh_token: refresh_token
}));
} else {
res.redirect('/#' +
querystring.stringify({
error: 'invalid_token'
}));
}
});
}
});
И Именно здесь я пытаюсь обернуть голову: как ПОСТАВИТЬ и ПОЛУЧИТЬ новый токен доступа к моему серверу с моего внешнего интерфейса.
app.get('/refresh_token', function(req, res) { --------------> STEP 4
// requesting access token from refresh token
var refresh_token = req.query.refresh_token;
var authOptions = {
url: 'https://accounts.spotify.com/api/token',
headers: { 'Authorization': 'Basic ' + (new Buffer(client_id + ':' + client_secret).toString('base64')) },
form: {
grant_type: 'refresh_token',
refresh_token: refresh_token
},
json: true
};
request.post(authOptions, function(error, response, body) {
if (!error && response.statusCode === 200) {
var access_token = body.access_token;
res.send({
'access_token': access_token
});
}
});
});
console.log('Listening on 8888');
app.listen(8888);
Я пытался:
handleRefreshToken(event) {
const {token} = this.state.spotifyRefreshToken
const options = {
url: 'http://localhost:8888/refresh_token',
method: 'get',
data: token,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': true,
Authorization: `Bearer ${window.localStorage.authToken}`
}
};
return axios(options)
.then((res) => {
//console.log('getBaristaCoffee() --->', res.data.data)
console.log()
this.setState({
})
})
.catch((error) => { console.log(error); });
};
с:
onClick={(event) => this.handleRefreshToken(event)}
, но я получаю ошибку:
Access to XMLHttpRequest at 'http://localhost:8888/refresh_token' from origin 'http://localhost' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
, а затем:
GET http://localhost:8888/refresh_token net::ERR_FAILED
nginx
Журналы обслуживания выдают ошибку 499, обычно вызванную тайм-аутом на стороне клиента: * 10 90 *
nginx_2 | 172.18.0.1 - - [07/Mar/2020:21:56:12 +0000] "GET /refresh_token HTTP/1.1" 502 559 "http://localhost/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36" "-"
nginx_2 | 2020/03/07 21:57:12 [error] 6#6: *186 upstream prematurely closed connection while reading response header from upstream, client: <my_ip>, server: , request: "GET /refresh_token HTTP/1.1", upstream: "http://<my_ip>:8888/refresh_token", host: "localhost:8888", referrer: "http://localhost/"
nginx_2 | 172.18.0.1 - - [07/Mar/2020:21:57:12 +0000] "GET /refresh_token HTTP/1.1" 502 559 "http://localhost/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36" "-"
nginx_2 | 172.18.0.1 - - [07/Mar/2020:21:59:24 +0000] "GET /refresh_token HTTP/1.1" 499 0 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36" "-"
nginx_2 | 172.18.0.1 - - [07/Mar/2020:21:59:57 +0000] "GET /refresh_token HTTP/1.1" 499 0 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36" "-"
ВОПРОС
Как мне реализовать вызов '/ refresh_token' на моем бэкэнде из моего внешнего интерфейса, передав мой токен refre sh и получить новый токен доступа?
БОНУС
Функция наблюдателя, отслеживающая время истечения токена доступа 3600, получит 50 наград.