Tips to handle Authentication in Redux #2 introducing redux-saga
After about 5 month from my first article Tips to handle Authentication in Redux, I think it’s time to share with you all how the actual redux ecosystem changed and improved the way I usually do authentication in redux.
One of the libraries that most improved my approach with redux apps is redux-saga, that allows to decouple any side effect from the actual reducer.
Modeling the store
When talking about authentication our store must know about two things: who are the users? which user is currently logged in? These are the main questions that will lead us choosing the right shape for our store. So we will have a property containing all the users indexed by id, and an auth property containing the currently logged user id.
This way, getting the currently logged user object could be easily done via any redux state selector.
These selectors can then be used in your connect’s mapStateToProps to help you getting the current logged user. Also, they provide more flexibility, because if you change your store shape you can simply change that small selector function instead of changing your whole app components. tip: you could also memoize them :D
Wait, anyone can easily change the logged user id!
Hey, we are doing frontend development here! YOU SHOULD NEVER TRUST DATA INCOMING FROM THE CLIENT! So, whenever performing any AJAX request from the UI, you should never pass in the user id.
Instead, as you can see in the store shape example, we will store the access token in the user object, and whenever performing any ajax request, pass that access token as parameter. This way our app could also support multiple users login and offline data insertion.
Getting started with the action creators
Now we will create action creators for the various login actions. As you can see, ducks may come in handy.
Just to describe a little the action creators, loginSubmit will take in {email, password}, loginRequest will take {email, password}, loginSuccess will take in a user object (including id and token), and login error any error structure you will use (take a look at redux-form! :D).
The users and auth reducer
Now it’s time to perform the real auth logic! Whenever a loginSuccess is dispatched, we will store the user and set the logged user id.
To provide a cleaner syntax, I will be using the object spread operator to append the just logged user to the users stack. More information about it can be found in the redux documentation.
Wait, where do I perform the REAL login request?
That’s where saga comes in handy! Sagas can be considered as daemon process that listens for dispatched actions and can dispatch actions whenever they want to. They are implemented using generators, which allows a great DX understanding and writing them.
Atm I am not so good with them, but I think you should have a deep dive into redux-saga docs, which provides great real world examples to dive in.
As you can see, the saga is a generator, which should be injected with createSagaMiddleware from redux-saga.
The real login request is done in the login() function imported from the api.js file, I’ll let you do that, but it returns a promise that resolves with the logged user object (with token included) or rejects with an error object. Any rest library like superagent can help you implement that.
In the saga I also dispatch startSubmit and stopSubmit from the awesome redux-form package, in order to provide a visual feedback about the outgoing rest request.
Follow-up: performing API requests with the access token
As said before, any rest request should also pass in the access token in order to identify the currently logged user. How can I do that? sagas come for the rescue. Using a similar pattern to the one used to perform the loginRequest and then dispach its response with loginSuccess or loginError, you can implement a saga that listens for a generic restRequest action being dispatched, perform the http request and dispatch restSuccess or restError based on the request response. This way you can access the stored access token and automatically append it to any rest request, and also this makes your code more testable, as you are separating concerns of side-effects.