Authentication in React with JWTs, Access & Refresh Tokens (Complete Tutorial)

175.57k views5158 WordsCopy TextShare
Cosden Solutions
🚀 Project React → https://cosden.solutions/project-react 📥 Import React (Newsletter) → https://co...
Video Transcript:
in this video you're going to learn how to correctly handle authentication in react when it comes to jwt's access tokens and refresh tokens we're first going to start with some Theory really trying to understand and get the mental model of how these things work and specifically how they work within a react application and then I'm going to show you some actual code so that you get to see how this actually looks like in a real react application and hopefully learn from it and then apply to your own projects right because this is important authentication is crucial for a good and secure react application if you don't get authentication right in your application you're going to ship security vulnerabilities and your user's data is going to be at risk some attacker can actually get your users data and that's never a good time for anyone involved so really pay attention watch until the end of this video and try to really grasp these Concepts because they are super super important cool let's get into it so over here we have your typical kind of traditional end to end life cycle when it comes to a react application and the different things that are involved for example over here we have the user who's actually using the react application right the user is going to interact with your application maybe they're going to click a button they're going to type on an input they're going to visit a page whatever they do they are interacting with the react application the react application takes those interactions and then converts them into requests to the actual server usually there's a server and then the server is going to receive those requests is going to process them and then return a response back to direct application either a a success response or an error response and then the react application is going to take that response and then reender and show the updated UI to the actual user right so in a nutshell this is kind of how a react application usually works of course there are exceptions you're not always going to have all of these things in the same way but for the most part this is how it works now for this to work properly the server over here actually has to know who the user is that is actually sending the request because the user first of all they're not talking directly to the server they're talking to the server through the react application the react application in this case is kind of acting as a middleman between the user and the actual server taking the user's inputs transforming them optionally in some way sending them to the server and then taking the server's response and actually showing that to the user the server in this case is not just going to send any response to any request that it gets right that's not usually how this works right you're not going to send data from your actual database to anyone that asks for it usually you're going to require that you know who the user that's asking for the data is cuz sometimes you might want to know at least that they're authenticated that they're signed into the application so that they can actually access it or in some other cases which is pretty common as well that they have the correct permissions to actually get and read and do things with the data that specific entry that they're trying to access and all of this falls under the concept of authentication identification or even verification right the server has to authenticate every single request has to know who is requesting the data and then it has to identify that user identify their permissions verify that the user can actually do what it's trying to do and then if the user can it's going to return a success response if the user can't it's going to return an eror response and then it's the job of the react application to return those responses to the user in the form of rendering right it's going to reender the UI the user is going to see something on the screen and then the user is going to be able to take appropriate action now for all of this to work we're usually going to use jwt's right so let's put here jwt's jwt's are Json web tokens if you're not familiar you can just Google jwt's and see the first link that pops up it's a Json web token it's essentially a token that is encrypted that can store data on a specific user and can be used by the server to actually validate if that user is valid if that request is valid or not JWT you can set data within them they're all encrypted so it's safe to actually transmit around and you can also set expiration dates which will allow you to expire the token after a certain amount of time and require the user to resign in to Reet a new token before they can proceed making more requests to the application it's essentially a security measure and these tokens are pretty much the standard in authentication in today's world now in the context of our react application over here we're going to have two of these tokens we're going to have something that is called an access token so we're going to do access token and then we're going to have something that is called a refresh token these tokens over here are going to be used either by the server or by the react application to essentially identify and authentic at the user and then allow the user to make requests to the actual server these things over here these access tokens and refresh tokens which are both jwt's by the way so both of these arej WTS are going to be used within this process to actually create a successful and robust authentication for any given user in any given application with any server so how does this work well let's start at the beginning let's consider the initial stage at this stage the user is not authenticated the user maybe opens the app for the first time and they're not authenticated then they're presented with a signin form because let's say that the react application requires them to be authenticated to have an account before they can do anything so they're going to have a sign in form in that form they're going to fill out the form enter their email their password whatever and then that's going to be an interaction that's going to then send a request when they press submit and that's going to make a request to the server to actually authenticate and log into the application the server is then going to receive that request it's going to get the credentials from the user the email the password makes some check that they're correct in the correct shape then it's going to look in the database see if there's an actual user with that email and if the password is correct and only if everything checks out is the server actually going to consider the user logged in and in that case the user is going to do two things it's first going to take some data from the user maybe the user ID maybe an email maybe something else altogether it really depends it's flexible and it's going to create a refresh token from the user using that data that refresh token the server is never going to send to the the front end this is a very very secure token and should never be leaked to anyone outside of the server the server what it's actually going to do is let me make an arrow here it's going to store this in an HTTP only cookie so let's do HTTP only cookie this means that let me just make here so we have a little bit of space this means that this is a cookie that cannot be accessed by JavaScript which essentially means that only the server is going to be able to set this cookie and only the server is going to be able to read this cookie the react application or any other JavaScript code that might be malicious code by some attacker can never read the secure token because it is an HTTP only cookie this is the first layer of security then the server is also going to take either again some data from the user or it's going to take that refresh token and it's going to generate from that an access token so let's make an arrow over here it's going to generate an access token that is then going to send through the response back to the react application and the react application is now going to be in charge of storing and managing this access token this access token over here is going to be the key that is going to allow the user to essentially authenticate against the server and this token has to be passed along every single request that the react application makes to the server because that is what identifies who the user actually is remember this access token was created from the refresh token which was created by some data from the user and that's how we can uniquely identify the specific user as as long as we have a valid access token we can make requests in our react application on behalf of the user and those requests are going to get validated on the server because this is a valid access token now here's the thing remember that we have these jwt's which access tokens and refresh tokens are we said that we can put expiration dates on these access tokens and we usually want to do that for security and what I'm going to do here I'm going to show you the typical expiration date for a token so for an access token you're usually going to want to do something very very short for example ex Le like 15 minutes like so that's how long this access token is going to be valid after 15 minutes this token is not going to be valid and then we're going to need to have a new one for the user to be able to keep interacting authenticated with the application the refresh token however this one because we're only storing it on the server and in an HTTP only cookie which is a secure cookie we can get away with having this expire after a bigger amount of time so we can do something like 30 days for example right doesn't have to be 30 days it can be 60 it can be 15 it can be 9 it really depends on your use case but generally 30 days is a pretty safe average that we can Implement for this example now with these two tokens we actually have everything that we need to first of all authenticate the user and then keep the user authenticated so this is how this works this refresh token is essentially the last source of Truth this one remember only stays on the server never goes on the client as long as this one is valid which for now we said it's going to expire after 30 days we're going to consider the user to be authenticated now this access token where do we store it where you might initially think that you want to store this in local storage right or even maybe in a cookie but I would advise against that because that is not secure remember everything that you store in a cookie or in local storage through your act application which really is through JavaScript an attacker can also access as well they can write the same JavaScript code to check your cookies to get the token or to check local stores to get the token I would not recommend that you put this token inside of local storage or inside of a cookie because it can be accessed by JavaScript so where do you put it well the safest place to put this token is to put it in memory that means in the context of a react application in state right that is state that is only local to this react application that lives inside of the memory of the actual computer the process that's running this react application right so let's come here and let's make a a little text here and let's say in memory and I'm going to put here State just so that we know that this is in state we're going to store this in memory in state so that no JavaScript can actually access this because it's it's a lot harder to access a computer's memory than it is to access the cookies or the local storage putting it in memory is safe enough and when you add the fact that this expires after 15 minutes you're doing a lot to actually make the security of this token and to prevent any unwanted attackers from actually getting this token right again it's much easier to access it's much harder sorry to access this token if it's in memory if it's in state than it is to access it if it's in local storage or if in a cookie but then you might ask yourself well if this token is in state and it also expires after 15 minutes what happens when the user refreshes the application or even what happens after 15 minutes what do we do with this token well this token first of all is the responsibility of the react application the react application receives this token from the actual server and then it is responsible for storing it in memory and it is also responsible for handling the case where this token actually expires right so let's think through an example the user sign in initially to our application right they filled in the signin form we send that interaction through the application as a request to the server the server authenticated the user created a refresh token stored it in an htttp only cookie and then generated also an access token send that to react application and we put that token in state what happens if the user either refreshes the application refreshes the browser window or if 50 minutes have passed since we generated that initial access token well in that case if the user refreshes the application we're no longer going to have a token so we're actually going to send undefined as a token to the back end or if you still have a token but it's expired we're going to send that token and the backend is going to realize when it tries to verify the token that it's expired in any way we're going to have a token that is going to fail the validation check on the server and then what the server is going to do is instead of logging the user out directly then in there it's first going to check the refresh token in the HTTP only secure cookie and if there is a refresh token there it's going to try to validate it and if it's valid it's going to generate a new access token and then send that through the response to the react application and the react application is going to store it in memory once again right and then the react application is going to take this token and then send it along every single request once again just like before that is going to work until the user refreshes the application or the token expires after 15 minutes and after that we're going to do the same thing again the server is going to check this refresh token again and if it's valid it's going to revalidate the actual access token and the reason why we do this is because we don't want to sign the user out every single time that this access token gets expired because that would be a horrible user experience we use this for security and for convenience and then we have this as a backup as the single source of truth of the actual user authentication status right so again as long as this is valid we're always going to be able to regenerate an access token and store that in memory as well and I mean that's really it right this isn't a nutshell how authentication works with jwt's right the server is only responsible for this refresh token and generating the access token but it's really responsible for keeping storing checking and verifying this refresh token the react application is only responsible for this access token and rest storing it in memory in state not in local storage not in a cookie and this is a pretty safe bet for this access token and then the user through this mechanism can interact either with this access token or using this refresh token that gets regenerated from the server and actually be be authenticated and identified against the server so the server always know who the user is that's making every single request this miss the this works so now that we have this and now that we understand this let's quickly look at some code to see this in action and see how you would implement this in an actual react codebase all right so over here we have a simple authentication provider component from another application that I have that really illustrates how all of this works so the authentication provider component is responsible for everything related to the actual token and sending the specific request to the server to actually authenticate the user so as you can see here we have a state variable for the token that is exactly the access token that I talked about here this lives in state inside of this component which is going to be handled by the processor memory of the actual computer that is running this react application then over here we have a use effect that runs once on Mount and it essentially tries to fetch the actual user from api. getet API me and then it's going to get the response. dat.
access token and then set that in the state if we don't have a token so if the user is not authenticated we're going to set the token to null and this gets used by the rest of the application to determine if the user is signed in or not actually if we have a token if it has a value the user is signed in if we have a token of null it means that we checked the back end that we sent this request but that there's no token so the user not sign out and if the token is actually undefined which is its default State over here we haven't provided any default value so it's going to be undefined that means that we haven't fetched the token yet and and then we should still we should throw a loading state to the user and let this run and this is going to actually set the the token once that this request has finished and then over here we have a use layout effect and I'll get to why we're using use layout effect versus use effect in just a moment but essentially this is creating an Interceptor for the actual request so we're doing api. interceptors this by the way is using axios as a fetching Library so we're adding an Interceptor on the request and then all that we're doing is we're checking here if we have a token right if we have a token we're actually adding that token in the headers in the authorization headers of the request and if we don't we're just passing the authorization headers that were there before this is actually has the job of taking the token this runs whenever the token changes to actually put it and inject it into every single request so going back here to our actual thing this is taking this access token and putting it in every single request automatically to send to the actual server this is going to run initially once on Mount so we're going to do this and then every time that the token changes which is why we put it here in this dependency area and the reason why we're using used layout effect as opposed to use effect is because we actually want this to block the rest of the rendering because other components further down in the component Tre are actually going to trigger requests and we want to guarantee that this code over here this Interceptor is put before any of those components actually trigger any request so that's why we're using use layout effect versus use effect now over here we have an actual helpers file for the API and we have this with off function over here that essentially wraps every single request that needs to be authenticated and this one all that it does is it just checks if we have a token from the config dohe heads. authorization and if we do it's going to try to verify it otherwise it's going to make make verified as false this verified token just receives the token and it's this function over here I'm using the Jose Jose whatever you want to spell it or pronounce it library to actually have a token this by the way all runs in the front end because this was an example that I used actually but typically you would do this inside of a server environment so this is not totally accurate but still it does the things that it's supposed to do it actually tries to verify the actual token and just returns either true or the actual payload because again we said that these tokens Can optionally Have payload of a specific user and in this case I was using the payload to identify who that user was so then we're calling that and we have this verified variable over here and then we're just checking if verified is true if it isn't then what we're doing is we're returning a 4 or3 so forbidden that is the status code for forbidden and a message of unauthorized otherwise we're just going to call the actual request because this user is authenticated and we've done this check this is a convenience helper to wrap every single request that is done in the application that needs to have authentication to essentially not have to repeat this code over and over again we just want to put this as a wrapper to any function and this will take care of checking the user's authentication and then if we go back here to the Au provider we're actually using that over here in this used layout effect so this we're creating again using use layout effect because we want this Interceptor to be there before any other component continues rendering and this one we're just doing it on the actual response this piece of code over here is essentially handling the case where we either no longer have a token or this token is expired and we tried to send it through an Interceptor through this initial Interceptor over here on the request but the server said hey this token either isn't there or it's expired so it's no longer valid but I have a new valid refresh token so here's a new access token for you that you can use to reauthenticate your user that is what this piece of code this use layout effect over here is doing so this as you can see we're putting an Interceptor on the response as opposed to the request as we did over here and then we're just doing nothing with the response but we're only checking if there's an error and let me remind you that this if we run this piece of code over here this is going to result as an error inside of axio so that's why we're doing the error and we're basically what we're checking is we're first storing the original request from error.
config in a specific variable and then we're checking the error. response. status making sure that it's four or 3 and that we have data.
message that is equal to unauthorized which again are these values over here 403 and then message unauthorized and if that is the case then we're in a situation where this user hasn't been authenticated the server said hey you're not authenticated this access token is not valid I can't fulfill this request for that user in that case instead of as I said signing the user out then and there we first want to make a check if the actual refresh token is still valid so over here if that check passes right 403 and unauthorized we're trying to create a new response and doing api. getet API refresh token that is going to refresh the token of the actual user then if we have an actual token response. data access token we're setting that token in the state again we are replacing the existing access token that was there before with the new access token that we just gotten from the API SL refresh token endpoint and then we're doing original request so this original request that we tried to fire initially that resulted in this error this 4 or3 error because the access token was no longer no longer there we're adding that token inside of the headers of the authorization we're doing that over here and then what we're doing here is we're adding this custom property doore retry equals true and the reason we're doing that is because remember this over here is going to run every single time this token changes and here we're actually setting this token to change and essentially we don't want this to overwrite the token token that we're setting over here in this use effect in this use layout effect we're putting retry true because this is a request that is being retried and over here if you notice we're checking config doore retry and only if retry is not there are we actually setting the token otherwise we're putting the token of whatever was there initially in that request and then with that the only thing that's left to do is return API and then call the original request over here so what we've done is we first sent this token to the actual server the server said hey this token is no longer valid that's fine then the react application intercepted that response checked if it's an actual response from our server that says unauthorized and then we're trying to actually refresh the token by sending a new request and this is all Happening by the way the user has no idea the user thinks this is just one request but actually this is now the second request and then if we have a new access token then we're setting it in the state again again to be used in further request through this use layout effect over here and we're setting it in the original orig request we're setting original request.
Copyright © 2025. Made with ♥ in London by YTScribe.com