Blog>
Snippets

Handling Authentication Flows with Middleware

Outline a scenario where middleware intercepts an action indicating an expired token, and triggers a Saga to handle refreshing the token and retrying the original action.
const checkTokenExpirationMiddleware = store => next => action => {
  if (action.type === 'API_CALL_REQUEST') {
    // Assume getState() returns something like { auth: { tokenExpiresAt: 1597534890 } }
    const { auth: { tokenExpiresAt } } = store.getState();
    const currentTime = Date.now() / 1000; // current time in seconds
    if (tokenExpiresAt <= currentTime) {
      // Dispatch an action to refresh the token
      store.dispatch({ type: 'REFRESH_TOKEN_REQUEST' });
    }
  }
  return next(action);
};
This middleware intercepts every action to check if the request is an API call that requires validation of the token's expiration time. If the token has expired (or is expiring), it dispatches an action to refresh the token before proceeding with the original API call.
function* refreshTokenSaga() {
  try {
    const response = yield call(refreshTokenApiCall); // refreshTokenApiCall is an example API call to refresh the token
    const newToken = response.data;
    yield put({ type: 'REFRESH_TOKEN_SUCCESS', newToken });
    // Optionally, re-dispatch the original action or a specific API call now that the token has been refreshed
  } catch (error) {
    yield put({ type: 'REFRESH_TOKEN_FAILURE', error });
  }
}
This saga listens for REFRESH_TOKEN_REQUEST actions to handle the token refresh logic. On a successful token refresh, it triggers a REFRESH_TOKEN_SUCCESS action, potentially with the new token. On failure, it triggers a REFRESH_TOKEN_FAILURE.
function* watchRefreshToken() {
  yield takeLatest('REFRESH_TOKEN_REQUEST', refreshTokenSaga);
}
This saga watcher listens for REFRESH_TOKEN_REQUEST actions and triggers refreshTokenSaga. Using takeLatest ensures that if multiple refresh requests are made in quick succession, only the last one is processed.
const sagaMiddleware = createSagaMiddleware();
const middleware = [checkTokenExpirationMiddleware, sagaMiddleware];
const store = createStore(
  rootReducer,
  applyMiddleware(...middleware)
);
sagaMiddleware.run(watchRefreshToken);
This code snippet demonstrates how to configure the Redux store with both the custom middleware for checking token expiration and the saga middleware. It also shows how to run the saga that handles token refresh.