Подтвердить что ты не робот

Базовая аутентификация React Native + Redux

Я ищу способ создать базовую аутентификацию для своего адаптивного приложения. Я не мог найти хороший пример для адаптивного приложения.

  • Чтобы войти в систему, приложение отправляет на мой сервер адрес электронной почты/пароль + clientSecret.
  • Если ОК, сервер возвращает accessToken + refreshToken
  • Пользователь вошел в систему, все остальные запросы включают носитель с accessToken.
  • Если истечение срока действия accessToken истекает, приложение автоматически запрашивает новый с помощью refreshToken.
  • Пользователь остается включенным все время, состояния должны сохраняться в телефоне.

Какой был бы лучший подход для этого?

Спасибо.

4b9b3361

Ответ 1

Когда приложение взаимодействует с HTTP API, который обеспечивает определенную форму аутентификации, приложение обычно следует следующим шагам:

  • Приложение не аутентифицировано, поэтому мы предлагаем пользователю войти в систему.
  • Пользователь вводит свои учетные данные (имя пользователя и пароль) и отправляет сообщения.
  • Мы отправляем эти учетные данные в API и проверяем ответ:
    • Успех (200 - ОК): Мы кэшируем токен/хэш аутентификации, потому что мы будем использовать этот токен/хеш в каждом последующем запросе.
      • Если маркер/хэш не работает во время любого последующего запроса API (401 - Unauthorized), нам нужно будет аннулировать хеш-маркер и предложить пользователю снова войти в систему.
    • Или при сбое (401 - Несанкционированное): Мы выводим сообщение об ошибке пользователю, предлагая повторно ввести свои учетные данные.

Вход в систему

В зависимости от рабочего потока, определенного выше, наше приложение начинается с отображения формы входа, шаг 2 срабатывает, когда пользователь вводит кнопку входа в систему, которая отправляет создатель действия login ниже:

/// actions/user.js

export function login(username, password) {
  return (dispatch) => {

    // We use this to update the store state of `isLoggingIn`          
    // which can be used to display an activity indicator on the login
    // view.
    dispatch(loginRequest())

    // Note: This base64 encode method only works in NodeJS, so use an
    // implementation that works for your platform:
    // `base64-js` for React Native,
    // `btoa()` for browsers, etc...
    const hash = new Buffer(`${username}:${password}`).toString('base64')
    return fetch('https://httpbin.org/basic-auth/admin/secret', {
      headers: {
        'Authorization': `Basic ${hash}`
      }
    })
    .then(response => response.json().then(json => ({ json, response })))
    .then(({json, response}) => {
      if (response.ok === false) {
        return Promise.reject(json)
      }
      return json
    })
    .then(
      data => {
        // data = { authenticated: true, user: 'admin' }
        // We pass the `authentication hash` down to the reducer so that it
        // can be used in subsequent API requests.

        dispatch(loginSuccess(hash, data.user))
      },
      (data) => dispatch(loginFailure(data.error || 'Log in failed'))
    )
  }
}

В этой функции много кода, но успокаивайтесь в том, что большая часть кода дезинфицирует ответ и может быть отвлечена.

Первое, что мы делаем, это отправить действие LOGIN_REQUEST, которое обновляет наш магазин и дает нам знать, что пользователь isLoggingIn.

dispatch(loginRequest())

Мы используем это, чтобы отображать индикатор активности (вращающееся колесо, "Загрузка..." и т.д.) и отключить кнопку входа в наш журнал.

Затем мы base64 кодируем имя пользователя и пароль для http basic auth и передаем его заголовкам запроса.

