User Authentication | Node.js API From Scratch Using TypeScript, Express And MongoDB #2

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hello everyone and welcome back to the second episode of my node.js from scratch series uh today we're going to be looking at adding fairly basic username password or in our case it'll be email authentication using json web tokens so we'll get into it the first thing we're going to do is install a couple of libraries so first thing we need we'll do mpmi and we need json web token so this will be the library that actually creates our json web token then we need bcrypt which is a hashing library so we'll hash the user's password when we start in the database so we install them and then we've got a couple of dev dependencies as well um so we'll do mpmi dash d and i'm just gonna copy these as usual um i did it for my last video i'm gonna which by the way we're using as a base for this so if you haven't watched the last video um you don't have to i guess technically you can just go onto my github and there'll be a link to that in the description and get the um node.js from scratch repository and download that and we're going to be using that as the base for this so the two packages we need are types json web token and types slash b crypt so as i was saying i'll leave these in the description like i did for the last video and just to make it a bit easier so you can just copy and paste them from the description without having to write them all out and although in this video there are actually there are no more packages to install so a lot less than the last video so now that those two are installed and i'm gonna have to go through and fix these by the way it doesn't look like there's too much of an issue at the moment with these they're just warnings but yeah i'll have to have a look at some point so the first thing we want to do is just edit our end files so what json web tokens need in order to be able to verify that they're legit is um a secret so we can create a new entry in our.example so this is just for developers that might be like pulling our code base and just to give people an idea of what um what end variables they'll need um so we'll add that to end when this is the end that actually gets used and we'll just call it secret i'll just give it the value of secret and one thing that we need to do is add that to our validate end to make sure that it exists so it's basically the same as like these string ones so we'll just add a new one for string but instead of underscore user we'll do jwt underscore secret and that's fine so the next thing we'll do is we'll basically add all the code needed to create tokens so the first thing we need is the interface so tokens will have their own interface um just to let us know what's actually starred in the token which in what information um because that's one of the good things about json web tokens is you can store information in them so token dot interface dot ts okay so that's going to contain the interface for token so we'll do import schema from mongoose here we go we'll do interface token extends object and we'll do the id and this is why we imported the schema because this is going to be the user's id which is technically um an object id from mongoose so we'll do types dot object id and then we want expires in and then that's just going to be a number and then we need to export that so expires in is going to tell us when the token actually expires so that when it gets passed through if it's expired um we'll mark it as well we'll basically just walk let the user do anything on the api we'll just say that it's unauthenticated and what you can do with that is you can make the user re-authenticate on the front end and so if we send a request back saying like status 401 unauthenticated um your front end will say okay you need to re-authenticate before you can make the request and then you can redirect them to the login page for example so after we've added that interface we have another file in utils that we need to add which is called token.ts and this is going to have the actual functionality for verifying and creating a token so we'll do import jwt from json web token then we'll do import user now we don't actually have user yet um i just want to get the token functionality out of the way and then a little bit later on we'll add all the user stuff so um we'll import user it doesn't exist yet but we'll do it'll be located in at slash resources slash user slash user dot interface and then we'll do import token from utils slash interfaces slash token interface and then the first thing we're going to do is create the create token function so if we do export const create token equals and then a user will be passed into this so we have type user and it's going to return a string and what we'll do is just do return jwt dot sign and then the id will be the user's id so we'll do user dot underscore id then we'll do process dot end dot jwt underscore secret so the second parameter for this sign is the secret and that it's going to be expecting the type of jwt dot secret so we'll do that now so we just do as jwt dot secret and then the third argument is gonna be um some options and for us we're just gonna use expires in and it's sort of up to you how long you leave a token um i guess the longer you leave it the more unsecure that is so we're just going to do one day which you can just do one lowercase d to represent that so [Music] yeah this j j w t dot secret is going to be expecting either a string a buffer or an object with these values in for us we're just passing in string so next thing we'll do export const again this is just so with that we can import them um by themselves so that we don't have to import both these functions at once so basically instead of doing like import we can basically do this import and create token like that from and then this file otherwise we would have to do like import i don't know token and then do something like token. create token so i think it just looks a lot nicer if we can import them separately but we will also add a default export that lets us export both functions but because of the way we're using them we're not going to use them in the same file verify token gets used on our authenticated middleware which we'll add a little bit later on so we'll do const verify token so this is where we actually verify that this is an actual token that we've signed and also hasn't expired so verify token this is going to be async and it's going to be arrow function with the parameters of token that's just going to be a string okay and then we need to do the return type which is going to be promise and it's going to be either jwt dot verify errors or can't get the right one there or it's going to be a token all being well it'll be a token so we'll just do another return it's gonna be a new promise so we're actually doing a promise here and we'll do resolve or reject that um that added an import here get rid of that so inside the promise we'll do jwt dot verify and this will accept token the secret so process dot end dot jwt underscore secret and that again will be as jwt dot secret and the third parameter is a callback and that will be that will accept it so i did it as a as if it was an object but it's a callback so it does it's a function error or payload there we go so if it's an error we want to return reject return reject and we'll put that error in that we'll pass the error through or we want to resolve it with the payload as token so that we return the correct type and that is looks like it's done although it's throwing an error i think we've missed there we go so there we go reformatted and then the last thing we'll do for there for this is export default and then we'll just export both of these functions all right so we can use them in the sort of normal method where we just we use a default export but we can also just import them all import both of them individually which will be quite useful for us because we don't want to use i don't think there's going to be ever a scenario where we use verifying creating the the same file at least for us anyway um maybe depending on how some people write their code they might have it on the same file but i think we're only using verify in the middleware which we'll create in a little bit after we've created all the user logic so yeah still getting an error here but that's just because we haven't created this user so we'll do all that now um similar to our post stuff you can see here we need a new folder called user and inside that folder we'll add all of the necessary files so the user controller dot ts user model.ts user interface dot ts user service dot ts and something i've been thinking about is creating some kind of command line tool for this because obviously it's quite involved having to create all these files every time you create a resource so that could be something i do in a future episode i'm looking at command line tools in in our code base but for now we'll just do it manually um first thing we'll do is create the interface so user interface is going to be an import document from mongoose and like our post uh interface that we made in the last video it's going to extend the document so we'll do export export default interface user and it's going to extend document so we're going to have an email which can be a string oh you can't type as usual name string password also going to be a string and then rule the last thing we need is a function that will actually be adding to the model directly and it's called is valid password and that will accept password not be a type string and the return for that will be a promise and it'll either be an error or a boolean so true or false whether depending on whether the authentication is successful so now the interface is done we can do the model so we'll do import schema and model and that'll be from the mongoose library impart be crypt if i can spell it be crypt there we go from bcrypt and then we'll import user which we actually have now from from at resources user user interface all right i'll do a const user schema it's equal to new schema oh and you open that there we go so we're gonna have a name that's gonna be a type string oh capital letters for this type um then required is going to be true because we want them to have a name then we'll do email type string again required true and then we've got a couple more things for this one so we want the email to be unique um the reason why is we're going to identify users by email so when they log in we're gonna find the user that's trying to log in by their email so it has to be unique so we'll do unique true and then the other thing we're gonna do is trim now i don't know how necessary this is but trim will basically get rid of white space on the end so i don't want the possibility of mongoose storing or mongodb storing the email with white space just in case we do a comparison and it's like oh this isn't the same just because there's some white space at the end i don't know if it would do that but i just just sort of precautionary um to do that so password now password is an interesting one the type's gonna be string as usual whether it's required or not is a difficult one um because if you're gonna be using oauth 2 in the future or some kind of some kind of like authentication like google authentication or something like that you probably won't be using a password um but you may also just want a password by default so that like if they signed up with google authentication you may still ask them to add a password just in case their google authentication file fails um but even then nowadays i think using emails for that is actually a better option um i think i don't know what they call it but yeah you can basically send an email to someone's account and then they can use that to sign in with um i think it's called like email magic link or something so for us we're gonna leave it we're not gonna say it's required um but it it's really up to you uh yeah like i said if you're using oauth you'll you won't be starring a password you'll be starring keys for the oauth stuff so yeah that's a that'll come down to you and what you're doing if you're just using if you're only ever going to use like username and password authentication making it required is is that that would make sense um and then the last thing would be timestamps set that to true and there we go so now we just need to export this export default and i've just remembered i've forgot all the other functions hang on a minute we'll just export and then we'll add this there's a couple of functions to add before we done with this model so that's going to be user and that's going to be called user and then we'll pass in the user schema all right perfect so now there's a couple of things we need to do um before we done with this model the first one is we need to add something that will hash the password before the user is added to the database now thankfully mongoose provides stuff for that so we do use a schema um i don't know if they call it hooks in mongoose i assume they do um there's a hook called pre and inside of that we can target save and i think we need to add user here so it knows what type we're dealing with so we'll do an async function and then we can pass in next so this is similar to like how um express works in the sense that next will keep going a little i don't know where it's going to when it does next actually because it's not going to another middleware i guess it's just continuing the code um so actually like saving serving the um the document i assume that's what it's doing so what we can do is we can do a check to see if the field is actually being modified so we can do if it's modified if it's not modified sorry we'll return next which will carry on serving it to the database otherwise we want to hash the password and then store the hash as a password so we'll do await bcrypt dot hash and then we'll pass in this this dot password and then the next option is the amount of salt round you want it to do now this is up to you um the last i heard 10 is a good number so we'll go with 10 basically the more you do the the more secure the password will be um but at the same time the more you do the longer it'll take to hash the password so you sort of have to find a balance between a secure password but then also not you know taking a long time when you're um and a lot of processing power hashing that password so 10 seems to be a reasonable number i'm not an expert on the matter but i go with 10 you can sort of make your own mind up maybe do some research on that if you want to and then we'll we'll set this.password equals to hash and then next and what it'll do is it'll save this.password um so it won't save the original password it'll save this hash which is good and then one last thing we're going to add the method that we defined in our interface so methods dot is valid is valid password is going to be equal to an async function and that's going to accept password which will be a string and it's going to return a promise of either error or boolean so as you can see it's basically the exact same as how we defined it in i'm going to put a semicolon in our interface and now we're just adding it so we'll just do quite simple function we just do a return await be crypt dot compare and then what we do is we pass in the password and then we pass in this dot password so like i said i'm not an expert on the matter but i assume what this is doing is it will [Music] is that fine okay that is fine um it's just my spelling mistakes causing problems again what it will do is will accept password it'll hash that and then compare it to the hash that we have saved in the database i think that's how it works and then and then it compares whether they're actually the same or not again i'm not an expert on the matter but um i think that's how it works but do you take what i say with a pinch of salt um so the next thing we'll do is add the validation stuff so once again um like we did for posts in the last episode um validation i'm actually going to create two validation schemas this time one for register and one for logging and so these are important just to make sure that not only to make sure that they have exactly what they need to make the request possible to execute but also um with the validation middleware it will return all of the fields that aren't correct so it's quite useful for your front end to um basically for user experience so they can see exactly what's gone wrong which is quite good so we'll import joy from joy and we'll do const register is going to be equal to joi dot object and inside the object name and we're going to do joy dot string so now we're going to do something i haven't used yet which is use max and this will make it so that when something gets submitted and it has a name on it um it has to be less than 30 characters so that's quite it's useful just for making sure people don't like add ridiculous length names um you know because it you won't be able to display it on your website if it's like 500 characters long so we limited 30 characters fairly reasonable um then we do email we'll do joy dot string and then we'll do another one that we haven't used yet which is email and that'll check that it's actually a like an actual email um i'm trying to think of what the the standard is but there's a standard for what an email essentially what an email should look like and what it should contain um and it'll check that does it say it on here to be a valid okay it doesn't say what exactly what it checks but it'll make sure it's got like the at and then the domain and stuff and then we'll do required and then last thing for register we'll do password now there's a lot you can do with passwords um we're not adding a confirm password on here but one of the things i've done before i'm pretty sure or by the way we're using min here to make sure that it's got a minimum of six characters let's quickly point that out and one of the things i've done before and i think you can you can do it quite easily in enjoying i think i've done it in a code base before you can pass in another field for confirm password and then you can actually compare the password and the confirm password together and make sure that they're all the same and that's quite useful i know a lot of websites obviously do confirm password um just to make sure you've got the right password i guess um so that is something you can do enjoy we're not going to be doing it now just to keep things simple but um at some point if people are interested in that i can go over it and so we'll just do the login schema now so we've got jy.object and then in this one we only need to pass in the email and the password and there doesn't really need to be many checks on this because we're not actually storing anything in the database so we don't need to make sure that the [Music] well actually that we could probably make sure that the email is a valid email um that's not too important because that's going to be what we're going to be doing is using the email to find a user in the database so it's not too important although we could probably still do that so if we do joi.string and then dot email and that should be fine um and then [Music] password we don't really need to do the same checks as we did before like making sure that it's got a minimum of six characters so we'll just do required like that because again we're not starting anything and we're just comparing the password with the whatever's hashed in the database then we'll do export default and we'll export both of these and in our case we are actually like i try to think the um token expo for the was it for yeah for oh this is still kicking up a fuss i don't know why i'm gonna make resources user user.interface okay there we go so this one we obviously did the individual exports as well um in our validation we are actually going to be using these in the same file so there's probably no point actually doing individual exports there's no real benefit to it in this case so we'll just do this we'll just export both of them in the same um default export and then we can use them basically like this we do the validate.create in our case it'll be validate.login and validate.register so now we will add the service code so this service code will handle all the actual logic around um basically creating a user and logging them in and stuff so we'll do an import here import user model from resources slash user slash and then model will impart token from utils slash token create a class called user service and the first thing we'll do is put a space here um create a private method or not method sorry property called user i'm passing user model again this is completely up to you because essentially this private user and user model are the exact same thing um i just like the way it looks when you do this.user i think it looks better um but that's completely subjective so you can use user model if you want um we'll do a comment here just to let any developers know what the function is actually doing although in this case it'll be quite obvious but we'll just do register a new user and the function itself is going to be called register that's what that's why it's obvious um so do public async register and this is going to accept um basically everything we need to create a user so it's going to be name and that's going to be a string email and again that's going to be a string password string and then roll which is also going to be a string now we're going to hand code the rule and we'll get to it a little bit later but yeah we'll just hard code that role when we pass it in we're not actually going to let the user submit a role which is why it's not in the validation so that's going to return a promise of either a string which is going to be the token actually we're returning the access token on this the actual json web token that gets created or an error if things go wrong so so this will do a try catch and we'll do const user is equal to await this dot user dot create and we'll pass in basically everything that we've passed through so we'll do name email password and rule and then we'll do const access token equal to token dot create okay and then we'll return access token and then in the controller hang on a minute one argument oh yeah sorry i forgot about that um you have to pass in the user because we're obviously using the user um as part of the token creation just to get the id of the user so we can store it and the error we're gonna throw a new error and we'll do something like enable unable to create user something like that um what you want to pass in is up to you there we go that's fine i'm going to do another function and you probably guessed already but this is going to be [Music] a login function so we'll just put attempt to in a user so we'll do public async login scroll down a bit and okay that's fine and in this case we're only going to be passing in the email and the password because that's all we need to log in a user and then it's going to return either a string or an error and we're essentially doing the same thing we did with the register except instead of creating a user we're just trying to find the user so what is wrong with this logging function implementation is missing okay it's fine i think my things just lagging behind a little bit so it's giving me errors that are actually um they've actually already been fixed so yeah that's just because we're not returning anything i think so if we do try catch again and inside of the try we'll do const user equals two and then we'll do a weight this dot user dot find one and we'll just pass in the email so it's just going to find one by the email and what we want to do is check that that user actually exists so if it doesn't exist we'll throw an error and we'll say something like unable to find user with that email address okay that should be fine um what have i done here i'm getting a lot of errors here oh okay i know what i'm missing oh my god is that all i'm missing is that the only reason why it's yeah okay is that fine yeah so i missed the promise and i just made the the entire uh function just yeah just messed up right so that's fine um yeah just make sure you put the promise there as well um okay so yeah basically we checking if the user exists if it doesn't exist we're gonna pass through an error and this is why in our controller we're actually going to use the errors that are passed through from this function because they're more descriptive so if the user doesn't exist or pass through the error and that it doesn't exist because there's no user with that email address and then we're going to do if and we're going to do an aware inside this if user dot is valid password i'm going to pass in the password and this will return a boolean so if it's true we want to return token dot create token and we'll pass in the user so it can create the token otherwise we're going to throw another error and we're just going to say wrong credentials given and then inside of this error we're just going to say we're going to be a bit more non-specific because we don't really know where it failed here so we'll just say unable to login user or something along though we could probably do something like wrong credentials given uh we'll keep it as this for now but yeah that's probably as non-specific as we can get i think um without being completely useless i suppose and then our final thing oh that's it for now actually yeah that's it i was going to say that you might have added one for actually getting the user but i just remembered something that um we actually we're going to stall the user in rec.user so when we create the endpoint to get the user we're just going to use whatever's in rec.use essentially which reminds me um we need to create um a type declaration which is interesting so our type definition so inside of a folder called definitions we're going to create a file called custom dot custom.d.t.s so custom.definition.ts and what this is going to do is so we're going to import user from at resources user slash interface and what this is going to do is add um our user object to the express um interface or the request interface that express provides um the reason being is it'll throw errors otherwise because it'll say something like our user doesn't exist on request on type request um obviously we're adding user to the type request so we kind of need to reflect that within the types so we'll do namespace express so declare global namespace express and then export interface request and then we do user and the user type user so what this will do is it'll just add a user to the request interface um which is really useful and it stops a lot of problems so otherwise we'll just get errors all over the place saying that like our user doesn't exist um but we know that it does so we need to reflect that here and now that that's done we can get on with creating the controller so we'll import quite a lot of things as usual so router request response and next function i'm getting better at typing without looking at my keyboard by the way that's why i'm not doing too badly but the mic is still in in the way of my keyboard um the next thing we'll do is import controller from i'll be at utils interfaces controller interface and we'll import http exception from and that's utils again that'll be exceptions and http.exception will import validation middleware actually knows what i wanted what what i want to type now based on i guess the other controller that i've got um so it knows it knows what to do which is quite useful um at middleware oh at slash middleware sorry and then validation middleware will impart okay maybe i'm not good at typing we'll import validate from and that'll be at resources user and then validation we'll import user service from at resources user user service and what's wrong with this is it just telling me that this has no oh good job i looked at that error no default export we forgot to do this default user user service there we go okay i should shut it up yeah i i keep forgetting to export stuff for some reason um the next thing we're gonna import it doesn't actually exist yet which is quite common in these videos now i think that something doesn't exist that we're using um but we'll add it in a little bit i think that'll be the last thing we add before we actually start testing our api so it's going to be in at middleware authenticated authenticated dot middleware so this will be a new middleware way and just detect check that the user exists and then also attach the user to rec.user um so we can use it throughout the application so we'll do class user controller implements controller and we'll do public path is going to be equal to slash users the public router router is going to be equal to router and then we'll do private user service it's going to be equal to new user service so we're just going to use the service we're just going to instantiate that that's fine and we'll add a constructor which will initialize the roots so do this dot initialize i spell that wrong initialize roots there we go and then a private function for initialize roots and that's going to return void and we can add all the stuff now so if we just do this.router.post and this is going to be for register so we'll do this dot path um slash register so we're using string interpolation here to do this this dot path slash register and we'll pass in the validation middleware and we'll do validate dot register and then the last thing we'll pass in this dot register which will be the register function that we'll define in a little bit of time this.router.post and this will be for login we'll do oh and curly braces this dot path and slash login how you name your authentication roots is up to you um i think slash user slash register makes sense although it could be like slash auth maybe um but in the case we're just gonna use this path that we've defined up here so we'll do validation middleware again and that'll be validate dot login and then this dot log in which will will be the the login function that would define in a little bit and the last one is going to be a get request this dot root dot get and this will just be the path so we'll do i keep doing p instead of the curly braces this dot path and this will accept a middle where the authenticated middleware which we'll define in a little bit of time and this dot get user which again will be the function that we'll define in a little bit so the first thing the first function we'll do is the register function so we'll do private register and i'll be equal to async and it will accept rec which will be request res which will be response and next which will be next function and the return type promise oh and that'll be either a response or avoid and then arrow function and there we go so we do a try catch and we get all the values we need for the register function so we do const name email and password and we'll get these from rec.body so then we'll do const token and that's gonna be equal to await this dot user service dot register and that's gonna accept name email password and i mentioned earlier that we're gonna hard code the rule we'll do we'll just hard code user and the idea here is that anybody that uses this register um this register endpoint will be a normal user so we'll we'll just pass in user here if let's say in the future you have an admin dashboard and you want to add more admins or like you know more staff something like that um you could create a separate endpoint that can only be accessed by admins let's say or whatever users are able to add users and then you could pass in a role or something like that so like add admin or customer support or something like that um but for this one we'll just hard code user because only users will um only people that are registering to become a user will use this endpoint well a confusing sentence um and then we'll do res.status 201 to indicate that a resource is being created and then we'll do json and we'll just pass through the token um depending on what how you do it um you may not want to pass through a talk because essentially passing through a token will log them in as well um depending on how you do it you may just want to say that you may just want to do like you may just want to send status essentially and say 201 the user's been created and then just leave it there and then make them log in on the front end um it's up to you it's from a user experience point of view it's a bit annoying when you register and then have to log in again essentially because you you've registered you've already passed in you like your username and email and password and stuff and then you have to again so it's up to you how you handle that um for me personally i i just log them in instantly but again depends depends how you want to do it up to you um so if that all fails we'll do a http exception and we will just use um so we'll do 400 of the error code and we'll just use the message from um the service essentially so the service will tell us if something's gone wrong because of like an email maybe an email already exists or something in which case we'll pass that back through to the user um it's up to you how you do that you could just send through just a message like [Music] unable to create user something like that but we'll just use error.message and then get a specific error message from the service and the next thing we'll do is create the login function um our endpoint so we do async rec request res response and you know what might be good actually if um yeah i could have something that like auto completes so if i wanted to create like a function for an express endpoint it would just automatically create all of this stuff essentially just minus minus this part it would just create like this for me that would be quite useful because they're essentially always the same except this part in the middle is a bit different um that would be good just another idea that i might work on at some point in the future um response or avoid as the return types and then the arrow for the arrow function and then we do attack try catch and this one is just going to be using email and password and that's going to be we're going to get that from rec.body and then we'll do cons token is equal to await this dot user service dot login and we'll pass in the email and the password and then we'll just do res.status and we'll do 200 this time just to indicate that it's been successful nothing's technically been created besides the um json web token which i don't think is enough to justify a 201 status and then in the error um basically the same as what we did before we'll do next new http exception and we'll just pass through well we'll do 400 and then pass through the error dot message there we go and last but not least we'll do the get user function which will just return the user um this is quite useful just generally but um specifically if you're using like um some kind of single page application i know a lot of them every time you every time a user goes onto the new page they'll request the user again to make sure that it's always up to date so this is quite useful for that so we'll do request res response next as usual oh not next function um next next function and then the return type it's gonna be either response or voice a little bit different this time because we're not actually doing it in a try catch or anything like that we're we're just returning the user essentially um or throwing an error so what we need what we want to do is check that the user actually exists so if there's no user we want to throw an arrow we're going to do next we'll do new http exceptions so we're not actually throwing the error sorry we're just um doing next with a http exception similar to how we do it up here and we'll do four or four in this case and then something like no logged in user or user not found it's up to you what you what you put there and if we find something we'll just do res.stairs 200 i think the default status is 200 so you don't technically need to do this i'm just being very explicit and we'll send we'll do json sorry jason and we'll send through the rank.user but we'll call it json uh oh should we just call it user actually user we're gonna call it i say we're gonna call it jason i'm jason we're gonna call it um data although in this case we're actually gonna call it user just i think that makes more sense um otherwise i think if we just did this oh yeah it would fail because it doesn't understand the dot there so user rec.body uh oh no sorry rank.user not body there we go okay that's good and then export default user controller controller and there's one thing that we need to do besides create the authenticated middleware we need to actually instantiate this controller here so if we do import user controller and that's going to be from oh from at resources slash user user controller there we go and we'll just add it here so we'll do new user controller and then just instantiate that there we go so that'll actually when it's that when the app starts up it will register um the these roots here because it'll call the instructor which we'll call initialize routes and then initialize these routes and that means we can actually use them um and then the last thing we need to do is create this new middleware so if you go to your middleware folder and create authenticated.middleware.ts and inside of here we've got a few things to impart again um we've got impart impart and then we've got request response and next function like pretty much every other file we've done so far um and that's gonna be from express then we want token so import token um to get the functionality i think verify token in this case we're gonna be using um so we could just do this i think verify verify token because i did i did say earlier that this is why we're exporting those um functions separately so that we can do something like this so we'll do verify token from at util token that's fine import user model and this will help test this will help check whether a user with that i think it's the um id of the the starred in the json web token um if the user with the id exists so we'll get that from resources user and then user model we'll do import a very confusing one here token again but this time we're actually it's the interface um as i mentioned last time i spelt that wrong from at utils i i mentioned the last time that you may want to name your interfaces with an i at the beginning of them i know some people do that but then some people like say don't do that again i don't know why um it's more about consistency when it comes to how you format your code and and the sort of the like naming conventions and stuff that you follow um if you do it throughout your code base and it's usually fine so if you want to do eye token i usually just get it from context depending on how i'm using it because i'm only going to use an interface as a type so i'll know that it's an interface that might still be a bit confusing but um yeah it works for me just fine so it's up to you what you do um with your naming convention so we'll do utils and we're going to get the http exception so we'll do exceptions http.exception and then the last but not least import jwt from json web token there we go so that's everything we need um we're only gonna actually do one function so that's quite a lot for one function um but it's quite it's a fairly big function so we'll do uh async function i'm going to call it authenticated middleware there we go and it's going to accept wreck which will be request which will be response next should be next function and that's going to return a promise either response or void so the first thing we're going to do is get the bearer talk and now i guess that's something i haven't mentioned yet but the way we we're going to start our json web token is in um it's in well it's in the authorization header so i'll just do this so const bearer is going to be rec dot headers oh head does plural and then authorization header and we start as a very talk which essentially just means that it's going to say bearer space and then the json web token so we actually need to get the json web token from um the authorization header so we'll do if if there's no bearer or the bearer doesn't start with so we do bear it dot starts with and then it's bearer and a space then we're going to throw an exception and this is the reason why this um so we'll do i'll not throw an exception sorry we'll just return that's unauthorized because it's not provided a token to us or the res dot status 401 let's send i'll do json here again and we'll do error and we'll just pass through unauthorized so as i was saying the reason why this function's so big is because we have to just go through quite a few use here uh quite a few scenarios sorry about what could potentially go wrong um so like in this case the bearer token hasn't been provided at all so we need to say okay that it's going to be unauthenticated um so we'll just return unauthorized because we can assume that they're not going to be able to do anything if we're using this middleware and the next thing i'm going to do is actually get the access token and we're just going to do bearer dot split and we're going to split it bearer space and then we'll do we'll get the first we'll get the second element in the array sorry and we'll do trim just to make sure it's got no white space and that should be fine and then we'll do a try catch and what we'll do is do const payload and i'm going to specify the type here so it's either going to be a token or jwt dot json web token error and that's going to be equal to await verify oh did it wrong hang on wherever i talk and there we go and i'm going to pass in the access token so what that and what that's going to do and we defined it earlier but it's going to check that um it's going to check it against the secret that we created in here so jwt secret and it's going to check that we actually signed that token for starters and then also check that that token hasn't expired and if it passes both those tests it'll count it as a valid token otherwise it will give us an error and what we're going to do is we're going to do a check here to see if this payload pit payload pair load is an instance of jwt json web token error and if it is then something's gone wrong um i'm not inside of the if statement there we go and we'll return once again just we can basically copy and paste what we did up here um if that's an error again we'll just say that it's unauthorized and the user will have to um try logging in again or something and if we know that that's fine and that we actually got a talk a valid token then we can use that token to find the user so we'll do await user model dot find by id and we'll pass in the payload [Music] id and we'll do a couple of more things so we'll do select and we'll do minus password uh so that what this will do is it'll get rid of the password um so it'll return all the other information about a user from the database except the password which is as you can imagine pretty important and then we'll execute that and then we'll do a check to see if that user exists so this is another um a potential scenario that the user doesn't actually exist um with that payload id so we'll do if no user um this time i'm not too sure why i've done that actually because i've wrote it weirdly so basically what i've got here and i've so return next for uh it's gonna be a new http exception 401 unauthorized which you might you might have already guessed why i'm a bit confused here one might spell that right unauthorized yeah so basically up here for some bizarre reason i've done return res.status 401 but then here i've done it as a http accept exception which probably makes a lot more sense so i'm going to do is i'm just going to get rid of these ones here and just and just use the http exception which would have made more sense right off the bat but whatever we'll go with it um yeah just just use um this http exception it essentially does the same thing um yeah that should be fine i think yeah that's okay so if there is a user we can set rec.user equal to user and then we can return next because it's at the bottom of the function we don't really need to add this return um so that's up to you if you want that and once again if all this fails we'll just say that it was unauthorized so yeah i don't know why i was doing the res dot status and stuff before i could have just used the http exception but there we go um i'm basing some of this off of an older code a bit code a bit code base which i've gone back and sort of rewritten a bit but not fully so that's why some of the things are a bit messed up um but it should be fine um and that looks like it i think for everything um this is still causing an error but i think it's just because it doesn't realize that it does exist now so if i do that you should refresh it and looks like that's fine oh it has no default i did it again hang on a minute oh okay i'm useless export default and what did you call it i will make you type it out again just copy and paste it there we go authenticated middleware oh god i need to get a better mic back to user controller yeah that seems to be fine all right so that's working um a lot of code but yeah that's that should be your login system done um you can in the future add other ways to log in as well and i would recommend doing that uh it makes it quite nice for the user to have like username and password as an option but then like google login or um the magic email link i mentioned earlier which is pretty cool way of doing things um so i have some requests set up just to test if everything's working so we need to actually run this so if we do npm run dev hopefully i don't get any weird issues like i did last time and we'll see oh i actually did as well using controller.ts okay let me just figure out a figure what's going on here okay so i figured out what the issue is and it's it's what i expected it to be um if you look at the register function and the login function and you look at the try catch the error that gets passed through um it complains about it being object unknown of type unknown and to solve that you can just do the the call on any again which i think we did we did in the last video as well to solve an issue um again i'm not 100 sure what's happening i think the problem is the the there's a slight version difference in typescript that i'm using for my old code base and this code base so yeah it's causing some issues i'm gonna have to look into that and make sure i can bring them both up to date so that if anything goes wrong i i spot it before i even start making the video but yeah if you just add um call on any and call on any it's not really an issue in this case obviously it's sort of frowned upon to add any because it sort of it removes the whole point of type checking and then stack type checking and whatnot um in this case though we know what it's going to be we know there's going to be an error and the only way it can get into this try catch is if it throws an error so it's not too much of an issue there's always going to be a message attached to it so in this case we can get away with it but yeah i'll have to look into it further to see what's actually causing this um but that should be fine um we can look now we're actually running it properly and it seems to be working fine but yeah as i was saying we have um or i've set up some requests in insomnia um to register and stuff i think that a user will already exist with this email so i'll do is i'll just say example test.com and i will send that request off there we go so it looks like it's registered the user and it's given us a token which is good login is gonna be the same i'm gonna have to change that to example oh i'll copy the entire email so we'll just do example test.com with a really secure password and we'll log the user in and it gives us another token so that's good um the last thing we need to do is check that we can actually get the user's information um as you can see if we send a request without any authentication uh without the bearer token added it gives us status 401 unauthorized if we go over to auth and we do bearer token and we add our token in here which we got from our login request we can do it again and it should oh cannot read property trim of undefined bear with me okay so the issue was um and i don't know why i did this i put a i put a call on after bearer um there's actually no call on it's just bearer and then a space so we try again um oh we'll go back to here try again and there we go we got our user uh yeah sorry about that i just once again my spelling just adding like random symbols all over the place as a bit of me on the ass again but um there we go that's working fine and yeah your login system should all be set up now and working um that's pretty much it so thank you for watching if you enjoyed the video uh actually if you had any any questions feel free to ask in the comments if you don't have any recommendations about what topics you want me to cover in the future let me know yeah other than that leave a like subscribe and i'll see you guys next time goodbye
Info
Channel: Rettson
Views: 219
Rating: undefined out of 5
Keywords: Create A NodeJS API From Scratch Using TypeScript And MongoDB, How To Create An API Using TypeScript, node.js api from scratch using typescript and mongodb, Create A NodeJS API Using TypeScript, how to create an api with nodejs and typescript, typescript, create a nodejs api from scratch, how to create a rest api with nodejs, how to create an api using node js, secure rest api, secure api, nodejs, typescript nodejs tutorial, typescript nodejs express, how to create an api with nodejs
Id: FXzsv2BJLKs
Channel Id: undefined
Length: 68min 50sec (4130 seconds)
Published: Sat Nov 20 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.