Авторизуйте Spotify, используя React и Nginx в Docker - PullRequest
7 голосов
/ 14 марта 2019

Я создаю докернизированное REST API приложение со следующей структурой:

../
  web/
  nginx/
       dev.conf   
       Dockerfile-dev  
  client/
        build/
        conf/
        Dockerfile-dev
        node_modules/
        package_json
        public/
        src/
           App.jsx   
           components/ 
                     SpotifyRedirect.jsx                  
 spotify-client/ 
                Dockerfile-dev
                node_modules
                package-lock.json
                package.json
                authorization_code/
                                  app.js 

ПРИМЕЧАНИЕ: В этом проекте пользователю необходимо пройти два авторизация / аутентификация процессов:

  1. один с моим приложением, который генерирует собственный token (с этим уже разобрались)
  2. другой с Spotify (который требует redirect URI и предоставляет собственный token для доступа к API)

    2a) Итак, на localhost, либо после localhost/auth/register или localhost/auth/loginотправить, я бы Spotify redirect URI (http://localhost:8888), принять меня на эту страницу:

enter image description here

2b) Затем, нажав на кнопку входа в систему, мне будет предложено подключить мое приложение к Spotify, например:

enter image description here

И с последним OK там я получил бы разрешение и вручил token, который я мог сохранить и даже обновить на моем React клиенте.


Строительные блоки для этого проекта были извлечены из этого урока:

using-spotifys-awesome-api-with-реакция

Тем не менее, я уже настроил React client, который также служит другим целям, помимо этого процесса авторизации.

Ниже приведен соответствующий код, который пытается объединить эти две службы: более общий client и spotify-client.


Соответствующий код:

Итак, моей первой попыткой было создать специальный сервис для spotify-client, ниже client сервиса, выставив его на порт 8888, например:

docker-compose-dev.yml

  nginx:
    build:
      context: ./services/nginx
      dockerfile: Dockerfile-dev
    restart: always
    ports:
      - 80:80
    depends_on:
      - web
      - client
      - spotify-client

  client:
    build:
      context: ./services/client
      dockerfile: Dockerfile-dev
    volumes:
      - './services/client:/usr/src/app'
      - '/usr/src/app/node_modules'
    ports:
      - 3007:3000
    environment:
      - NODE_ENV=development
      - REACT_APP_WEB_SERVICE_URL=${REACT_APP_WEB_SERVICE_URL}
    depends_on:
      - web

  spotify-client: // NEW
    build:
      context: ./services/spotify-client
      dockerfile: Dockerfile-dev
    volumes:
      - './services/spotify-client:/usr/src/app'
      - '/usr/src/app/node_modules'
    ports:
      - 3000:8888
      - 8888:3000
    environment:
      - NODE_ENV=development
      - REACT_APP_WEB_SERVICE_URL=${REACT_APP_WEB_SERVICE_URL}
    depends_on:
      - web
      - client

Затем я настраиваю каждый процесс узла на свой Dockerfile, например:

client /Dockerfile-dev

# base image
FROM node:11.6.0-alpine

# set working directory
WORKDIR /usr/src/app

# add `/usr/src/app/node_modules/.bin` to $PATH
ENV PATH /usr/src/app/node_modules/.bin:$PATH

# install and cache app dependencies
COPY package.json /usr/src/app/package.json
RUN npm install --silent
RUN npm install react-scripts@2.1.2 -g --silent

# start app
CMD ["npm", "start"]

и :

spotify-client / Dockerfile-dev //NEW

, на котором запущен другой процесс в соответствии с требованиями Spotify веб-документов:

# base image
FROM node:11.6.0-alpine

# set working directory
WORKDIR /usr/src/app/authorization_code

# add `/usr/src/app/node_modules/.bin` to $PATH
ENV PATH /usr/src/app/node_modules/.bin:$PATH

# install and cache app dependencies
COPY package.json /usr/src/app/package.json
RUN npm install --silent
RUN npm install react-scripts@2.1.2 -g --silent

# start app <-- NOT npm start
CMD ["node", "app.js"] 

Мой обратный прокси-сервер, я пробовал:

nginx / dev.conf

