web-dev-qa-db-ja.com

React Native + Redux基本認証

反応ネイティブアプリの基本認証を作成する方法を探しています。反応ネイティブアプリの良い例が見つかりませんでした。

  • ログインするには、アプリがメール/パスワード+ clientSecretをサーバーに送信します
  • OKの場合、サーバーはaccessToken + refreshTokenを返します
  • ユーザーがログインし、他のすべてのリクエストにはaccessTokenを持つベアラが含まれます。
  • AccessTokenの有効期限が切れると、アプリはrefreshTokenを使用して新しいトークンを自動的に要求します。
  • ユーザーは常にログインしたままで、状態は電話に保存する必要があります。

これに最適なアプローチは何でしょうか?

ありがとう。

24
alexmngn

アプリが何らかの形式の認証を実施するHTTP APIと通信する場合、通常、アプリは次の手順に従います。

  1. アプリは認証されていないため、ログインするようユーザーに要求します。
  2. ユーザーは資格情報(ユーザー名とパスワード)を入力し、送信をタップします。
  3. これらの認証情報をAPIに送信し、応答を検査します:
    • 成功した場合(200-OK):このトークン/ハッシュを使用するため、認証トークン/ハッシュをキャッシュします以降のすべてのリクエスト。
      • 後続のAPIリクエストのいずれかでトークン/ハッシュが機能しない場合(401-不正)、ハッシュ/トークンを無効にし、ユーザーに再度ログインするように要求する必要があります。
    • または、失敗時(401-不正):ユーザーにエラーメッセージを表示し、資格情報の再入力を求めます。

ログインする

上記で定義されたワークフローに基づいて、アプリはログインフォームを表示することから始まります。ユーザーが以下のloginアクションクリエーターをディスパッチするログインボタンをタップすると、ステップ2が開始されます。

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

これを使用して、アクティビティインジケータ(回転ホイール、「読み込み中...」など)を表示し、ログインビューでログインボタンを無効にします。

次に、HTTP基本認証用にユーザーのユーザー名とパスワードをbase64でエンコードし、リクエストのヘッダーに渡します。

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')

loginSuccessloginFailure、および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_REQUESTAPI_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のみを扱っています。

概念は同じままであると思うので、accessTokenrefreshTokenをストアにプッシュし、後続のリクエストで抽出します。

リクエストが失敗した場合、accessTokenを更新し、元のリクエストをリコールする別のアクションをディスパッチする必要があります。

57
peterp

私はこの分野の例としてあまり見ていませんが、より多くの報道を必要とするものだと思います。私はまだ自分自身で認証を実装していません。それ以外の場合は、いくつかのコード例を示します。しかし、私はあなたが正しい方向にあなたを助けるかもしれない私が集めたいくつかのリンクにあなたを指すことができます...

認証の実行方法に関係なく、アクセストークン、更新トークン、シークレットトークンを安全に保存する必要があります。 iOSでは、 keychain を使用してそれを行うと思います。Androidは KeyStore のように見えます。 oblador/react-native-keychain 役に立ちますが、まだサポートしていませんAndroid it サポートしているように見えるAndroid =まもなく

4
Chris Geirman

実際、私はあなたの質問の少なくともいくつかに答えるビデオチュートリアルシリーズに取り組んでいます。ビデオとトランスクリプトおよびサンプルコードは、次の場所にあります。 http://codecookbook.co/post/how-to-build-a-react-native-login-form-with-redux-pt1/

0
kmdupr33