Question

Almost every blog post I've encountered around generic auth handling using JWTs in a React/Redux/Saga application does the same thing, which is to store the JWT in local storage, in the action/saga.

For example:

Example A (redux-saga):

function* authorize({ payload: { login, password } }) {
  const options = {
    body: JSON.stringify({ login, password }),
    method: 'POST',
    headers: { 'Content-Type': 'application/json' }
  };

  try {
    const { token } = yield call(fetchJSON, '/login', options);
    yield put({ type: AUTH_SUCCESS, payload: token });
    localStorage.setItem('token', token);
  } catch (error) {
    let message;
    switch (error.status) {
      case 500: message = 'Internal Server Error'; break;
      case 401: message = 'Invalid credentials'; break;
      default: message = 'Something went wrong';
    }
    yield put({ type: AUTH_FAILURE, payload: message });
    localStorage.removeItem('token');
  }
}

function* Saga() {
  yield takeLatest(AUTH_REQUEST, authorize);
}

Example B (redux):

function login(username, password) {
    const requestOptions = {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ username, password })
    };

    return fetch(`${config.apiUrl}/users/authenticate`, requestOptions)
        .then(handleResponse)
        .then(user => {
            // login successful if there's a jwt token in the response
            if (user.token) {
                // store user details and jwt token in local storage to keep user logged in between page refreshes
                localStorage.setItem('user', JSON.stringify(user));
            }

            return user;
        });
}

function logout() {
    // remove user from local storage to log user out
    localStorage.removeItem('user');
}

Now - storing JWTs in local storage aside - what concerns me - is the setting of state inside of the action creator/saga - rather than in a reducer.

My understanding is that action creators/sagas should be used just to determine the data to be used - and it is up to the reducers to actual store/persist the data.

Now of course - at its initial state, redux won't persist data. However, you can use a redux middleware like redux-persist to persist it to local storage (and there are other redux-persist middlewares to persist data to other places). Point here is - redux is the state management.

Why then - does everybody seem to just sidestep using redux as your application state when it comes to authentication?

No correct solution

OTHER TIPS

I will do a guess from different sources.

  1. I heard "security is not a crosscutting concern" I would put authentication in it. It can not be separated from the program although not included in the model and then pressed in when needed.
  2. Whether you are authenticated is not part of your model in a way that you have two objects, one authenticated and one not authenticated at the same time.

Thus, you try to push authentication as far away from the other logic. You put it in a layer between communication and model. That layer is so thin that you do not give extra space to it in an example as this layer may not evolve or evolve in unforeseen ways in programs building on the example. Besides, there are only two places to handle that use case.

An other one is that in an example, you like to go for simplicity instead of perfection.

PS: An answer like this usually leads to better answers coming. Good luck!

For me the main reason to store the token in local storage instead of redux store is that they are fundamentally different in nature in that:

  • You usually want too keep the auth token between page refreshes
  • You may want or not to do that with the application state

Aside from that, setting the state in the action creator seems to me just wrong, since the sole responsibility of those should be returning the action.

Setting local storage is by definition a side effect so it seems to me that the logical place is in your side effects handler of choice - in many cases the sagas.

Licensed under: CC-BY-SA with attribution
scroll top