server {

  listen 80;
  listen 8888; // NEW

  location / {
    proxy_pass        http://client:3000;
    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;
  }


  location /auth {    // <-- app authorization, not Spotify's
    proxy_pass        http://web:5000;
    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;
  }

На своем интерфейсе я создаю component для моей ссылки перенаправления:

client / src / components / SpofityRedirect.jsx

import React, { Component } from 'react';

class SpotifyRedirect extends Component{
    render(){
        return (
            <div className='SpotifyRedirect'>
                <a href='http://localhost:8888'> Log in with Spotify </a>
            </div>
        );
    }
}

export default SpotifyRedirect;

и здесь я показываю эту переадресациюect link в '/'.

client / src / App.jsx

import SpotifyRedirect from './components/SpotifyRedirect';

 (...)
 <Switch
  <Route exact path='/' render={() => (
   <SpotifyRedirect/>
  )} />

 (...)
 </Switch>

Подробнее:

spotify-client / authorization_code / app.js

(это предоставляется Spofity, и я только вставил http://localhost:3000)

var express = require('express'); // Express web server framework
var request = require('request'); // "Request" library
var cors = require('cors');
var querystring = require('querystring');
var cookieParser = require('cookie-parser');

var client_id = 'is'; // Your client id
var client_secret = 'secret'; // Your secret
var redirect_uri = 'http://localhost:8888'; // Your redirect uri

/**
 * Generates a random string containing numbers and letters
 * @param  {number} length The length of the string
 * @return {string} The generated string
 */
var generateRandomString = function(length) {
  var text = '';
  var possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';

  for (var i = 0; i < length; i++) {
    text += possible.charAt(Math.floor(Math.random() * possible.length));
  }
  return text;
};

var stateKey = 'spotify_auth_state';

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
  var scope = 'user-read-private user-read-email user-read-playback-state playlist-modify-public playlist-modify-private';
  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',
      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:3000/#' + //NEW
          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) {

  // 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);

______

Docker служб в командной строке:

CONTAINER ID        IMAGE                      COMMAND                  CREATED             STATUS              PORTS                            NAMES
0e9870a7412c        dev3_nginx                 "nginx -g 'daemon of…"   9 seconds ago       Up 6 seconds        0.0.0.0:80->80/tcp               dev3_nginx_1
e6bc5bbff630        dev3_spotify-client        "node app.js"            26 minutes ago      Up 26 minutes       0.0.0.0:3000->8888/tcp           dev3_spotify-auth-server_1
a6b9e84953a3        dev3_client                "npm start"              25 hours              "/start.sh"              25 hours ago        Up 25 hours         80/tcp, 0.0.0.0:3008->8080/tcp  
16fb623ca2b3        dev3_web                   "/usr/src/app/entryp…"   25 hours 

Наконец, перед сборкой я запускаю:

$ export REACT_APP_WEB_SERVICE_URL=http://localhost


Пока что с настройкой aboce, когда я нажимаю Войдите в систему с Soptify , я получаю:

enter image description here


ВОПРОС:

Как использовать вышеуказанную конфигурацию с моим nginx reverse proxy для того, чтобы:

  1. Укажите местоположение / со ссылкой для моего Spotify's перенаправления uri http://localhost:8888
  2. Авторизуйте приложение с помощью Spotify
  3. Завершитеобратно в локацию '/' когда авторизация будет завершена?

Ответы [ 2 ]

4 голосов
/ 16 марта 2019

Проблема: ни один контейнер не прослушивает порт 8888. Вы можете опубликовать spotify-client:8888 непосредственно на порту 8888 (без nginx). Обновление docker-compose-dev.yml:

 spotify-client:
    ports:
      - 8888:8888

Если вам действительно нужен nginx, вам нужно будет поиграть с конфигурацией nginx + вам также нужно опубликовать nginx на порту 8888. nginx/dev.conf пример:


  server {
    listen 80;
    location / {
      proxy_pass        http://client:3000;
      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;
    }
    location /auth {
      proxy_pass        http://web:5000;
      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;
    }
  }

  # reverse proxy on the port 8888 for spotify-client
  server {
    listen  8888;
    location / {
      proxy_pass http://<spotify-client service/ip>:<port>/;
      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;
    }
  }

docker-compose-dev.yml и опубликованные порты nginx:

  nginx:
    ports:
      - 80:80
      - 8888:8888

Как правило, вам необходимо правильно настроить nginx:8888 -> spotify-client:port.

ИМХО: вам вообще не нужен сервис spotify-client. Просто используйте неявный поток в своем приложении, чтобы получить токен Spotify. Это лучший выбор для React / Angular (код JS браузера). Имейте в виду, что токены обновления не существуют в этом потоке, поэтому вам также потребуется реализовать тихое обновление.

0 голосов
/ 23 марта 2019

Когда вы решили использовать nginx, вам нужно отредактировать конфигурацию и опубликовать nginx на порту 8888.

...