2016-06-28 9 views
6

Mam aplikację Redux i zdalny interfejs API, który służy jako serwer OAuth. W oparciu o typową rutynę użytkownik wymienia swoje poświadczenia na token, który jest następnie używany przez aplikację do pobierania danych i robienia pewnych rzeczy na serwerze. Ten token jest przechowywany w sklepie, a także w sessionStorage.Strategia udostępniania tokenu dostępu OAuth w aplikacji Redux

Czasami żeton dostępu wygasł, ale od czasu otrzymania tokena odświeżania lepiej jest najpierw odświeżyć i tylko wtedy, gdy coś pójdzie nie tak, wyloguj użytkownika.

Całkowicie rozumiem część dotyczącą podpisania, co technicznie oznacza po prostu wysłanie określonego działania. Ale jak mogę uprościć rutynową procedurę odświeżania tokenu?

Próbowałem redux-saga, ale jest bardzo gadatliwa. Dosłownie muszę częściowo zduplikować kod dla każdej akcji, która zależy od zdalnego interfejsu API, aby upewnić się, że każde takie żądanie sprawdzi najpierw token dostępu i czy jeszcze nie wygasło, i uda się go odświeżyć w inny sposób.

Kolejną rzeczą, którą próbowałem zrobić, to oprogramowanie pośrednie, które wymagałoby pewnego rodzaju akcji z prośbą o zdalne API zawinięte w obietnicę. Tego rodzaju prace, ale jestem ciekawy, czy jest inny sposób.

Czy ktoś kiedykolwiek wdrożył ten (dość rodzajowy) rodzaj rzeczy? Jakieś pomysły, jak zautomatyzować odświeżanie tokena i nie denerwować się zwiększoną ilością kodu? Może komponent wyższego rzędu?

+1

Mimo, że istnieje odpowiedź oznaczająca rozwiązanie, każdy jest bardzo mile widziany, aby dodać swoje przemyślenia i doświadczenia. –

Odpowiedz

9

Dla kodu, który musi się zdarzać wielokrotnie i dla czegoś, co musi być płynne i ogólne, middleware są zwykle drogą do zrobienia. Może to być tak proste, jak dodanie dwóch linii kodu, aby uwzględnić oprogramowanie pośrednie podczas tworzenia sklepu i napisanie prostej funkcji, która zajmie się logiką tokena.

Powiedzmy byś stworzyć swój sklep jako takie:

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

Wtedy można by nazwać tę funkcję skądś, z początkowego stanu.

const store = finalCreateStore(rootReducer, initialState); 

Pozwoli to zrobić coś ze wszystkimi działaniami, które przechodzą przez sklep. Ponieważ bardzo popularne jest oprogramowanie pośrednie, które obsługuje zgłoszenia API za pomocą obietnic, niektórzy ludzie wolą ponownie je wykorzystać w tym celu i połączyć je razem.

Typowa middleware będzie wyglądać mniej więcej tak:

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

Twój token odświeżania thunk może być coś podobnego do tego:

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, 
     })); 
}; 

Inną alternatywą, które można ewentualnie przejść dla byłoby obsługiwać to przy zmianie trasy.

Załóżmy, że masz gdzieś trasę najwyższego poziomu dla tras, które wymagają uwierzytelnienia, i ważnego użytkownika do obecności w systemie. Nazwijmy je authenticated routes.

Można zawinąć te authenticated routes trasą najwyższego poziomu, która definiuje funkcję obsługi funkcji onChange.Coś takiego:

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

Podczas tworzenia tych tras i utworzenie sklepu, po utworzeniu sklepu, można powiązać go z tej funkcji o nazwie checkAuth.

const authEntry = checkAuth.bind(null, store) 

Innym sposobem byłoby owinąć definicje trasy w funkcji i przekazać sklep do niego, a wtedy masz dostęp tylko samo, ale okazało się, że nie może być tak czyste jak ten (osobistych preferencji).

Co teraz zrobi ten checkAuth?

coś takiego:

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 
} 

Obie te powinny być wystarczająco rodzajowy, tak by zapewnić Państwu wielokrotnego użytku kodu w centralnej lokalizacji. Mam nadzieję, że znajdziesz te pomocne.

+0

w jaki sposób sprawić, aby to oprogramowanie pośrednie nie odświeżyło tokena, jeśli zostanie wywołana jakaś inna akcja? powiedzmy coś, co nie jest związane z siecią lub inne żądania, które nie wymagają tokena autoryzacji –