Sign Up & Sign In with Rails/React/Redux/Hooks-Part 2
User Authentication with JWT — JSON Web Tokens
Last week, in Part 1, I covered the first half of this series with building frontend and backend User Sign Up functionality. This article will cover the second half: User Sign In ,User Auto-Login & User Sign Out.
Ruby on Rails Backend / Server
In regards to the backend, I am going to follow the same steps as last weeks article and past my three backend controllers quickly here so that we can focus on the frontend components and attribute the code much to Reinald Reynoso’s great blog that started me down this path.
React.js Frontend / Client
Since we have already done the SignUp component, lets jump right in to the SignIn component. All of our components will be functional components so that we can not only get more experience with React Hooks, but also can learn Redux Hooks for state.
THE SIGNIN COMPONENT
Your Sign In form can take in as many fields as you’d like; you’ll just need to update you <input> fields accordingly. Although my Sign Up form asked for more information, my Sign In form only asks for input of two fields and then validates them on the backend in our Auth Controllers ‘login” method: authenticating that the email matches against the password.
You can see the Auth Controller above, with the login method, and here is the frontend Sign In form:
The code above is very similar to the Sign Up form from last week, where we import useState for working with state in a functional component, and useSelector and useDispatch to work with Redux (and might I say how much easier it is than working with mapStateToProps and mapDispatchToProps in a class component). The main difference in this component, aside from the input fields of the form, is that now we wrap our new action signInUser (line 34) as we send our user to this action.
ACTIONS CONSTANTS FILE
Before we go any further, I’d like to stop and make a quick code refactoring. In last weeks blog post, I had originally designed it so that my exported action constants (ex: export const POST_USER = “POST_USER) were directly in my action components. Because we are going to keep making more of these constants, I think it would be a great idea to start their own file, so we only have to import them one time in our reducer from one file and we can even refactor one of our constants so that other action files can use them, if necessary(like POST_USER). Because the first action in all of our action functions (again, in the signup user case this was called POST_USER) is to simply let the reducer know that we are loading the information and to set the value of isLoading to true, we can change the name of that function to something else, and use it in the remaining action files we build, making our reducer a bit more DRY. This file will look like this and, so far, contain our Sign Up and Sign In constants and our renamed LOADING_USER, which will get used in all 3 action files we build in total:
THE SIGN IN ACTION FILE
Again, this is very similar to the Sign Up action, with the differences being we are sending a POST method to the login method in the Auth Controller, so that the method will go find the user, authenticate it and then follow the necessary steps to create and encode a token and send it back to the front end along with the user object. After the promise of the fetch has been fulfilled, 1 of 3 things can happen, we have success and we send the logged in user to the reducer as the payload, we have errors of why the login wasn’t successful, or we have an outright failure that is caught (usually in the instance that something is actually causing an error in direct relation to our server.
THE REDUCER
We will use the same exact reducer for all of our actions, so let’s update our imports, update the one constant we changed and add our next 3 cases:
AUTO-LOGGING IN A USER
Both our User Sign Up and Sign In functionality that we have built so far, will automatically ‘log’ us in and send us to the component that we specify, but what happens if we have a page refresh, etc, and oh, no! we have lost our user…the user data and login info didn’t persist, so our program has completely forgotten that we had a logged in user. We don’t want our user to have to login again, super inconvenient and no one wants that kind of experience on a site.
There are different ways that developers handle this scenario. I recently learned that some developers continually send their users token with every api request they make to ensure the user on the front end is the user we have logged in on the backend, but I would wonder, myself, if this is secure? I mean, I truly understand that this whole process with JWT’s is in and of itself a security risk unless massive implementation of security and auth and embedding happens around JWT’s at all. I am going to develop this the way I have learned, but am very open to better ways of doing this step, just lmk.
In my application, I put call to action of getting the user directly in my App.js file, so that I can wrap the rest of program in this logged in user and, unless something goes wrong, or a refresh happens, we only have to make one API call to the backend to get/authorize the logged in user/user token.
THE GET USER ACTION FILE
Our getUser.js action file will now be built like this: Please note that although this is a GET request to the ‘auto_login’ method in our Auth Controller, we are sending along a header of : Authorization: `Bearer ${token}`
THE UPDATED REDUCER
Here is our newly update reducer with the 3 new action constants that we used in our getUser action:
Just yesterday, I had the opportunity to use Ruby/React/React Hooks/Redux/Redux Hooks in an interview scenario. I felt incredibly empowered that I mostly knew what I was doing, and even more so, relieved, because React Hooks and Redux Hooks seem to make the entire process so much more easy and clean.
Please let me know if I left anything out here or if you have any questions. Have a great upcoming week!