2016-06-28 10 views
6

私はReduxアプリケーションとOAuthサーバとして機能するリモートAPIを持っています。典型的なルーチンに基づいて、ユーザーはトークンに資格情報を交換します。トークンは、アプリケーションがデータを取得してサーバー上で何かを行うために使用されます。このトークンはストアに格納され、sessionStorageにも格納されます。Reduxアプリケーション内でOAuthアクセストークンを提供するための戦略

アクセストークンが期限切れになることがありますが、リフレッシュトークンが受信されているため、まずリフレッシュを試みてください。間違った場合にのみ、ユーザーに署名してください。

私は、技術的には特定のアクションを単に送信することを意味する完全な署名部分を完全に理解しています。しかし、どのようにしてトークンリフレッシュルーチンを簡略化できますか?

私はredux-sagaを試しましたが、非常に冗長です。リモートAPIに依存するすべてのアクションのコードを部分的に複製して、そのようなすべてのリクエストが最初にアクセストークンをチェックし、それが期限切れでないかどうかを確認し、そうでなければそれを更新するようにしなければなりません。

私がやろうとしたもう一つのことは、PromiseにラップされたリモートAPIへのリクエストで特定のタイプのアクションが予想されるミドルウェアです。この種の作品ですが、別の方法があれば私は興味があります。

誰もこれを(かなり一般的な)種類のものに実装しましたか?任意のアイデアトークンのリフレッシュを自動化する方法とコードの量が増加して怒っていない?おそらく高次のコンポーネントですか?

+1

解決策が示されているにもかかわらず、誰もが自分の思考や経験を追加することを大変歓迎しています。 –

答えて

9

シームレスで汎用的なものが必要なコードでは、通常はミドルウェアが必要です。ストアを作成する際にミドルウェアを含めるコードと、トークンロジックを処理するシンプルな関数を記述するために、2行のコードを追加するだけで簡単です。

はあなたがのようなお店を作成しましょう:

import { createStore, applyMiddleware, compose } from 'redux'; 
import rootReducer from './reducers'; 
import { browserHistory } from 'react-router'; 
import { routerMiddleware } from 'react-router-redux'; 
import tokenMiddleware from './middleware/token'; 

const finalCreateStore = compose(
    applyMiddleware(
     routerMiddleware(browserHistory), 
     tokenMiddleware, 
    ), 
    window.devToolsExtension ? window.devToolsExtension() : f => f, 
)(createStore); 

その後は、初期状態で、どこからこの関数を呼び出します。

const store = finalCreateStore(rootReducer, initialState); 

これにより、店舗を通過するすべての操作で何かを行うことができます。約束を使ってAPIコールを処理するミドルウェアを持つことは非常に一般的であるため、この目的のためにAPIコールを再利用して一緒にバンドルすることを好む人もいます。あなたは、おそらくのために行くことができる別の方法で処理することです

function refreshToken(user, maybeSomeOtherParams) { 
    const config = getSomeConfigs; 

    return dispatch => { 
     makeAPostCallWithParamsThatReturnsPromise 
     .then(result => dispatch(saveNewToken({ 
      result, 
      ... 
     }))) 
     .catch(error => dispatch({ 
      type: uh_oh_token_refresh_failed_action_type, 
      error, 
     })); 
}; 

:あなたのリフレッシュトークンサンクは、これに似たものになる可能性が

export const tokenMiddleware = ({ dispatch, getState }) => next => action => { 
    if (typeof action === 'function') { // pass along 
     return action(dispatch, getState); 
    } 

    // so let's say you have a token that's about to expire 
    // and you would like to refresh it, let's write so pseudo code 

    const currentState = getState(); 
    const userObj = state.authentication.user; 

    if (userObj.token && userObj.token.aboutToExpire) { 
     const config = getSomeConfigs(); 
     // some date calculation based on expiry time that we set in configs 
     const now = new Date(); 
     const refreshThreshold = config.token.refreshThreshold; 

     if (aboutToExpireAndIsBelowThresholdToRefresh) { 
      // refreshTheToken can be an action creator 
      // from your auth module for example 
      // it should probably be a thunk so that you can handle 
      // an api call and a promise within once you get the new token 
      next(refreshTheToken(userObj, someOtherParams); 
     } 
    } 

    .... 

    return next(action); 
} 

典型的なミドルウェアは、このようになります。これはルートを変更するときです。

認証が必要なルートと有効なユーザーがシステムに存在するルートのどこかにトップレベルのルートがあるとします。それらをauthenticated routesと呼んでみましょう。

authenticated routesは、onChangeハンドラ関数を定義する最上位ルートでラップできます。このような何か:

<Route onChange={authEntry}> 
    <Route ... /> // authenticated routes 
    <Route ... /> 
</Route> 

これらのルートを作成し、あなたが店を作成したら、あなたはcheckAuthと呼ばれるこの機能にバインドすることができ、あなたの店をセットアップします。

const authEntry = checkAuth.bind(null, store) 

もう一つの方法は、関数内のルート定義をラップし、その中に店を通過し、その後、あなただけの同じアクセス権を持っているだろうことであろうが、私はそれはのようにきれいではないことが判明し、この(個人的な好みとして)。

今このcheckAuthは何をしますか?このような

何か:彼らは中央の場所で再利用可能なコードを提供するよう

export function checkAuth (store, previous, next, replace, callback) { 
    const currentUser = store.getState().auth.user 

    // can possibly dispatch actions from here too 
    // store.dispatch(..).then(() => callback()).. 
    // so you could possibly refresh the token here using an API call 
    // if it is about to expire 

    // you can also check if the token did actually expire and/or 
    // there's no logged in user trying to access the route, so you can redirect 

    if (!currentUser || !isLoggedIn(currentUser)) { 
     replace('/yourLoginRouteHere') 
    } 

    callback() // pass it along 
} 

これらの両方が十分に汎用的でなければなりません。これらが役立つことを願っています。

+0

他のアクションがディスパッチされた場合、どのようにこのミドルウェアを更新しないようにしますか?ネットワーク関連ではないものや、認証トークンを必要としないその他のリクエスト –

関連する問題