GraphQL/JWT Speed Run with Refresh Tokens

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
on this blue color coder we're going to do a speed run with graphql axios react query jots refresh tokens and react wow i think i got it all hopefully this is a viewer request video and it's kind of a combination of a bunch of viewer requests but i think this is actually maybe a fairly common use case so if any of all that confuses you i hope this helps unconfuse you so of course as always all of this code is available to you on github so you can check it out either as we're going through it or after okay let's jump over into the terminal and get our application off the ground so i'm in a directory called temp jwt and i'm going to use my npx create mf app to build two different apps in this directory the first one is our react host app and i'm going to call that host it's going to be a type of application it's going to import 8080 and it's going to be a react app and i will use typescript and tailwind and now i'm going to run that again and i'm going to go create our graphql server so we'll call that not unsurprisingly server it's going to be an api server on port 3000 and i'm going to use the graphql apollo template there's much of them but i'm going to use the graphql apollo template for this one all right so let's bring up vs code and have a look around so i'm going to go and create two terminals and create one that's in the host directory and that was yarn to get that going and i'm going to create another terminal and i'm going to go into the server directory and pretty much do the same thing okay it looks like the server is done so now i'm going to go and add some dependencies in there because that's where we're going to start and to that server i'm going to add guid and also json web token so these are going to add support for generating a unique id and also the json web token that's going to give us the ability to create a json web token and then verify the token that comes back to us and a json web token in this case or a jot or a jwt depending on how you want to do that i don't know why it's called jot i think it reads better i guess than whatever that would be so it's a jot it's basically going to maintain our user identity so the first thing we're going to do is authenticate against our graphql server using mutation and then we're going to get back a jot and that jot is going to have in it our identity but it's also going to be signed by the server so that it can't be spoofed and then as we go and get some data it's going to allow the server to then get who we are from that json web token so we don't have to tell it who we are we just add it as part of header and then every time we make some requests we just get our data as opposed to anybody else's which is really easy now since we're in typescript i need to add the types for that so i'll just go and add those in and then let's go have a look at what the code of the server is it's actually really simple stuff so we just bring in the apollo server and gql at this point it's got a little book example in there we're not going to use that it's got some books and it's got a resolver that returns the array of books and then we just fire up that apollo server so it's really just a really nice starting spot that's got all of the typescript and everything else ready to go so we don't want to do books here we're going to have a bunch of users and they're all going to have to do's and we're going to allow the user to read their to-do so we're not going to do the full crud on the to-do's that i leave that to you if you want to do that so first thing we need to do is get some users so i'm just going to create a new file over in the source directory called users.ts so this code is just an object it's got as the key the string which in this case is the user name so like sally is one of the users and jane is one of the users and then each one has their properties so in this case sally's got an id of one password is one two three and to-do's of this obviously passwords in clear text not good in fact this whole structure is not all that great but really it's just there for us to test okay so then we export this as our export default so there you go cool we're not gonna have to change that again now over in here we're gonna bring in jot from json web token and good from good then we're going to bring in those users that we just created over that users file and then we're going to change these typedefs so i'm going to just have a typedef for the query which is going to get the to-do's and notice i'm not going to give it a user id to pass along to ask for a particular user's id that's just going to come in from the jot that's included in the header and so how do we get that jot well we call this mutation called authenticate which has a name and a password and returns a string and that string is the jot so that in any subsequent requests from the client to the server we then pass that jot that we got back from the authenticate as a header that goes along with our query okay we don't need these books any longer so let's get the word of those now this books is going to turn into to do's and it will just return at this point an empty array but what we really want is that authenticate mutation so i'm going to bring that in so here's authenticate so the first value to any resolver is the parent and we don't have a parent in this at this point so that's just you know we ignore that entirely and then it's the argument so that would be the name and the password as specified here so you have to have the name that's why there's an exclamation point there and it's required same thing with password and there you go and then what we do with that is we check to go and see if we've got that name in our database of users and then if we have that name then does the password match the password that we've got back and then if it is then we're going to vend a jot that's that's the correct authentication details so we're going to do is we are going to sign a jot and the data that we're going to sign is the name so we're just going to use the name as the token you could use the id use whatever you want but this is essentially secure data so that's that's fine and then we're going to encrypt it with something so we need to create a jot secret we'll get there in a second and then we need to say how long it's going to last for so in this case we're going to vend one that lasts for a whole hour so what's going to happen is at a certain point that's going to expire and then you're going to need to ask the user to re-log in and that's actually what the refresh token is about but we'll get to that later on so that adds a lot of complexity all right now if that didn't work if they get the wrong username or password then we give back an authentication error and say hey bad credentials we don't know you so now we need that jot secret so let's go build that so i'm just going to shoot up to the top of the file and we'll just bring in jot secret and we'll say well hey if we get a jot secret from the environment variables then we'll use that otherwise i'm just going to use secret which is not exactly a great secret obviously not production worthy code don't do that okay so let's try this out let's go over here and we are going to now start the server and we'll bring that up and that brings up this cute little apollo ui here so i'm going to click on that and that brings up the apollo studio which is really cool and apparently i've pre-loaded here some queries that's great so the first one we want to do is test out that mutation so we've got a name here that maps to the variables coming in on the query in this case i'm going to put in jane one two three we'll run it and hey cool awesome now we've vended ourselves a jot we got back the authenticate and we've got a jot there so now we want to send that back to the server when we do our to-do's so we're going to do is we are going to copy this and put this into a value down here and that this is where the headers go and this is going to be x-axis token right so now this is our access token and when we do the query we want to get back the values for well who did we sign in as we signed in as jane's we want jane's to-do's so let's go back over here to our headers we'll hit run and currently we get nothing because well that's the hard-coded value that we did over here in response to the to-do's we said nothing well we want to fix that so the first thing we want to do is set up the context so there's a third parameter to any resolver there's the parent which is the first one there's the arguments which is the second one and then there's context so into the context we are going to decode that x-axis token and say okay cool well that's we verified the jot and it is this person and then this to-do's is going to look at that context and use that context to send back the to-do's for that person so we need to first set up that context so let's go down here to the apollo server initialization and i'm going to bring in a context key and that's a function so it gets called on every single request and then that's got a request on it and the first thing we're going to do is set up that context we're going to say that the name is null and then we're going to look to see if we have that x access token that we put in the header so over here x-axis token like that and if we have it then we're going to do that verify now verify throws if it doesn't like it so that's not great but that's what we're going to do but if it doesn't throw then we're going to look at the data that's coming back and we know that we stuffed into the data up here the name so that's going to come back as the token.data there's a couple of things on token like the time that's left on the token and all that all we want in this case the data in that case it's the name all right so that's pretty good and then the next thing we need to do is come over here to to-do's and actually look at that context and get the right data for that person if we have them okay so here we go so we've got our parent which is again unknown we don't care arguments we don't care we are bringing back that context and that context has a name which has a string and there you go so then we see if we have that name and if we do not have that name then we fire again that authentication error but if we do have that name we just give that the to do so let's uh save this out and then we'll rerun it and we'll come back over here and we'll see you oh awesome okay so we're hearing that our to-do's as jane is the learn c and learn c plus plus so let's go take a look is that true so let's go back over here go and take a look at users and we scroll down we see jane yep and she's got learn c and there's people so also perfect rockin this is great okay so let's go over to our ui and start actually making the requests from our ui so i'm gonna close out these and then we're going to open up host and go into the host terminal and then i'm going to add a few things the first thing i'm going to add is axios that's going to be making our request for us as well as react query which is going to bring essentially that axios query mechanism into the react context and manage all the error states and everything else and do it all as hooks to make it really easy and workable in our react environment and the next thing i'm going to do is bring in daishi kato's react hooks global state and i've gotten a lot of questions about where to store jots and how to store them and there's some interesting connection here between axios and also the react stuff and what's going to happen is that both axios and also react are going to want to know about that jot the react stuff is going to want to know if you have a jot or not in order to say well am i going to show the login ui or i'm going to show the to-do's ui but the axio stuff is going to actually want to know what the dot is and maintain the jaw in order to go and send that jot along as the x-axis token so the way that i do this is i use that react hooks global state to create a state for the jot in one place that's usable not just by axios but also by react thank you daishikato again for an awesome library okay so let's go over into the source and then i'm going to go and create a new file called client.ts and paste into that some imports so i'm going to bring in use callback from react we've all seen that before and bring in create global state from that react hooks global state the daisicato library and then also axios from axios and i'm going to use that create global state to create a global state it's going to have an object that has in it the jot token which are going to initialize to an empty string and then i'm going to get back use global state get global state and set global state so use global state is what i would be using if i just was using react code and get global state and sec global state are when you've got code that is outside of react that wants to set or get that global state which in this case would be that axios code so they use get global state and set global state and then i'm going to kind of make it easy on myself i'm going to wrap these up into some essentially selectors so i'm going to have get jot token which basically just gets the global state of that jdbt token set jwt token which just sets that based on a value that gets in these are just functions and all i do is just set the global state of jwt token to that value this jwt token matches this key right here and then of course use jwt token which is what we'd use in the react context all right so the next thing we need to do is create our axios client so we use axios.create to create a client and we give it the base host which in this case is localhost 3000. that's where our graphql server is located since we're going to be doing cores i'm going to go and add the access allow access control origin cores header as well as the usual content type for application json and then i'm going to create an interceptor and i love that interceptor with that word i've been a huge fan of the word interceptor since the original mad max road warrior v8 intercepta i love interceptors anyway whatever so in this case an axios interceptor basically is some middleware that gets run before or after the request is made to alter the request so in this case we are going to well you don't need a console log that out but we do need to go and set that x access token to the token that we get from jbt token which is going to start off being empty of course and then we're just going to return that config and if there's an error then we just reject it fine okay so next thing we're going to do is we're going to build out a custom hook for this and this custom hook is going to be called use client so it's going to give us back the jot it's going to give us back a method for login log out and also get to dues eventually so it's gonna have all the stuff required by the react code to interact with that axios client and make the requests to the server so of course the most important thing we need to do is log in so this is where i'm gonna use that use callback that we brought in at the beginning and we're gonna use that use callback to create a login function and that login function is going to take a name and a password and it's going to use the client that we created above here with our axios create to post to slash we're going to give it the query which in this case is the mutation that we had before as well as the name and password so this is effectively what we were doing over in our sandbox over here this is the mutation that we're making is this one right here and those the variables we're sending along except in this case we're not just sending jane one two three like we have it hard-coded here we're sending whatever we have in the code so in that case it's you know name and password and we're also going to use with credentials to say that we want to pass along any cookies that we get we're not getting any cookies yet we get those with a refresh token later all right and then coming back we get data and that's going to have our jot in it hopefully now i'm just going to do the happy path here you want to handle all the edge case and error cases yourself so once we get that data we're going to call set jdot which we got back from our use jwt token up here we've got the basically setter and getter just like a standard use state and that's going to set that jot then we need our log out function that just basically sets it to an empty string in reality you're probably going to want to call the server to actually formally log out but i just want to make this pretty easy and of course then we want to return all this the jvt the login and log out and hey we'll also export the client although we're not going to use that but we are going to use this use client right here for our react stuff so let's go over into our app.tsx and start bringing that in so the first thing i'm going to do is upgrade our import from react to bring in use state and use callback then i'm going to bring in that client from that we just created with its use client and then down on the app i'm going to turn this into return statement i'm going to say that this is our to-do's app and changes to to-do's app and then at the top of this i'm going to bring in our use client to get the login the logout and the jwt and then i'm also going to set up use dates for the name and the password as well as the login callback and we're going to initialize the name and password to be sally in one two three just to make it a little bit easier on ourselves and finally we need to go and bring in the ui for this so i'm going to replace all of this kind of placeholder ui with some ui that has a two column layout so we've got a grid two columns with a little bit of gap in between and then on the left hand side it's going to have the login or logout ui so in this case if we've got a jwt then we say log out if we have it if we don't have a jwt then we've got to log in over here and then we'll bring in the to-do's ui in this second section which will be on the right-hand side so let's give this a try we'll go over here to the host and then we'll do yarn start okay cool looks like we've got our ui so let's go and inspect what we're gonna do here we'll go over to network make that a little bigger and then we'll hit log in and then we get blocked on a coors request so we need to go and upgrade the server to allow for course access because we're doing that with credentials so let's go back over here to our server stop it and then go back into our server code and the way that we enable cores is to add on the course key to say what the origin is going to be what the acceptable methods are and what that we are expecting credentials to go along with it so we'll save that out and we'll restart the server and now we'll try logging in again and it looks like we're good looks like we got a jot back so we're doing a post on localhost 3000 we can see the mutation down here and then we can see the response we get back has the dot in it so yay we're looking good okay the next thing we want to do is actually go and get those to do's so we can do a round trip using a job so let's go back over into our visual studio code and i'm going to close this out and then go back into the client and i'm going to scroll down to the bottom of use client and i'm going to add in get to do's so this get to do's is another used callback again it uses the same thing as before right just like with mutation we're doing a post but in this case we're doing a query where we query for to do's again we send along the credentials and then we are going to get that data back and pull out the to do's from there and all we need to do at this point is just make sure to expose that as part of the return and then we're going to go over into our app and bring that in so go over here to get to do's bring that in and then we need a place to store it and also a fetch it so we're going to create some state which is just going to be a an array of strings it's going to have set to do's and get and to-do's and we're going to have a callback called on get to do which is going to call that get to dues and await the results so this is an async method it's going to wait for the result to come back and then set it again always the happy path on this code and then finally we need to actually have that ui to both show it and uh start the get to do's so i'll bring that put that into this right hand column here and that's gonna make sure that we have a jot and we have to do's and if we do then we're going to iterate through those to-do's and also we're gonna have a button that gets the to-do's so let's save that out and we'll take a look and now we need to log in and we get our to-do's cool okay so that these are the to-do's for sally which is learn graphql and learn dots which we're doing right now awesome okay so that's part one of the video and now in part two of this video we're going to learn about how to do refresh tokens and also how to get react query working with all this so what is a refresh token so we've got a one hour time limit on the token as we already have specified but what happens if you want to bring that time limit down you want to constantly be going and getting new jots every once in a while but you don't want to go and have the user have to log in every time so let's say you want to bring the j lifetime down to like five seconds or so so we don't want the user have to log in every five seconds that doesn't make a whole lot of sense so how are we going to make it so that we have a nice way where they don't have to log in but we do have these short term jots well the way that we do that is we use a refresh token so what's going to happen is when we authenticate if we authenticate properly we're going to send back not just the jot but we're also going to send back a refresh token as a cookie and the cookies can't be messed with on the client so they're secure and that refresh token will then allow us to if we get a response from the server saying oh that was not authorized we will be able to go back and call another method called refresh and as part of our request we'll be passing along that refresh token cookie and the server will then look at that refresh token and say okay you are in this case sally and so therefore i'm going to generate you a new jot that's going to also again last for five seconds that has that credentials on it so you get a refresh token without actually having to log in again and also i'm going to refresh the refresh token cookie all right so let's try this out so the first thing we need to do is go back over to our code for the server and drop down that time limit to about five seconds or so just make it really easy ourselves so i'll go back over here to our server and index and we'll see when we vend that jot we're going to vend it for a much shorter amount of time so currently we are expiring in one hour let's requi let's do it in five seconds just like that okay so the next thing we need to do is format the response so if we know that they've asked for authenticate then we are going to also go and add the cookie onto the response so how do we do that in an apollo server we add another key here called format response and what this is going to start off doing is it's going to say okay so if we have any errors meaning that we are not authenticated then we are going to return a 401 now normally in apollo even an error is going to return a 200 and don't even get me started on that i think that's a terrible idea but we actually need to force it to do the 401 so that's going to help us on the axio side because axios is expecting that if you've got an error you're going to get a bad response code so we need to make that happen and this is the code that we need to use on the graphql side to make sure that we get a 401 response if i go back for the to-do's and i'm no longer validated because my token has run out so the next thing we need to do is vend out the cookie so if that's not the case if i did authenticate or in which case we're gonna have this authenticate on data coming back or it's a response to refresh which is a new mutation that we'll add then we're gonna go and create a new refresh token so we create the token expiry date by going and adding seven days on to today we go and use good to create that refresh token good and then we're going to get the data coming out of the response from the authenticate or the refresh that we just did because we want to see that name what do we authenticate as so that's going to come back as data here and then we're going to create a map called refresh tokens that maps from the refresh token to the identity and that's going to be just something we manage on the server so there's no way to spoof that you have to hack the server itself in order to reclaim that identity with that refresh token and then we use a jot to build the refresh token itself we give it a long expiry date and we just sign it with the guide that we got back from before so what's going to happen is when we ask for a refresh we're going to get that guid that refresh token guide we're going to look it up in this refresh tokens object and then if it's valid we're going to say okay here's a new jot based on that identity and then finally we're going to set the cookie coming back from the client that's going to have that refresh token in it so we got to store those somewhere so let's go up to the top of the file and we'll create a new object called refresh tokens which can be that map that goes between the refresh token and the user that that's assigned to but we also need to update our context so let's go down here to the context creator now before it was just managing the name but now we want to also pass along that refresh token if there is one so i'm going to replace this code with some extra code that's going to map that so so we're going to add on refresh token to the context and we're going to see if there's any cookies if there's some cookies then we're actually going to do the parsing ourselves of the cookie to go and get to turn this cookie string into an object that has cookie value and name and value on it and we're going to use that to get the refresh token which is going to be on that cookies and we're going to add that to our context and then we're going to have what we have before when we looked at the access token so now we need to go and add on to our typedefs we need to go and add an additional mutation so we're going to add refresh and that's not going to take any parameters because the way that we're going to get that refresh token is again from those cookies of course we need a resolver for that so let's go down here to our mutation we have authenticate already so we're going to add in the mutation for refresh now refresh doesn't care about the parent doesn't care about the arguments because there are no arguments but it does care about that refresh token that's coming in from that cookie and all he needs to do is verify that that refresh token is for realsies and then it's going to go and look up that guide which is what's in the refresh token and see okay so is it in there and if it is in there then it's going to have the user identity so we're going to use that the refresh tokens on that token data to get the original user identity so if it was sally sally would have the jot that has her name in it that username but she's also going to have that refresh token cookie so let's save that out and try it out so we're going to go down here to our server we're going to rerun it all right looking good so let's go back over to our ui and i'm gonna hit refresh and then log in and what i want to see in the response headers coming back is i want to see there you go set cookie so that's coming back from the server and saying cool this is our refresh token so that's that's awesome that's a great start and that's going to implicitly be added to any subsequent request so in this case you've got the cookie which is the ajs anonymous id but in subsequent requests we're going to get that refresh token also going out as part of the request so now let's do the tricky bit over in the client so let's go over to our code in the host or in source client and we're going to do is create an interceptor here and what we're going to do is we're going to say that if we get a response from the server saying that we're 401 meaning that we're not authenticated anymore we are going to retry and this is going to be something that axios does on its own axios is going to see that we tried to get to dues and that the token was not valid and so we're going to go back and ask for a refresh we're going to call that post with that mutation refresh and assuming that we get the token back we're then going to set the token again and then we're going to retry the request which is really cool because it means as the user of this axios client i don't actually have to care on each and every request if that request was invalid on a token and then go and remake that request it's automatically going to happen for me so that's really cool all right let's try it out and see if that's going to work so we'll go over to our host we'll restart and now here's what i should expect to see so we're going to do our login that's going to work and i'm going to get to dues and that's going to work and then after a certain amount of time uh five seconds i'm going to try it again and that's going to not work that's going to give us our 401 unauthorized back so we're seeing that right here we get this this response to the 401 unauthorized to our request for to do's so then we call subsequently a request for mutation to refresh that gives us back a updated jot that we then recall query to do's and we get back the to do's awesome so you no longer have to care as the person who's writing the code to go and get the to-do's if you are unauthorized it's just going to go back and ask for another token and away you go now of course if the long-running refresh token runs out then you will get an error and because we're only going to retry the once and then you'll need to re send the person back to the login so that was a lot of code so amazing stuff okay let's uh finish this up by getting this integrated with react query so i'm going to go back over to our app code and then bring in react query i'm going to create a query client right down here using new query client from react query and now i've got to wrap the app in that provider so that's just a react query thing so i'm going to create a new component called app and then i'm going to use that app as my basis for the app as opposed to the dues app so let's see if this is running let's go back over to our app okay looks pretty good everything's still running fine so let's go over and replace our fetch code that would do the to-do's with react query instead so in this case it's going to so in this case we're going to use use query i'm going to give it the key of to do's and we're going to give it get to do so react query actually doesn't make queries itself it uses a query function to do it and get to do's fits that bill perfectly and then the final thing we need to do is do on git to do's and update that to instead of doing the management ourselves we just invalidate the to do's okay let's try it out so refresh i'm going to log in that looks pretty good i'm going to get the to-do's and it did it automatically that's pretty cool and that's because we have this enabled over here so the moment that it saw that we had a valid jot it made the request to go and get the to do's and then finally i'm going to get the to do's again and we can see after five seconds or so that we get the 401 and then we get automatically the request for the updated data just like that all right well that was pretty epic so many technologies involved in all this and it's a very intricate dance when it comes to the jots and the refresh tokens and i really hope that this code acts as a template that you can use to at least get you started in your journey around jots and refresh tokens of course if you have any questions be sure to put those in the comments section down below if you like the video be sure to hit that like button and if you really like the video be sure to hit the subscribe button ring that bell and you'll be notified the next time a new blue collar coder comes out
Info
Channel: Jack Herrington
Views: 5,776
Rating: undefined out of 5
Keywords: apollo graphql jwt authentication, axios jwt, axios react, axios refresh token interceptor, graphql, graphql jwt, jack herrington, jwt, jwt authentication, jwt refresh token, jwt tutorial, react jwt authentication, react query, react query authentication, react query tutorial, refresh token, refresh token jwt
Id: QChEaOHauZY
Channel Id: undefined
Length: 32min 28sec (1948 seconds)
Published: Thu Oct 28 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.