Login Authentication in React.js with Redux | MERN Stack Project

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
a react login form and login authentication powered by redux and rtk query is the next goal for our mernstack project [Music] hello and welcome i'm dave today we will build a react login form that uses rtk query hooks and dispatches redux actions i'll provide links to example source code and all resources in the description below our starter code is the completed source code from lesson 7 which is the last time we worked on the front end app i also have the code from lesson 8 running the back end rest api that you see here and this is a separate instance of vs code so we can try out the login code when it's complete i'm going to minimize this now and we're back at the front end code the only change i'm going to make in the package json to start is to change this lesson 07 to lesson 09 dash front end now let's look at the user stories we have completed number two and number four in previous lessons and we also completed number 10 through 13. so let's check those off i'll put an x in each one of those and then we'll look at what we might complete today however many of the goals that we still have are waiting for the roles and permissions to be applied to the front end app after we have the authorization from the server so what we can complete today along with the login that we of course expect to provide is to have a log out option so we'll definitely be able to check off number seven the rest of these may be waiting on another domino to fall in place before we can actually check them off so let's save the user stories with number 2 4 and 10 through 13 checked off and we'll move on let's go to the source directory in our code and then let's go to the features directory and finally to the auth directory and we have a lot to add here today so let's create an auth slice to start out i'm going to click new file and i'm going to create auth and then with a capital slice.js notice this is not an api slice but more of a traditional slice like we would have just with redux now i'm going to import create slice from redux js toolkit and after that we'll create our auth slice so i'll say const off slice set this equal to create slice and there should be an object inside the parentheses and now i'll put in the first two properties of the object so we'll name the slice auth and then our initial state will have an object that has a token property and will be set to null because we'll be expecting to receive the token back from our api then we're going to have reducers and reducers are an object as well and i'm going to quickly insert the two reducers we're creating and then we'll go over those so the first one is set credentials after we get some data back from the api we're going to have a payload and that's going to contain the access token then we're just going to set the state dot token to access token and that's because we're already inside of the auth slice here with the name auth so we don't have to set state.auth.token we know we're in that so then we also have a log out reducer here and that's just going to set the state.token to null at logout time and now we need to export these things so just underneath i'm going to export set credentials and log out they're both the action creators here from the auth slice dot actions so those are exported we'll also export the auth slice dot reducer itself so all the reducers because we need to add that to the store and then we're creating one selector that's select current token notice here it does refer to state dot auth.token and remember auth is here because it is the name of the slice we created above and now that we've created our slice we need to go to the store that's inside of our app directory so there's storage a s and we need to add it to our store so first we need to import that so we will import auth reducer and that is going to come from and then we should go up out of this directory and then we're going to look inside of the features directory and then inside of features we have auth and inside of auth whoops go back we should have a slash there then we should be looking in the auth slice now once we have imported that auth reducer we should be able to put it inside of the reducer for the store here where we have the api slice of course supplied as well so now we'll put in auth and have our auth reducer now let's go back to the auth directory and as you might guess we do need an api slice as well so let's now create that so we'll have auth and then capital a for api capital s for slice dot js this shouldn't be a new concept as we created separate slices for the notes and the users as well and we extended the api slice and that's what we'll do here let's start with our imports first we're going to import the api slice so we can extend it we're also going to import the log out function or log out reducer that we created inside of the auth slice after that let's go ahead and say export const and we'll have our auth api slice we'll set this equal to api slice that we imported dot inject endpoints and then we'll have an object inside of that and now that we're injecting endpoints we should define those endpoints so we'll start here with that and then we'll pass in the builder and that's an arrow function and there will be an object inside of this where we can put each endpoint we'll start with our login endpoint and we'll quickly break this down so this is the login we call builder.mutation this will be a mutation and now we define the query inside of the mutation we're passing in what we're calling credentials this would be the username and password that we send with the query then we'll have this sent to the slash authroute and this will be a post method and here we'll spread in the object that we expect to receive as credentials into the body object and while that endpoint is fairly straightforward the next one has a little more to it so i'll paste this in and we'll break it down so we've got our login endpoint above and now we have a send logout notice i didn't name it logout because we're importing our logout reducer from authslice we have a send logout and this is also a builder mutation and we define the query first this goes to our log out route that we defined on the back end so it's slash auth slash logout and the method will be post which is expected after that it gets just a little more complicated so let's go over what we have here rtk query provides an onquery started function that we can call inside of our endpoint now what this does and it's async it accepts an argument that we're not really defining but it needs it here as the first parameter but then it also provides things like dispatch and query fulfilled so we can verify our query has been fulfilled and because we're putting async here we can await that so i'm putting a try catch inside of our onquery started function and we're awaiting the query fulfilled now notice i have this commented out because you could set const data equals query fulfilled it returns a data property and then you could log that data and you'll get the message from the rest api that we created that says cookie cleared and that's what you should get as a response so if you want to get that go ahead and set the const data and destructure it from query fulfilled i just wanted to put that in there as an option and then of course you can view it with a console log after that we're going to dispatch our logout reducer that we imported from the auth slice this will set our token to null in our local state and we need that as well so we've logged out on the server we're setting that token to null and then the api slice which is separate from the auth slice and this is going to need to be cleared as well and so we can call api slice because we imported it up here at the top and then we go dot util dot reset api state and that is a method that can be called and it will clear out the cache and the query subscriptions and everything to do with our api slice so that also needs to be taken care of at logout now this doing all of this inside of onquery started keeps us from needing to import the dispatch or i should say use dispatch into a component and then dispatching each one of these in every component so we can put it here and then we can just call this log out in point the mutation that we would import into the component and it will take care of everything so this is a more efficient way of doing that if there is an error we're just going to log it to the console there really shouldn't be when you log out but if there is a logout error this is what we'll do here and now i'm going to scroll for some more room and we'll add our third and final endpoint and it is just the refresh endpoint it is also a mutation and we're just defining the query which goes to auth refresh and it's a get method because essentially we're just sending a get request that includes the cookie when we send it and it will hit that refresh endpoint so we could get a new access token when needed now just like with our other slices what we need to do is export all of the mutations that we have now created so you can see we have export const and we're exporting use login mutation use send logout mutation and use refresh mutation and with that complete we are ready to work on the login component that will use some of these things and you can see we've already started the login we just put in a placeholder functional component let's start at the top by putting in some basic imports and we'll break these down so we've got use ref use state and use effect from react we also have use navigate and link from react router dom but that's not all we need we also need some things from redux and rtk query so we've got use dispatch from react redux set credentials from the oslice we created and then the use login mutation in our auth api slice now let's start at the top of the component and we'll bring in some of these basic hooks that we've imported from react so we're creating a user ref that we will use to set the focus on the user input and error ref that we'll use to set the focus if there is an error and then we've also got state for the username the password and a possible error message after the state i'm going to scroll up just a little bit and we'll go ahead and use our use navigate hook to bring in the navigate function use the use dispatch hook to bring in the dispatch function and then we've got our use login mutation hook and we'll bring in a login function that we can call when we need it it's also going to have an is loading state that we're going to use mutations do provide other states but we will not need it in this component now let's go ahead and define an error class and then also check that is loading state so we're defining an error class first and if we have an error message in our state for error message then we're going to apply the class error message this is a ternary statement otherwise we're going to apply the class off-screen so we'll see where this is used as we create our form also we have this is loading state that we are checking from when the mutation is called so this is where it's actually right here i scrolled up too far this is where we're using the mutation when we call login it will have an is loading state and if it is loading we'll just return this simple loading paragraph you could use a loading spinner if you have one that you like to use and now let's define our content so i'll say const content and it will be on more than one line so i'm going to provide parentheses as well and then here instead of this placeholder h1 login i'm just going to return the content as well now we'll put our content inside of these parentheses i'm going to scroll up just so we have some more room and i'll paste in the first part and of course notice it gives us a closing main and we're starting out a section here with a class name of public remember these class names align with my css if you've provided your own you could do something different here we have a header that says employee login now remember this is not inside of our protected routes where we have a dash with the dash header and the dash footer this is a separate public page much like the first page that provides some information about dande's business so right here we're just going to have to provide our own header and footer for the content of this page let's go ahead and close everything out after that closing main tag so we're going to have a footer as well that's just going to link back to the first page of the website this is also a public page and then we close out the section now inside of the main element is where we're going to put our form so i'll start the form here but not add everything yet we could add the closing form tag just so it lines up and doesn't show that error for us but inside of this let me go ahead and save and it should indent there we are we'll start out with a label and this label is for the username and notice it has an html4 that should align with the id attribute of the input for user name also notice the class name for form is form which lines up with more css in my css and then it has an on submit handler called handle submit that we have yet to define so we'll come back to that other than that you can look at the different attributes for this text type input notice we are applying the ref we created earlier the user ref and then we're applying the state with a value so this is a controlled input and then the onchange also has a handler that is handled user input that we have not defined yet we want auto complete to off because we don't want to show any other possible usernames that have been entered and we do want to make this required now let's provide a blank line underneath and we'll put in the information for the password as well very similar to the username input you can see the html4 is password that lines up with the id password the type is password so it doesn't show the entry it also has a handler that we have to create yet called handle password input it's a controlled input with the password state and it is required and then when you only provide one button inside of a form by default it is the submit button i'm going to press ctrl b just so we can see this a little better but we're providing a button with class name here and that matches up with my css once again but we don't have to say that this is a type submit by default it already is and then we're just putting sign in on the button itself now let's go back and provide those handlers and a couple of other things that we did not provide at the top of the component one of the things we did not provide was how to handle the refs that we've put on both the username and password input so i'm going to put in the use effects that handle those or at least handles the first one for user ref and what we've got here is userref.current.focus and this is an empty dependency array and use effect so it only happens when the component loads and it puts the focus directly in that username field the second use effect is to clear out the error message state when the username or password state changes so our user may have already read the error that has appeared and then when they once again type in the username or password field it will clear out the error that is being displayed because they've already processed the error there's no longer a reason to display that error and speaking of displaying errors i believe that's the one thing i currently left out of our main area so let's put that error in right above the form and here we'll be able to see how it is applied so we have the error ref on the paragraph which we will use in just a moment we also have that class name error class that we defined above and then there is an aria live attribute and it says assertive that means when this gets focused it will read like a screen reader would read the error message that appears and that's also important to do now let's create those handlers for the username and for the password fields and for the form itself i'm going to start underneath the use effects we've applied and then i'll put in the first two handlers we've got one for handle password input and one for handle user input and you can see they're very simple they receive the event itself and then they set user name to event.target.value or set password to event.target.value the handle submit for the form is just a little more complex so let's start building that and i'll put the closing curly brace there we're going to start out by just having this be an async function and it receives the event and then we're saying event dot prevent default that's the first thing you need to do when you submit a form really because otherwise the default is to reload the page and you don't want that to happen inside of your single page application and now we're going to need a try catch block so i'll start with the try then we'll add the catch here after and i'll receive an error in the catch let's put in our try block contents first i'll paste this in and go over it we're going to get our access token back after we call the login mutation function this is what we receive this login function from our use login mutation hook we can await that result we pass in the username and password state when the username and password are complete then we call unwrap at the end because i'm not using the error state i actually want to use this in a try catch block and that's what we do in redux if we want to use the try catch block instead of using the rtk query states such as is error so i'm doing that to catch the error here instead we're also going to dispatch set credentials which we mentioned earlier and we receive an access token back and that's going to be our credentials so we set that state.token then we have set user name and set password just being set to blank and that's great because we just want to empty out that state and then we have navigate that will take us to the dash after we've logged in so this is all if it's all successful and of course nothing happens until we've awaited this login if we have an error there it will go directly down to our catch error so these things won't happen unless we are successful now the catch block might actually be considered a little more complex than what we just went over for the try but let's look at it it's if we have an error status or if we do not have an error status i should say here's the exclamation mark saying if we do not have an error status we're going to just say there was no server response and that's the only time we shouldn't have an error status if we have an error otherwise we're going to check the status and if it's a 400 we know that should be missing a username or password 400 usually means bad request and that would be the case for a login 401 would be unauthorized or we can just go ahead and set the message and remember it won't just be error.message here we'll be receiving error.data.message and i'm using optional chaining here just as a precaution after that we have our error ref and we can set that errorref.current.focus so the focus is then set on our error message which would be read by a screen reader as well because we put aria live attribute set to assertive and one last look at that handle submit and i believe it is completed yes so now we have finished our login component let's go back to the file tree before we test this out and let's go up to our components directory and go to that dash header because once we've logged in we also need to provide a log out we're currently only importing link from react router dom but that is about to change so i'll paste in the imports and we can look at those we're going to have use effect from react then we're also going to use a font awesome icon and then we import that very icon that we need which is fa right from bracket it kind of indicates a log out it's often used for a log out and then there is use navigate link as we had before and use location coming from react router dom but that's not all we also need to import our use send logout mutation from our auth api slice and before we move into the component i'm going to import some cons that we are creating here and these are regex constants so i have a dash regex a notes regex and a user's regex we're going to use these to compare to the location in the url to verify what location we are on or not on and we can use those to decide if we want to display something such as a button in our header or not and now i'm going to scroll up a little bit and we can start inside the component that we already have for dash header and i'm going to import some of these hooks so we're going to get the navigate function from use navigate we're going to destructure the path name from the use location hook and now we're going to get the send logout function from our use send logout mutation hook we're also going to get several things about the status when we call the function is loading is success is error and error much like you've seen in other calls to rtk query hooks i'll scroll up just a little bit and underneath the call to that hook we're going to put in a use effect and use effect is going to check the is success status and of course we need to put in the navigate function just to appease the warnings that we might get inside of the console even though we know that the navigate function won't change so if is success then we're going to just navigate to the root and this is because it's a log out so we would go back to the root of the site other than that we have an on logout clicked handler that is going to call that send logout function that we're getting here from use send logout mutation and underneath that i went ahead and put in the if is loading we're just going to return the paragraph that says logging out and if the is error status is true we're going to return an error message here that we're going to get from our hook and as i look at this i realize i need error dot data and then i'm using optional chaining again for the message underneath the error i'm going to define a class for the dash and this could be done with a tern area as well but i just think it's easier to understand this way so i did it like this because it's kind of long i'm going to press ctrl b to hide the file tree so you can see it all as well i'm defining this dash class with let here and just setting it to null but then we're also checking to see if the tests here for the different reg x's we're essentially making sure that we're not on the dash path itself the root and we're not on the notes list or the users list we do not want to be on any of those pages and if we're not we will set this dash class to a dash dash header two underscores container dash dash small i'm using a bem naming convention here if you're wondering why my class name is so long for this class that i have inside of the css but anyway quickly defining that class and you could do it with the ternary if you want to but this is how i'm doing it just because it's long it's a little easier to understand this way i think now let's go ahead and put in our log out button that we will see in the header we're going to define it first so it is a button has a class name of icon dash button the title is log out and we're going to call our on log out clicked handler which i believe we already defined up here it essentially just calls send logout that we received before and now let's scroll down to see the rest of our content we have already defined content what we haven't done is put that dash class that we created inside of our dash so let's go ahead and add that it's going to be on this div that we have that has a class name of dash dash header two underscores and container but it's a modifier the first thing we need to do is go ahead and put all of this inside of curly braces here and so we'll do that first and then we'll turn this into a template literal so i'll select the first double quote and press ctrl d to select the second one and press back tick instead so now it is a template literal and now we can insert a variable and so now i have dollar sign curly brace i'm going to insert my dash class variable and this way we can add this extra dash class if it does exist and otherwise i'm just going to put the log out button below and here instead of saying add nav buttons later i'm just going to say we're going to add more buttons later because we absolutely will add buttons to our header depending on the path name in the near future once we have the different permissions worked out with our logout now added to the header let's go ahead and press ctrl backtick and start up our react app now you should have already started your back end as well as i showed mine was running from the beginning of this tutorial that is the code we completed in lesson eight which was the back end here we're in lesson nine and we're starting the front end and we can check our changes so let's see what we get once the react app starts up we have successfully started let's go to the login and here is the login page i'm going to log in with dan d and the password i put for him he is our main stakeholder the owner of the business and he should have an admin role this is worth noting as well if you did not already create those test users or a user for dandy before you added the verify jwt middleware in the last lesson then it won't let you create those new users so you need to create one with postman before you've added that verify jwt middleware and hopefully you've done that in the past as i displayed that i had just noting that okay i can log in now with dandy and i'm here on the home page notice we have this log out button in the top right of our header and we have the different pages we can go to now we do not have a jwt being provided when we make a request we should have received one and we can check that in our redux state component but let's look at what happens if we try to view the tech notes we're not sending that access token back to the server so it says we're unauthorized the same if we try to view the user settings right now if we go to add new user we shouldn't have a problem getting to this page because it does not require any new data or any i should say any existing data from the api we're not making a request to go to this page so we can get there if we go back to home we're going to have a problem when we try to add a new tech note so we do that and we've got nothing and if i expand this over to full screen we should be able to open up the console and we see some errors here as well so we need to fix this not because it will be much of an issue in the future but i don't like to leave this hanging as it is so let's go back and we'll refresh here on the dash and we should get our dash page back but what we need to fix is in the new note i'm going to drag this over and we'll drag visual studio code back and we can see what's going on here i'll close out the terminal we can leave it running in the file tree we should go down to features and then notes and we should go to our new note component the very first one and here's where the issue lies we're expecting the users to go into that new note form because we need to know what users exist so we can assign that new note to an existing user there's a couple of things we can change here first of all this use selector is calling select all users which has been renamed just from a select all and that select all memoized query always provides an array so even if we just check users here we're going to have users it's just an empty array so this isn't going to work exactly as we expect instead let's check to see if that array has length and we can do that right here and i'll paste that in and then we're just going to return a simple statement that says not currently available after that we can shorten up the next line and i'll just highlight over this one and paste this in and we'll just put our content equal to the new note form where users is passed in and return the content so essentially if we don't have users for any reason or we don't have any length of users we're just going to say that that form is not currently available so once we fix this we should be able to go back now and well i don't really need to change that i'll put over here to the left let's go back and once again we can just refresh to make sure we've got those changes and try to add a new tech note and now we get the not currently available and that's because we do not currently have access to that data so instead of unauthorized we're just putting that message in now when we are authorized or when we've protected those routes then another message will occur so we won't have an issue there i just wanted to temporarily fix it for now and it's nice to have everything at least responding as expected without an error at this point now i'm going to drag the app to a full screen and i'm going to open up devtools and inside of devtools we can clear out the console but let's go to redux and we want to check our state here and actually our redux dev tools if you have them installed it likes to have a little more room so i'll move this over i don't need it quite that much there we go i just want the state down here below and we're looking at the tree view and this has our off state we're logged in or we were logged in and we have a token null now let's go ahead and refresh this and yes we should have a token all after the refresh we don't have in our protected routes yet so we're not getting kicked back out so let's go ahead and hit the log out that kicks us out to the beginning we'll do the employee login once again and i'll do dan d i'll put in his password and log in and now we can see our auth state here has a token and we have our token inside our api has also made some requests and it has subscriptions as well so we can see the get notes and get users although back in the console we see those requests are replied to with a 401 unauthorized because we're not providing that token when we're making those requests we're also seeing these subscribing and unsubscribing notes that we have inside of our pre-fetch component that we created in the previous lesson 7 tutorial we're in lesson 9 right now okay so that's what we have in the console and our state is showing here in redux what we want to test now and we're not providing that token yet when we do those requests but what we want to test is our logout to make sure that the subscriptions and the queries all go away that it resets our api state and the token is set to null so let's hit that log out and now you want to select your last choice here inside of where your state was and it will show the different requests up here at the top i know the window's a little scrunched here is our log out the token is null we currently still have the subscriptions but then when we go to rest api state or reset api state i should say the subscriptions are cleared out the queries are cleared out and everything is as expected and we can scroll on down to get to the very end and we can see the same thing the token is null this is the current state we're in and our api has been cleared out so our logout is working as expected and as i had mentioned before when we log in we're currently not able to see those lists that we had so let's log back in here's the console we can clear this out and see what happens in the console as we log in now so put dandy in and once we get logged in we see the subscribing unsubscribing when the component unmounts and that's the strict mode from react 18 and then subscribing once again so that's all in our prefetch component and then we have a couple of 401s when we request the notes and the users that's unauthorized so our prefetch didn't really work they were both unauthorized requests and now of course when we go to view the tech notes we are still unauthorized and you can see that request here so we can go back same for users and we're unauthorized and now the changes we made to the tech note page gives us the not currently available message which is fine we can go to the add new user form although if we tried to add a new user we would once again be unauthorized now in the very next tutorial coming up we're going to learn how to provide that token with the request and of course if that access token is expired we're also going to learn how to automatically supply the refresh token and make that request to get a new access token we're going to do that all with redux and rtk query in the very next lesson remember to keep striving for progress over perfection and a little progress every day will go a very long way please give this video a like if it's helped you and thank you for watching and subscribing you're helping my channel grow have a great day and let's write more code together very soon
Info
Channel: Dave Gray
Views: 20,271
Rating: undefined out of 5
Keywords: login authentication in react js, login authentication in react js using redux, login authentication in react js using api, login authentication in react js jwt, login authentication in react js useing redux, login, authentication, auth, login auth, login authentication, react login, react, react.js, redux, mern, mern stack, mern stack project, react authentication, react login form, redux login, redux auth, redux authentication, react auth, react redux login, react redux, js, form, api
Id: PDJm1Hwx0oo
Channel Id: undefined
Length: 34min 17sec (2057 seconds)
Published: Fri Aug 26 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.