const hash = new Buffer(`${username}:${password}`).toString('base64')
return fetch('https://httpbin.org/basic-auth/admin/secret', {
  headers: {
    'Authorization': `Basic ${hash}`
  }
/* ... */

Если все пойдет хорошо, мы отправим действие LOGIN_SUCCESS, в результате чего мы получим аутентификацию hash в нашем магазине, которую мы будем использовать в последующих запросах.

dispatch(loginSuccess(hash, data.user))

С другой стороны, если что-то пошло не так, мы также хотим сообщить пользователю:

dispatch(loginFailure(data.error || 'Log in failed')

Создатели действий loginSuccess, loginFailure и loginRequest являются довольно типичными и на самом деле не требуют образцов кода. См.: https://github.com/peterp/redux-http-basic-auth-example/blob/master/actions/user.js)

Редуктор

Наш редуктор также типичен:

/// reducers/user.js
function user(state = {
  isLoggingIn: false,
  isAuthenticated: false
}, action) {
  switch(action.type) {
    case LOGIN_REQUEST:
      return {
        isLoggingIn: true, // Show a loading indicator.
        isAuthenticated: false
      }
    case LOGIN_FAILURE:
      return {
        isLoggingIn: false,
        isAuthenticated: false,
        error: action.error
      }
    case LOGIN_SUCCESS:
      return {
        isLoggingIn: false,
        isAuthenticated: true, // Dismiss the login view.
        hash: action.hash, // Used in subsequent API requests.
        user: action.user
      }
    default:
      return state
  }
}

Последующие запросы API

Теперь, когда у нас есть хэш аутентификации в нашем магазине, мы можем передать его в последующие заголовки запроса.

В нашем примере ниже мы собираем список друзей для нашего аутентифицированного пользователя:

/// actions/friends.js
export function fetchFriends() {
  return (dispatch, getState) => {

    dispatch(friendsRequest())

    // Notice how we grab the hash from the store:
    const hash = getState().user.hash
    return fetch(`https://httpbin.org/get/friends/`, {
      headers: {
        'Authorization': `Basic ${hash}`
      }
    })
    .then(response => response.json().then(json => ({ json, response })))
    .then(({json, response}) => {
      if (response.ok === false) {
        return Promise.reject({response, json})
      }
      return json
    })
    .then(
      data => {
        // data = { friends: [ {}, {}, ... ] }
        dispatch(friendsSuccess(data.friends))
      },
      ({response, data}) => {
        dispatch(friendsFailure(data.error))

        // did our request fail because our auth credentials aren't working?
        if (response.status == 401) {
          dispatch(loginFailure(data.error))
        }
      }
    )
  }
}

Вы можете обнаружить, что большинство запросов API обычно отправляют те же 3 действия, что и выше: API_REQUEST, API_SUCCESS и API_FAILURE, и поэтому большинство кода запроса/ответа может быть перенесено в промежуточное ПО Redux.

Мы извлекаем маркер аутентификации хэша из хранилища и настраиваем запрос.

const hash = getState().user.hash
return fetch(`https://httpbin.org/get/friends/`, {
  headers: {
    'Authorization': `Basic ${hash}`
  }
})
/* ... */

Если ответ API с кодом статуса 401, мы должны удалить хэш из хранилища и снова представить пользователю журнал.

if (response.status == 401) {
  dispatch(loginFailure(data.error))
}

Я ответил на вопрос в целом и касался только http-basic-auth.

Я думаю, что концепция может оставаться прежней, вы нажимаете accessToken и refreshToken в хранилище и извлекаете ее в последующих запросах.

Если запрос терпит неудачу, вам придется отправить другое действие, которое обновляет accessToken, а затем вызывает исходный запрос.

Ответ 2

Я не видел слишком много примеров в этой области и думаю, что это определенно то, что требует большего освещения. Я еще не реализовал auth самостоятельно, иначе я бы указал вам на некоторые примеры кода. Но я могу указать вам пару ссылок, которые я собрал, которые могут помочь вам в правильном направлении...

Независимо от того, как вы выполняете аутентификацию, вам необходимо безопасно хранить ваши доступ, обновления и секретные токены. На iOS я считаю, что вы сделаете это, используя keychain, а для Android это выглядит как KeyStore. Вы можете найти oblador/react-native-keychain полезный, хотя он еще не поддерживает андроид похоже, что он может скоро поддерживать Android.

Ответ 3

Я на самом деле работаю над серией видеоуроков, которая отвечает хотя бы на некоторые из вопросов, которые вы задаете. Видео вместе с транскриптом и примерным кодом можно найти здесь: http://codecookbook.co/post/how-to-build-a-react-native-login-form-with-redux-pt1/