2017-02-02 11 views
6

До сих пор я имел дело только с приложениями с сервером, где после входа пользователя в систему через имя пользователя/пароль или с помощью поставщика OAuth (Facebook и т. Д.) Сервер просто устанавливает cookie сеанса при перенаправлении на соответствующую страницу.Как обеспечить интерфейс с помощью веб-маркера JSON после аутентификации сервера?

Однако теперь я пытаюсь создать приложение, используя более «современный» подход, с React на интерфейсе и бэкэндом JSON API. По-видимому, стандартным выбором для этого является использование веб-маркера JSON для аутентификации, однако у меня возникли проблемы с разработкой того, как я должен предоставлять JWT клиенту, чтобы он мог храниться в сеансовом/локальном хранилище или где угодно.

Пример для иллюстрации лучше:

  1. Пользователь нажимает ссылку (/auth/facebook), чтобы войти с помощью Facebook

  2. пользователя перенаправляется и показаны Facebook входа в форму и/или диалог разрешения (при необходимости)

  3. Facebook перенаправляет пользователя обратно на /auth/facebook/callback с кодом авторизации на буксире, сервер обменивает это на токен доступа и некоторая информация о пользователе

  4. Сервер находит или создает пользователя в БД с использованием информации, затем создает JWT, содержащий соответствующее подмножество пользовательских данных (например, ID)

  5. ???

На данный момент я просто хочу, пользователь будет перенаправлен на главную страницу для React приложения (скажем /app) с JWT на буксире, так что интерфейс может взять на себя. Но я не могу придумать (элегантный) способ сделать это, не потеряв JWT на этом пути, кроме как поместить его в строку запроса для перенаправления (/app?authtoken=...) - но это будет отображаться в адресной строке, пока я не удалю он вручную использует replaceState() или что-то еще, и мне кажется немного странным.

Действительно, мне просто интересно, как это обычно делается, и я почти уверен, что здесь что-то отсутствует. Сервер - это Node (Koa с паспортом), если это помогает.

Редактировать: Чтобы быть ясным, я спрашиваю, что лучший способ предоставить токен клиенту (чтобы он мог быть сохранен) после перенаправления OAuth с использованием паспорта.

+0

Вы узнали ответ? Я пытаюсь найти ответ на эту проблему, но я не могу найти полезной информации. – Mariusz

ответ

1

Когда вы получаете токен с любых сайтов аутентификации паспорта, вы должны сохранить токен в localStorage вашего браузера. Диспетчер - это промежуточное ПО Redux. Игнорируйте dispatch, если вы не используете сокращение в своем приложении. вы можете просто использовать setState здесь (Немного странно без сокращения).

сторона клиента:

Вот что-то подобное API моего, который возвращает маркер.

сохранение лексемы

axios.post(`${ROOT_URL}/api/signin`, { email, password }) 
     .then(response => { 

      dispatch({ type: AUTH_USER }); //setting state (Redux's Style) 
      localStorage.setItem('token', response.data.token); //saving token 
      browserHistory.push('/home'); //pushes back the user after storing token 
     }) 
     .catch(error => { 
      var ERROR_DATA; 
      try{ 
       ERROR_DATA = JSON.parse(error.response.request.response).error; 
      } 
      catch(error) { 
       ERROR_DATA = 'SOMETHING WENT WRONG'; 
      } 
      dispatch(authError(ERROR_DATA)); //throw error (Redux's Style) 
     }); 

Так когда вы делаете несколько запросов с проверкой подлинности, вы должны прикрепить маркер с просьбой в этой форме.

аутентифицированные запросы

axios.get(`${ROOT_URL}/api/blog/${blogId}`, { 
     headers: { authorization: localStorage.getItem('token') } 
//take the token from localStorage and put it on headers ('authorization is my own header') 
    }) 
     .then(response => { 
      dispatch({ 
       type: FETCH_BLOG, 
       payload: response.data 
      }); 
     }) 
     .catch(error => { 
      console.log(error); 
     }); 

Вот мои index.js: Маркер проверяется каждый и каждый раз, так что даже если браузер получил освеженный, вы можете установить состояние.

проверяет, является ли проверка подлинности пользователя

const token = localStorage.getItem('token'); 

if (token) { 
    store.dispatch({ type: AUTH_USER }) 
} 

ReactDOM.render(
    <Provider store={store}> 
    <Router history={browserHistory}> 
     <Route path="/" component={App}> 
.. 
.. 
.. 
     <Route path="/blog/:blogid" component={RequireAuth(Blog)} /> 
//ignore this requireAuth - that's another component, checks if a user is authenticated. if not pushes to the index route 
     </Route> 
    </Router> 
    </Provider> 
    , document.querySelector('.container')); 

Все, что Dispach действия делает это устанавливает состояние.

мой файл редуктора (только для Redux), иначе вы можете просто использовать setState() в файле маршрута указателя, чтобы обеспечить состояние для всего приложения. Каждый раз, когда вызывается диспетчер, он запускает аналогичный файл-редуктор, подобный этому, который устанавливает состояние.

установка состояния

import { AUTH_USER, UNAUTH_USER, AUTH_ERROR } from '../actions/types'; 

export default function(state = {}, action) { 
    switch(action.type) { 
     case AUTH_USER: 
      return { ...state, error: '', authenticated: true }; 
     case UNAUTH_USER: 
      return { ...state, error: '', authenticated: false }; 
     case AUTH_ERROR: 
      return { ...state, error: action.payload }; 
    } 

    return state; 
} //you can skip this and use setState() in your index route instead 

