Как обрабатывать запросы refre sh token с ReactJS, сервером узлов и nginx прокси - PullRequest
1 голос
/ 07 марта 2020

Фон

Моему приложению для работы в доке требуется авторизация пользователя для получения его данных из Spotify.

Для этого я использую код авторизации, для которого требуется сервер- обмен данными между серверами, то есть: я делаю запросы к службам учетных записей Spotify с внутреннего сервера, а не с моего Клиента.

Примеры получения токена доступа можно найти повсюду, но я не нашел любой пример того, как получить другой токен доступа после истечения первого срока действия, используя refre sh токен.

Схема кода авторизации:

enter image description here


Мой сервер

Чтобы реализовать этот поток, я создал внутреннюю службу узла за 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 наград.

...