Passport JWT tutorial - Authentication with JSON Web Tokens

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
in this passport JWT tutorial we're going to show you step by step how to work with Json web tokens and passport.js if you want to learn how to use passport.js with sessions then we have a video for that too please subscribe to the channel for more tutorials before we do anything I want to talk about Json web tokens all by themselves so we understand at least the basics of how they work before we start working with passport and authentication so I have an empty tutorials folder and I'm going to make a folder called JWT hyphen basics I'm going to CD into that folder and open up the entire thing I'm also going to create a GitHub repository and all that well I guess we could just do get in it takes like one second okay so now I'm gonna make a server folder and then CD into that server folder and we're gonna create a an official project with npm in it hyphen Y which just kind of you know creates a package.json file okay so now we can make an app.js folder and we can create our server so the I'm just going to go ahead and write out some basic routes and I will see you in one moment okay so we have a basic server and basically we're just pulling in Express and then we're storing the express server in the app cons and we're making a couple of routes so we've got the home page create token profile and wrong secret all of them basically do nothing at this point so let's go ahead and npm install Express and while we're waiting we can go to package.log Json and make a script if you wanted so let's say start nodemon app.js add a comma we'll save that and oh and I have a git repository so I'm going to make a git ignore foreign okay it's done so let's go ahead and npm run start okay and localhost 3000 nothing to see create a token okay so we have our routes working so now we can make them actually do what they're supposed to do so the first thing I want to do is fill out the create token route that way we're actually able to create tokens and that's what we're going to do in that specific route so what we're going to do is go back to our app.js file and inside of our create token endpoint we're going to create a temporary user I'm just going to say user equals some guy with some properties okay so we have a user object and now typically what will happen is you create a Json web token with jwt.sign it's a function by the inside this package is this JWT package or Json web token so we're going to sign it with a secret key and then it's going to create this jumbled string so let's take a look at how that works so const JWT equals require Json web token and then we can use that function or that that package so JWT and it has a sign method on it and so we're going to pass in the user that we want to pass into the token so I'm just going to say user is user and we're going to give it a key of top encrypt key okay and then let's also store this in a variable or a const so I'm going to say cons token equals jwt.sign and then we're going to console.log at the token okay and then we're just gonna say do just keep this rest I'll send so let's save it and then I think we're going to need to install a package yep cannot find module Json work token so let's close the server npm install Json web token okay that's installed let's start the server back up now we should be able to just visit this create secret this create token and then visit the page because we have a console log that's logging out the token now so let's visit what was it create token okay coming soon create token nothing changed here but if you look at the logs we'll see this token ey and the big long string so this is the token and it's just a big jumbled string so it's supposed to be secure because this string actually represents some data but we need to decode that data and the only way we can decode it is with the correct token but also we have to have the correct key so the benefit of having this key is that if you say you have a desktop like a website and you also have a mobile app and you know you want the mobile app and the desktop to share the same login well you can do that with the top secret key so if the desktop site has this key and the mobile app has this key then the token should work but if you have a different key then even if you have the same token it's not going to be decrypted correctly or decoded correctly so that's why you need this top secret key here and you can name it whatever you want I could name it elephant or XYZ GH doesn't matter so we have this token and yeah but it's useless at this point we have to actually decode it so that is what we're gonna do now or we're actually going to write it to the database so when you are working with tokens these tokens have to be stored somewhere so so we've been we've we've created this token and now we have to give it to somebody so they can use it so think of it as like a token in your pocket or coin in your pocket and anytime you want to go somewhere that you have permissions to you just pull out the token or the coin and show it to them and they'll let you in so that's basically what the web browser does is they store this token in you know local storage or you know somewhere and they'll pull it out and show it to the server and then the server will be like okay I see where you have access to this thing here so that's pretty much how tokens work so anyway let's go ahead and make a we have a we do not have a fake file okay we're going to create a new file in the server and it's going to be called a fake local dot Json and then we're just going to have an empty object in here and so now we're going to store the token in there so I'm going to do that with this file system.write file which is a package that we need to import so const FS equals require fs and I'm pretty sure that's built in so we don't have to import we don't have to install anything so now we're going to write this to this this Json file so I'm going to say await fs.write file I think local or Json is where we're going to write it to and then I'm going to make it look like I could spell and talk at the same time authorization [Music] and Bearer and then the token okay so I'm going to save that and now let's try this again let's look at our fake local.json file we see that it's empty but if we hit enter or refresh the create token endpoint again now we come here and we see this authorization key has been added or property has been added to this fake local dot Json object here so authorization and then Bearer space and then our token and so this is the token with the fancy user object that we have created and it's just decode it's just encrypted or you know jumbled so we have to decode it now but we have some we have a place to to actually store it so this is going to pretend to be our you know our browser with the token so now we should be able to like visit this profile page and see our own specific user information because we have the token with our user data inside of it so now let's create this profile page I'm just going to go ahead and write it out real quick and then I will explain it okay so basically we're just logging out the fake local dot authorization so fake local is already stored or imported actually it's not imported so let's import that with cons to fake local equals require dot slash fakelocal.json so we're importing this file and storing the results of that into fake local and so we're basically saying like what is the value of fake local to authorization well if we look at it right here authorization it equals this Bearer token so we're printing that and then we are taking a chunk just a portion of the string because we don't want the bearer space we only want the actual token so we're taking only the actual token and sending it into jwt.verify here and then we're also passing in the top secret key into the verify function that way you know it has the correct key or secret key to be able to properly decode it and then we store the results of that into this result const and we give the result a DOT message property and then we print out the whole result and we also send it back as Json to the the client so let's go ahead and take a look at how that looks so we have our token already here so now if we visit slash profile we get our user with all the data and then we get some fancy JWT stuff and then we get our message property that we added to the results object and then once again just like in the client we get it here result is this user with the message that we added so we're basically taking this token and decoding it into a user and then we're able to use that user to do whatever we need to do so that this is like us being logged in and then we can grab the user's you know information by their ID or by their email if we had it in here we're logged in and I want to show you what it looks like to not be logged in also so let's do a wrong secret one as well okay so what we have here is in if you hit the wrong secret you'll still have your token available and we're going to simulate an invalid token or an expired token or just something's wrong with the token and so in this situation we have an incorrect secret so if we were to use top secret key it would have worked but because we have incorrect secret it's not going to work the token is going to be invalid and we're going to catch an error and you can console log the error if you want but or you can also log the result and you should actually probably do that so console.log result okay let's give it a try and I'm actually not sure if it's going to send make an error okay so you failed to hack me interesting okay so I think because this secret is incorrect there I think it just errors out instead of it so it doesn't even like store a result here so I guess we don't really need that I don't want to like Lead You astray so we'll do that so this comes out bad [Music] causes an error so I guess what we would want to do is console.log the error here the error.message it would probably be better let's try that again wrong Secret okay Json web token invalid signature so that's the situation and then we get this you failed to hack me because we are logged out or the token's invalid so that's pretty much how tokens work in a nutshell now let's figure out how to get them to work with Jace with a passport and I'm going to go ahead and duplicate this project just so we can keep this I know you could use git but I'm not going to deal with that JWT basics so let's go to our tutorials folder here I've got this basics here I'm going to duplicate [Music] and then I'm going to rename this passport JWT tutorial okay and then I'm going to open it up and CD into that folder CD into passport JWT tutorial server mpm I think it's npm start npm start foreign we're going to write a bunch of code just to get started so I'm going to delete everything except the app.listen and the index route and I'm gonna go ahead and write a bunch of it and then I'm going to show you what I wrote so this may look like a lot but it's actually pretty simple so we just have the view engine that we're setting to ejs so we're going to need to install that package and then we're setting so the request can to the body or the server can read the body of the request and then we have our index route secure route a log out route get route I get for assigned login sign up failed Success login and sign up and these these two are post requests to handle a form of submission so everything's pretty much just sending regular data except the render so the login and the sign up have renders and then be sure to make sure everything is async if it says async you can always fix that later though but anyway so now we need to set up our view engine so I'm gonna go here and I'm going to install ejs and I think I need body parser let me just stick with the ejs for now okay and then I'm going to try to start it oh all right all right so it seems like we're good to go the next thing we're going to do is make sure that we can view the pages for the login and the sign up and so basically we have a view engine of ejs that reads the views folder so we need to make a views folder inside of our server and then we're going to create login.ejs and sign up.ejs and these are basically just going to be as HTML document with a very simple form so let's go ahead and create that now okay so basically I just pasted this in here this is literally just a regular old HTML document I don't know what happened there so we've got a doctype HTML tags a head tag with a title and a body with an H1 that says login and an HTML form that is a post request to the login endpoint I've got some comments here and then an input with a username and then an input with a password that is of type text and it's already filled in with the default value of password they're both required and then there's a button type submit inside the form that is it and so I'm just going to save it and then I'm going to copy the whole thing over to the sign up page and then in the sign up page we're going to have the we're just going to say sign up for now and well okay so we'll switch this to be correct in the right away so we're gonna we're gonna eventually switch over to email and password so we can switch these this email this username input field to email okay so email and password and then this button will be sign up okay and I think that is it save okay so we have an email and password and the sign up and then a username and password in the login and that's okay okay so if we have done everything correctly we should be able to like the main two things or the login and the sign up so let's test those we want to make sure that we are able to hit this these two routes when they are submitted so we should get these to display on the page when we submit the form so with the server started I'm going to visit the login route I'm going to hit login form submitted that is good sign up and if I hit sign up oh whoops login form submitted let's go back to our app.js oh we need to fix the sign up then so the sign up still has the endpoint of login being hit so we need to change that to sign up save okay let's try it again with a sign up sign up form submitted and a login form submitted okay so we are good to go and we're ready to move on to the next step okay so the next step is to actually get a basic login working with passport so the first thing that we're going to do is go to our app.post route where we hit the login endpoint post we're going to delete the rest.send because we're not going to need it and we're going to inside of this function body of the login we're going to write passport dot authenticate and we're going to use the local strategy and we're just going to take a function with three parameters error user and info and then we're basically just going to say if there's not a user object we redirect to the failed route and then if there is a user then we redirect to the success route so we're just going to hard code that right now I don't know where that came from Okay so and that's it so we're gonna either redirect to the field or the success route and so at the end of this passport to authenticate function we need to make this a function call so we're going to call this with the rec rest and next and so this is the custom callback strategy there's another way to do it that's a little bit like more built in to passport but I don't recommend using it because this way gives you more control if you do want to see how it's used then you can check out my other video where we talk about passport with sessions so check that out and I'll we'll do a quick screenshot at the end to just kind of like summarize it but we'll get to that later so anyway we have a custom callback here and we're calling the local strategy and then so we need to make this strategy because this is not the first thing that happens the first thing that happens when you hit this is it goes into the local strategy and if it succeeds well or if it errors out whatever the result is that gets passed into this function here so we need to go make this so let's do that we're going to do it up at the top I'm going to put it underneath this body parser code here and we're just going to say passport.use and a new local strategy it's going to take in a function with the username and password that we entered in the login form and then a callback function there which will be well well it's this is kind of like the next I guess but it's it's a done it's a function so the next thing is our function body and we're just going to hard code passing in some information to this done function right now so we're going to say return done null username Bob and ID of one two three so this represents a successful the successful Authentication because there's a an object as the second parameter which is the user [Music] okay so this represents a success let's hit save and we need to install local strategy and passport itself so let's do that is a const passport equals require passport and local strategy foreign Okay so npm install passport and passport hyphen local npm start okay and then now let's try the login form so I'm going to hit the login endpoint I'm going to hit login and we get success and so let's again look at the flow basically you your very first step is you hit the login route here it renders the login form you submit the login form it has to be username and password right now so we submit the login form and that hits the post endpoint the post login endpoint here that starts the authentication process using the local strategy so the next step is we go and find this local strategy here this is where we verify that the user has entered the correct information the user exists and the password you know has the right you know number of characters meets all the requirements if it does then we Pat this null represents the error object then then the second parameter represents the user so there's no error when we have a user so we're passing that in here and then after this gets completed this done spits out these pieces of information into the Callback which is right here and so this is the user object that we passed into our done function above and so if this user does not exist we redirect a failed if the user does exist we redirect to success and so that is why we were rerouted to the success page so let's give it a try with the user failing so I'm gonna say return done null false save and then we'll try the login form again let me just not this one login hit go and we get failed because when the second parameter is false or it's not a user then we get the failed situation so this user does not exist it was false so we redirect to the failed route the next thing we'll talk about is the application error so that is this first parameter here error and we're just going to say something like if error then return next error or error.message so if you want to show the error with the stack Trace then just the error otherwise error.essage and so we when you have an application error you pass it as the first parameter of this function up here so we can simulate like some kind of application error if you want and we'll do that by making a variable called my error equals a new error and we're going to say oh no an application error explanation point okay and then now we send that as the first parameter so so we'll do return done my error okay and so now no matter what well because it's hard coded so no matter what we do anytime we try to log in we're going to get this error it's going to be passed in as the first parameter and then down here it's going to see this error from this parameter and then it's going to say yes there's an error so return next with the error.message so let's give it a try log in go let me get oh no an application error without the stack Trace because we just sent the message and so one thing about application errors from the way that I understand them at least is that it's okay for them not to like redirect or whatever and you can control that you know with the front end but error handling specifically is beyond the scope of this tutorial but anyway let's go ahead and move on so I'm gonna go back to the JWT here the next thing I want to talk about is the optional messages that you can pass in so you can pass in a third parameter to this done function and that will be a message object or just an object you can use for whatever so in the case of a failed user you may have like a message that says user not found so you do message user not found and then in the case of a success you could have you know something like message congrats you are logged in and then from here we could use this as the third parameter here this info object so now we're able to do something like failed question mark message equals info.message and I'm just gonna copy this paste it here and so now we have query parameters in our redirections that we can use so info.message and that gets passed into this redirection so now we'll go to our failed and our success and wait not that one we want little back ticks for each of these and then we're gonna say space and then rec.query Dot message I'm gonna copy this paste it here and then now our failed and success Mount routes should have a message that we send all the way back from up here so let's try it with this failed so oops okay login failed user not found let's try it with a success we get success congrats you were logged in so that is optional messages with the third parameter of this authenticate function this info the next thing is naming functions so by default this local strategy will have the name of local and so because of that we don't need to name it here but if you want to name it anything else or if you want to have multiple then you're going to need to name them so we can name our login authenticate strategy we'll name it login and then because this is named login this will no longer get called unless you tell passport what the name is and so you do that with the very first parameter of the use which would be login and that's because we named it login down there so let's give it a console log deal works save it and now I go to the browser login submit the form and we get our still works so this is named authenticate functions let's start working on the sign up part of the functionality now so we're gonna go to our app.post sign up and we're going to get rid of the rest.send and inside the body we're going to make a passport dot authenticate we're going to name the function that gets triggered by this sign up and then our function async function error user info this is the same exact way that the other function that we did works here so we're just going to rest.redirect success for right now and then we're gonna go tackle this sign up portion so up here underneath or above your passport.use we can make one for sign up so we're going to do that here foreign and then this is also going to be a local strategy and there was actually something I was supposed to cover but didn't which is the custom naming of the oh wait maybe I did talk about that if I didn't anyway we need to we're going to be using email and password and passport uses username and password and so if you want to switch the username field out to like email or elephant or you know something other than username for the input you have to pass an object so we're going to say username field and it's going to be email and then password field is going to be password and so that allows us in curl or Postman or the browsers like HTML form to use email as the key as the key of the key value pair of the body that gets sent in the passport so you'll see what I mean in just a moment so here's our username and password object here and then we have our function so async and this is just like this we've named it email now so we'll just you know you name the parameter email password and done we already know how this function works we just learned about it here in this login section so I'm just going to go ahead and write the code for validating and signing up the user because all we're doing here is writing to a user's Json file which we're going to create by the way I'm going to go ahead and write this code out and I'll be back in a second okay so we added a few packages and we need we're pulling from a file that doesn't exist so we need to fix some of that so the first thing I'll do is scroll to the top and I'm gonna do cons users equals require dot slash users dot Json that'll be our users array and then cons to B Crypt equals require bcrypt and what else is there I think that might be everything okay I cannot find moduleusers.json that goes inside of our server folder so users.json okay and this is just going to be an empty array think that might cover everything can I find module B Crypt okay we have to install bcraft so let's close the server npm install decrypt and I also forgot uuid so npm install uuid okay and then my text editor pulled this dumb crypto package in and we're not using that so we're going to be using uuid so const and uuid has a weird way of importing things so we're going to do it's going to look like this V4 colon uuid V4 equals require uuid and I actually used this wrong in the actual package so I'm gonna find this and it's actually uuid V4 so that's how we create a new random generated string okay so save that and I think we are good we're actually not good down here in the sign up we need to use this these parentheses with the rec rest and next at the end to actually call the function otherwise it's not going to get ran so you're gonna you might have a really weird error if you don't do this it might be hard to roll nothing really happens so it's just kind of harder to bug so anyway wreck rest next this should call this function okay okay so let's actually talk about what's happening so the very first thing that happens is the user hits the post the post sign up form or the get the get endpoint when they visit the HTML form and then they fill out the form and it hits the post sign up route because that's what the form tells it to do so we're at this post sign up route it goes into the body it reaches this passport to authenticate signup function and so then just like what we've been doing it goes and finds the passport.us with the corresponding name in this case sign up and so here in the sign up we run this code it looks for a key value pair of email and a password which it has in the views the sign up form email and password as the names so that's why that works here and so we're going to name these appropriately I mean you could name this username if you want but that would be confusing so anyway we've got you email password and done here so now we try to do this block of code so basically we require that the user's password that they're trying to sign up with is greater than or equal to four and that they that the email exists so if the password is less than or equal to four characters or they don't have an email then we give them a user input error it says we give a false object for the user which would show up in the next function and then we also give provide an optional message it says your credentials do not match our criteria so this in this case it would go down here and we actually don't have anything so it would redirect us to success anyway which is not good but that's what we're doing so anyway it's gonna pass this here and then if if everything is correct you know and you would want to do more checks you'd want to make sure the email is unique and all that kind of stuff but assuming everything works and is valid and the email is unique we can do this block of code so we would we would take the existing password that they entered just the plain text password and we would stick it into bcrypt and it would do its magic and it would spit out a jumbled string and we would store that in hashed pass and because we're using a weight we have to have an async up here so we'd store this jumbled string into hashed pass and that would become the users like crypto encrypted password I guess and so now we're going to create a new user object we're going to take the user's email and we're going to take the password not the plain text password but the hashed password we're going to store it in this new user object and he's going to have he or she or whoever is going to have a uuid a a a unique string so once we have this new user object we push it into the users array this user's array is pulled in from the users.json which is just this MD array here and then we yeah so we add this to that array but that's not going to be permanent unless we write the file so here is where we use FS dot write file which is our file system which allows us to write to this this file on the hard drive so we write to the users.json we stringify the user's object the whole array of users and we stick it on to the users.json file and then we log that we have successfully updated the database and then we return done saying with the with the new user object and an optional message saying we signed them up successfully so if we just run this code and everything we haven't made a bug anywhere we should end up with a new entry in this users.json file so let's give it a try so everything is saved I'm gonna go to my form sign up oh I need to start the server npm start okay so now we'll go to sign up and I'm going to sign up with the user a I know it's not an email but that doesn't actually matter because we're not checking for verifying it's an email so we're going to hit sign up and it says success undefined so let's go here and if we look in our users.json we have a user and they have a jumbled string as a password and a unique ID and so so this all works and then let's go down here and if we have if we look at this success redirect route this is why we're getting undefined because we don't have a query parameter this time so let's actually use the info object that we have passed in we've passed it on both success and failure so this should work regardless so we can redirect the success with a question mark message equals info.message this is the same stuff that we covered in the login portion so let's save that let's try again sign up I'm gonna name the email B this time okay success signed up successfully so now let's set up the actual logging in because this sign up that we have here so far does not actually log us in so right now I just it just creates the user and then redirects us to the success so I'm gonna go ahead and comment this out for a second and we're going to first handle the error and if the user doesn't exist situations so we've already talked about that here so we're just going to basically do the same thing and actually I have code right here that I can just copy so I'm going to copy that paste it here so we got if error return next error you can do error. message or error doesn't matter and then if the user is not there then res rest stop redirect to the failed with a message the info.message that we pass in with this object here so that's good for that and then we want to actually log the user in also and so back in our Json web token section we were looking at how you create a token which is just a bunch of jumbled characters strung together but it has the user data inside of it and so we need to in order to log in with Json web token we need to have that data we need to have a token and we need to give it to the client so that's what we need to do now we need to create a token and so what we're going to do here is we're going to have we're going to make a body which is going to be an object and we're going to give it an ID of user dot underscore ID and an email of user.email and then we're going to create the token off of we're going to pass this body into where we're creating the token so I'm going to say token equals JWT DOT sign user is body and we're going to give it a secret of top secret so this will create a token for us I think I might have this wrong so let me just console.log the body just to make sure this is correct I don't think it's gonna affect the app at all though because it's just a demo but anyway we have this token and now we want to write it to a you know somewhere where we can store it and so in this app we're just gonna have a fake Json file so we already have this fake local here so we're gonna go ahead and use that I'm gonna get rid of this object though and make it empty again so we have this token we need to write it to that fake local.json file or you know in the real web app it would be you know local storage or something like that so we can do a weight a fast write file and we're going to write fakelocal.json sorry I made a typo here you would put a comma here and then this would go inside of here hopefully that's right and then get rid of that so my colon and then get rid of this comma so save and we have our right file so we're writing this to the fakelocal.json which is going to help us log in or authenticate with the JWT strategy so let's save this and make sure that things are looking good so I'm going to save and the server is good so we're going to go here and we're going to sign up sign up and I'm going to sign in with d this time so sign up oh and I forgot I think I forgot the rest dot redirect so this needs to be uncommented okay let's try it with did work though let's fix this let's just make an empty array go back to sign up and this time we can do user a again sign up signed up successfully if we look in here we get one user with a and if we look at our fake local we have an authorization Bearer token so this was handled by our I'll write file here so we're technically kind of logged in and let me take a look at this console log body because I think it may have yeah I did so that doesn't really affect the tutorial much but I would like to we'll make it the ID I just hope that works so we verified that the user entered the correct credentials and it did everything as our app requires it to do so unique email all that kind of stuff here we made sure passwords the correct length Etc and then once we've done that we successfully gave the user an authorization token via JWT and so now we finally need to verify that that token is valid anytime the user requests something from the server and so that's where the JWT strategy comes in so we're going to go to the top and we're going to initialize passport so you could do that maybe right here we'll say app.use passport dot initialize I honestly don't know what that does it's just used in pretty much everywhere so I'm gonna go ahead and add it in and then the next thing is we're going to use a JWT strategy to verify that this JWT that the user has is valid so we're going to have a function and a JWT strategy here so I'm going to go ahead and type this out and then we'll talk about it okay so we have our Json web token in the fake local.json and so now anytime we make a request to the server that requires the user to be logged in we can call this strategy we're not calling it yet but what we're going to need to call this and so we're going to call this Json web token strategy it's going to tell us or it's going to ask okay how do we get the Json web token which is the in our case we're going to use this get JWT function which pulls it out from the fake local file so it pulls out just the token this thing decodes it and then if it if it's invalid or anything like that then it doesn't even make it past this get JWT function but if it is at least valid then it will get passed to this function with the token and the next function here that we can run and so from here we're just going to hard code a success we're going to return a no issues and we're just going to pass the token.user which is the user object that was pulled out from the token so from here that's the end of the strategy and then it moves into the next function in the chain and so I'll show you I need to get my lingo correctly but I'm pretty sure it's middleware but I'm not I'm not actually sure the correct terminology for this but what we're going to do is we're going to make a secure route so see here is our secure route and let's so if this is secure then the user needs to be logged in to visit this route so if they're not logged in they can't access it so here's our function that does the rest.send secure route but before they get here we need to authenticate them so that's why we're going to put in this like quote-unquote middle function I think you would call it middleware but anyway we're gonna do passport dot authenticate and this one is called JWT and we're not using sessions so sessions are false and then that's it so if the user passes this Authentication then we can run this function and so what's going to happen is if it gets in here and it's able to pass done with a token.user or an object from the token then that means that it's successful and we can run the next function which is this in this case so I'm going to make this a little bit more descriptive I'm going to have a little bit more information here okay so inside of this function here we're logging out these functions or the well a function and then the return value of a function and then a rect.user erect.log in and erect.log out which are functions that passport should give us it's kind of like one of the magic things that magic that passport does for us so anyway but the most important thing is that we pass through this authentication and we can tell because it'll say rest dot send it'll it'll say welcome to this top secret place if we try to access this route okay so let's give it a try so I have my server on oh and we have we have to import JWT strategy so let's do that at the top of app we'll say const JWT strategy equals require passport JWT dot strategy and I already have JWT here so we're good and it looks like I made a typo hopefully that works okay so I cannot find module passport GWT let's install that npm install passport JWT okay and let's start the server back up okay so now let's attempt to log in or not log in but view the secure route so hit enter error login sessions require a session support maybe I forgot this in the passport.authenticate for the secure route oh whoops okay it's not sessions false it's session false so hopefully that fixes the problem let's try secure route again okay welcome to the top secret place a so we made it and we are authenticated and so then we get rect.is authenticated true rec.user which is the information or the object we passed from our JWT so this token so up here this token dot user should be the thing that is this user here so if I was to turn this into a fake user so test and I'll refresh hit enter refresh this turn the logs we get this rec.user is this misspelled word here that I that I put in so the rec Dot the this object to your pass from done is what ends up being the user and this is stuff that passport does for us now that we've covered the intro to this JWT strategy I'm going to go ahead and fill out the rest of this code and then we're going to look at each of the examples so hopefully I've written this clear enough to where each option makes sense but let's go ahead and take a look okay so I've got a an example for each possible scenario that can happen in this GWT strategy so let's briefly take a look at the whole flow so just from the very start so the very first thing that happens is the user hits the login or sign up endpoint so here's a login or sign up and then when they post the form or they submit the form it will whether depending on which one they pick let's find the login one post login it sends them through the authenticate login strategy or the the yeah the strategy name that we named login so in this case that is this one here because we named it login up here so then it goes through here oh okay well we have this error that we need to fix but let's assume that this error is not there and it returns done with a null error and a user so this user gets passed to The Next Step which is the Callback of this post login to this authenticate login so it passes the user here and then we get this user and it logs us in and assuming we have the JWT so I guess I should have used the the top the sign up one but if we were in the sign up it would it would make an authorization Bearer token it would stick it in the fake local.json file and then we would have a JWT in the fake local Json file or if we were had a web browser and a front end it would be in local storage or something like that so we have this authorization token that we need to check to to kind of like unlock the door so we are going to now on every request that is locked by this JWT strategy it's going to take the token and and verify it so I think we have a strategy or a route secure out here so let's say we try to hit the secure route it's going to trigger the JWT authentication strategy which is right here and then the very first thing is it calls this function to get the Json web token from by using this function so in this situation we just pull it out of the fake author the fake local file and we get the JWT and then if it succeeds through here then we make it into this function so let's talk about all the different situations so the first one is that we don't even make it through make it to this function because of a no token or an invalid token so let's give that a try so I'm going to clear out the fake local file and I'm going to open up a web browser it's a new day for me so I have to start over okay so secure route it says unauthorized because I do not have a token and so if we were to log in so let's say well let's just log in oh I think I have to sign up sign up ABC okay so I'm successfully signed up and we have the bearer token so now if we visit the secure ad it's going to work okay welcome to the secret place ABC but if I make this token invalid by changing a character and I try to hit it again it's going to be unauthorized so that is both no token and an unauthorized token we don't even make it into this into this in JWT Strat this this function here so it just kicks us out once we get to and get JWT so anyway the next step is to try with a token error so if we have and a username or email with the name of token error it's going to simulate an application error so this could be something like I don't know maybe the the database disconnects or I don't know but I actually don't know a situation where this would happen off the top of my head but if we need to throw an error this this is you would pass it as the first argument of the done so to simulate that we're we've made an if conditional if it's if the email is token error then it will run this block here so let's give it a try so I hope that we have a log out if we don't let's just I've forgotten what I've done and what I haven't done so far so anyway we have this empty fake local so that means we're logged out so let's sign up with what was it token error token error so a username of token error all lower case we'll sign up we've successfully signed up but we can't view the secure route because it triggers an error so we've simulated an application error in the JWT strategy for users with the name of token error so this is the latest application error and you know you can hide the stack Trace by doing error.message instead of just error or you know whatever you want to do and because it's an application error it's okay to just have it route or just display the error we could say that we logged the error or something like that however you want to handle error handling so the other situation is where there's an empty token so maybe the user it's like the goal is to pull out a user from the token but what if somehow there's no user in the token well we can simulate that by passing it making an a user or an email with the email of empty token and what you would do this situation is you would pass false as the user so let's give it a try with empty token once again we're going to make an empty object here and we're going to sign up with empty token sign up Okay so we've signed up successfully but if we try to view the secure route we get unauthorized and that's because we didn't pass a user we passed false and the way that JWT strategy or passport knows that the authentication was successful is to pass a user to the function and so we've already talked about logging in but our current example code here doesn't kind of demonstrate all the examples nor is it Dynamic so I'm gonna go ahead and fix this login strategy to be more friendly also most importantly is pay close attention to the fact that this new local strategy we were using the username and password and I already showed you in the sign up this username field how you can switch it to email so that's not going to work until we add this little object here so I'm going to add that in that'll be really important so username field will be email from now on and then password field will be password and then we have to change that in the login form as well so anyway okay so this is the entire working code for the finished example of the login so we are now taking an email input instead of a username so we'll have to switch that but once we do if the email is an app error then it will throw an error and then it will handle it down here otherwise if it fi it'll search the database for the user that the user entered by the email and then if it doesn't find the user it'll send a false object for the user and with a message user not found if it does find the user it will compare the plain text password they entered it will stick it in decrypt it'll jumble it into the bcrypt hash hashed password and compare it against the hash password in the database and then return a Boolean on whether or not it matches if the password does not match it will say that there's invalid credentials and if it finally makes you through all that then it's a successful login it'll pass the user object with an optional message object that says hey congrats you logged in so that's pretty much the login strategy we've all covered all of this separately so there's nothing really new here so we do need to fix the email and well the password's already there but we need to fix the input so in the login it's no longer username it is email and so the name is the most important part but I guess it's probably a good idea to change those as well something I didn't do in the final GitHub repo or my blog post but anyway email and so now this login system should work and we can I guess we can go ahead and test it oh wait am I logged in let me log out and see what users we have I think I have a user a ABC okay I do have a user named a so let's log in as a log in a okay hey congrats you were logged in okay so the login functionality is working and then if we're logged in we should be able to hit the secure route and it looks like we can't and that is because sure our login like our login local strategy works and we've verified that the user has entered the proper credentials like their password batches and everything but if you go down to the app.post what happens after this is it actually has to log the user in currently we're just sending the them to a success page so we need to actually log the user in so like here in the sign up page in the sign up endpoint on the post submission we actually write to the off the fake local.json file with a bearer token so we create the token and then we write it to the fake local.json we just logged in but we don't have anything here in this fake local.json so we have to actually make the login system work here so I'm going to go ahead and write this out again this is nothing new this is just get creating and signing a token and then storing it in fake local.json okay so the code that I wrote here basically just some console logs and then a check to see what we actually got back from that previous function that we were working on so if we pass an error then it's going to error out either with or without the call stack or the yeah the call stack and then if there's no user passed or returned then redirect to the field route with an info.message property stored in the query parameters and this was an issue that I had spent a lot of time on it's just figuring out how this rack.login works in the context of using Json web tokens and it appears that they just don't do anything I thought at one point that the rack.login would add the or would manage the state of whether or not you're authenticated and in like the rec.is authenticated and that it would create the user object for you with the rec.user but if you try to look at the index route like if you were to put some console logs in here I'm not seeing the rack.user or a correct is authenticated state so it doesn't seem like it's being managed properly I don't know if I'm just doing something wrong or what exactly the problem is or if it just doesn't work so I don't I don't know that's still something that I need to figure out but so far off works and so I haven't had any issues so I'm that's why I'm fine with it for now but just a heads up so I don't know exactly the purpose of rec.login if it even has one and that includes the rec.logout as well because with the JWT strategy you're finding out if you're logged in by the the bearer to the toe the JWT inside of local storage or whatever and so if you do something like this and you make it into the secure route you do have access to the is authenticated but this and the rec.user only seem to work when it passes through the JWT strategy so that's kind of a couple of points just to kind of like be aware of so I don't know I don't know if rack.login does anything in this situation so anyway we were at the login post request and so we take the body we store it in this body const or we take the user's information store it in the body const and then we sign a token with the user's details and a secret key we store it in token and then we write the token to the Json file then we redirect to the success page and so now we are finally logged in or we have the information to prove that we're logged in if we were to actually run the login code so so let's make sure that this works and then the last thing is this rest Us in so if we weren't returning anything here or here well actually this one needs a return on it but if we weren't doing these then you could actually get to the next function here because that's how Express works but anyway let's try to log in now so log in with the user of a hit login oops and I made a mistake so if you look at the terminal it says error is not defined and that's because I messed up this it says console.log error when it should be error so I have to fix those as well error error I think that is everything so we'll save try to log in again so with a okay it looks like it worked and then let's make sure secure route works okay welcome to the top secret place a and again what I was talking about earlier this recta is authenticated true is here the rack.user is automatically added so it's kind of interesting the disconnect there and I'm not sure exactly what's going on but off works so I'm happy with that so I think we're pretty much done with this file I just want to clean up a few quick things so the first thing is the secure route and I want to add some comments here to just kind of describe the different options and the different ways to test this thing to make sure it works fully I also want to make sure we set up an official logout route because right now it just sends logged out so let's actually make it log out and so logging out I mean this is the same issue I don't know what the rec.login does wait in the context of JWT and it seems like the log out doesn't really do anything either so really in JWT what it means to be logged out is to either have an expired invalid or no JWT at all so all we have to do to simulate the logout is to get rid of the token in the fake local.json so we've already shown you how to write a file so it's just a tiny bit different we're just writing an empty string to that file and then after the file has been written we can just redirect and so I'm going to redirect to the login page and that's it and there is another option with the rec.log out I'm going to include it in the code in the source code when I push to GitHub so you can take a look at that or you can just look at this comment here but again I don't think it does anything so I'm just gonna not have you watch me type it I don't want to type this out so okay so we're pretty much done with this file there's only one more thing that I want to show you in the entire tutorial and this requires us to make one more new file so we're going to make a cons secure routes and we're going to require a new file secure route and then we're going to down well right underneath the initialize we'll say app.use slash user comma secure routes which will prepend the slashed user before any of the routes that are in here and then in this file we'll end up using the router instead of app because it's a new file so we're going to make this new file secure routes.js and then in here I'm just going to go ahead and type a bunch of stuff out and then we'll talk about it okay so the difference with this file is that we have a router.all so every route within this section is going to pass through this and it allows us to have a like a callback like a custom callback with the JWT strategy as well so you'll notice that in the other one in the app the secure route it has us using the JWT strategy like as a parameter of the get route but in here this is an action this authenticate is in the body and so because it's in the body we can have this recrest and next and this allows us a little bit more control so basically we have a couple different options so the first one is don't even make it through the get JWT function so this would either be no token or invalid token and this will have this will show up in the info object here or this info parameter and so let's kind of simulate that I think we have a token here already working so let's try the user slash profile route so if we try this we get the Json and that's because after in the secure routes after you go through this then it hits the next function which will hit this profile so when we try user profile it hits this all first and then it goes into here and then it prints out the Json so anyway that's how that works but we can simulate an error with this info if we have an invalid or not present JWT so let's try to make an invalid one so I changed the authorization token to be invalid now I refresh and we get invalid algorithm so that's the error message which gets passed to the secure routes function in the info object and so if we so it goes from the JWT it doesn't even make it through here and because of that because we're trying to go through this router.all and it doesn't pass this authentication it sends this specific error into the info so hopefully that makes sense and then we have control we can redirect or we can error so I chose to rest.send the just the error message which is what happened here and then if there's no token then it will do the same thing but it will give us a different error save refresh and it says no off token so we have a little bit more control and flexibility with this strategy so far the next thing is an application error and in this case we are using this is the same situation using the token error as the user so in here when we try to log in it we'll see if the user's email is token error it will throw an error so if an error is thrown in here with this other strategy that we're doing now we pass in an error as the first parameter to done then that will get stored in the error object which is the first parameter here so let's give that a try so we're going to sign in with the token error user so sign wait log in and token error hit login and it says we've logged in but if we try to hit the user slash profile route it'll say something bad happening happened we've simulated an application error and that is because that's the error that gets hap that happens here and then it gets passed in as the first parameter and then here's the first parameter error so if error then we log this and we send the error.message the next thing is if the user is not is not pulled out of the token so we can try that with I forget which one it is but let's just go look so empty token so let's try getting rid of the token we'll sign in again with the empty token user okay so we're going to log in with empty token hit login and it says we are logged in but if we try to hit the user profile it'll give us this error so we're simulating empty false user being decoded from the token and then finally I think finally yeah if we have a successful user then we can send it to the next function because that is the only situation where they pass the authentication and then they get into here and then do the instructions in this route here and the weird thing is that we lose access that is authenticated and the dot user so if you comment this out then these probably hopefully won't show up in here so we have to manually set these now and so that kind of makes me wonder if this is incorrect but I think it does work because it just gives us full control over what we want to do that's my thought at least so anyway let's simulate this with a real working user so we're going to go to fake local log out and then log in with the user a and then we'll hit user profile and it works and once again that's because the whole process we get a valid user here then we call next which because we have this well because we're calling next you know this is you could call next with an error I think and it would take us to this next function here and you can do whatever you want but we only want to send it to the next function if the user exists and so that's where we store the user on the rec object and then we have access to it in the next function which in this case is the profile so hopefully all of that makes sense I think that's it for this tutorial there's actually one more quick thing and that is you'll notice that the authenticate functions here we're we're using like a custom callback so this post login here it has this authenticate login function is in the body and it has a Rec and next we're calling this function it's in the body and then we're also able to add another function after this but the other alternative that I did not cover in this video but I did Cover in the sessions version of passport is that there's another option which is to pass the authenticate function as a parameter of the the route so here we've got like this this this function and then this is the end of the line so and then it's got these failure redirect success redirect and things and I do know I don't recommend doing it this way I like the custom callback strategy a lot better because you have a lot more control and I don't think there's any any like benefits of doing the other way but if you want to understand this other way then go check out the passport sessions tutorial thank you for watching the video please subscribe to our Channel and click the Bell to be notified when we release new tutorials also be sure to like the video and leave a comment to let us know what you think lastly check out the link or links in the description we usually create a blog post to go with the tutorial and we might have a newsletter or course or something to check out as well have a good day and I'll see you next time
Info
Channel: TruthSeekers
Views: 7,638
Rating: undefined out of 5
Keywords: Passport JWT Tutorial, Passport.js JWT Tutorial, Passport and JSON Web Tokens, Passport and JWT for beginners, JWT Tutorial, Learn Passport & JWT
Id: Tau0ZMJ4aR0
Channel Id: undefined
Length: 92min 22sec (5542 seconds)
Published: Mon Nov 07 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.