Удалить маркер из вашего LocalStorage выйти из системы.

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

Server-Side браузера:

рассматривает файл паспортные услуги. Вы должны установить поиск заголовка. Вот passport.js

const passport = require('passport'); 
const ExtractJwt = require('passport-jwt').ExtractJwt; 
const JwtStrategy = require('passport-jwt').Strategy; 
.. 
.. 
.. 
.. 
const jwtOptions = { 
jwtFromRequest: ExtractJwt.fromHeader('authorization'), //client's side must specify this header 
secretOrKey: config.secret 
}; 

const JWTVerify = new JwtStrategy(jwtOptions, (payload, done) => { 
    User.findById(payload._id, (err, user) => { 
     if (err) { done(err, null); } 

     if (user) { 
      done(null, user); 
     } else { 
      done(null, false); 
     } 
    }); 
}); 

passport.use(JWTVerify); 

В моих router.js

const passportService = require('./services/passport'); 
const requireAuthentication = passport.authenticate('jwt', { session: false }); 
.. 
.. 
.. 
//for example the api router the above react action used 
app.get('/api/blog/:blogId', requireAuthentication, BlogController.getBlog); 
+1

Я понимаю, как использовать токен для аутентификации с внешнего интерфейса, меня интересует только то, как получить его для клиента в первую очередь. В *** сохранение токенов *** вы делаете запрос Ajax от внешнего интерфейса для входа с именем пользователя и паролем, но при использовании стратегии OAuth, например, «паспорт-facebook» (или «паспорт-твиттер» и т. Д.), Который не является 't вариант из-за перенаправления потока. Если я чего-то не упущу. – Inkling

0

здесь запрос Войти на стороне сервера.это хранение маркера в заголовке:

router.post('/api/users/login', function (req, res) { 
    var body = _.pick(req.body, 'username', 'password'); 
    var userInfo; 

models.User.authenticate(body).then(function (user) { 
     var token = user.generateToken('authentication'); 
     userInfo = user; 

     return models.Token.create({ 
     token: token 
     }); 
    }).then(function (tokenInstance) { 
     res.header('Auth', tokenInstance.get('token')).json(userInfo.toPublicJSON()); 
    }).catch(function() { 
     res.status(401).send(); 
    }); 
}); 

здесь запрос Войти на среагировать сторону, где я схватил маркер из заголовка и установив маркер в локальном хранилище раз аутентификации имени пользователя и пароль PASS:

handleNewData (creds) { 
    const { authenticated } = this.state; 
    const loginUser = { 
     username: creds.username, 
     password: creds.password 
    } 
    fetch('/api/users/login', { 
     method: 'post', 
     body: JSON.stringify(loginUser), 
     headers: { 
      'Authorization': 'Basic'+btoa('username:password'), 
      'content-type': 'application/json', 
      'accept': 'application/json' 
     }, 
     credentials: 'include' 
    }).then((response) => { 
     if (response.statusText === "OK"){ 
      localStorage.setItem('token', response.headers.get('Auth')); 
      browserHistory.push('route'); 
      response.json(); 
     } else { 
      alert ('Incorrect Login Credentials'); 
     } 
    }) 
} 
0
  1. Client: Открыть всплывающее окно с помощью $ auth.authenticate ('имя поставщика').
  2. Клиент: Войдите в систему с этим провайдером, если необходимо, затем авторизуйте приложение.
  3. Клиент: после успешного авторизации всплывающее окно перенаправляется обратно в ваше приложение, например. http://localhost:3000, с параметром строки запроса (код авторизации).
  4. Клиент: параметр кода отправляется обратно в родительское окно, открывающее всплывающее окно.
  5. Клиент: Родительское окно закрывает всплывающее окно и отправляет запрос POST в параметр/auth/provider withcode.
  6. Сервер: код авторизации обменивается на токен доступа.
  7. Сервер: информация пользователя извлекается с использованием маркера доступа с этапа 6.
  8. Сервер: найдите пользователя по их уникальному идентификатору поставщика. Если пользователь уже существует, возьмите существующего пользователя, иначе создайте новую учетную запись пользователя.
  9. Сервер: В обоих случаях шага 8 создайте веб-маркер JSON и отправьте его клиенту.
  10. Клиент: проанализируйте токен и сохраните его в локальном хранилище для последующего использования после перезагрузки страницы.

    Выйдите

  11. Клиент: Удалить маркер из локального хранилища
2

Недавно я натыкался на этот же вопрос, и, не найдя решения здесь или в другом месте, писал this blog post с моими углубленных мыслями.

TL; DR: Я пришел с 3-х возможных подходов отправить JWT клиенту после OAuth логинов/перенаправляет:

  1. Сохранить JWT в печенье, а затем извлечь его на фронт- конец или сервер на следующем шаге (например, извлеките его на клиенте с помощью JS или отправьте запрос на сервер, сервер использует cookie для получения JWT, возвращает JWT).
  2. Отправьте JWT обратно как часть строки запроса (что вы предлагаете в своем вопросе).
  3. Отправить обратно сервер рендеренной HTML-страницу с <script> тег, который:
    1. Автоматически сохраняет встроенный JWT в localStorage
    2. автоматически перенаправляет клиента на любой странице вы хотите после этого.

(Поскольку вход с JWTs по существу эквивалентно «сохранение JWT в localStorage, мой любимый вариант был # 3, но возможно есть и отрицательные стороны я не считаются. Я заинтересован в слышать, что здесь думают другие.)

Надеюсь, что это поможет!

+0

Фантастический. Они боролись между одним и двумя вариантами. Третьего не произошло. Отличная работа. –