The ultimate NestJS Authentication guide

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
there is no point building an app if you can't get the authentication right if you get it wrong it can have disastrous consequences we're talking about security Brides lawsuits and even getting fired so if there is one topic you should take seriously it's definitely this one so I have been using nestjs for more than 3 years now but every time I have to implement or change authentication I always have to double check the documentation so this time I have decided once and for all to master this topic and share with you everything that I've learned so that it becomes absolutely crystal clear for you to so I'll be using some diagrams to show you how the different code modules fit together and I'll show you two ways of implementing the entire authentication flow and I will do that step by step and in the second method we'll use passport which is the best not just authentication Library out there there are many ways of implementing authentications so you have o sessions API Keys Etc but in this video we are going to focus on one of the most common authentication mechanism and here is how our authentication will work first the client will send a request with a username and password and our backend Nest app will validate that username and password if it's correct then it will generate a token and that token will be sent back to the client and when the client wants to access a restricted endpoint it will send a request with the access token in the header when our backend app receives that token it will be validated and if it's a valid token then we will send a response to the client otherwise the request will be rejected to implement the entire authentication flow we will have two modules the users module and the off module the users module will have a user service to find user data users by their name and the O module will receive authentication requests through the login endpoint in the controller and we'll have an off service that will validate the username password generate the token and validate the token you can start with an empty nest application and add a users module with the nest CLI then you can create the user service with the nest CLI as well here I'm passing the no SP option because we won't be writing unit test in this app you can define a user type with a user ID a username and a password I have already prepared some mug data matching that user type the user service will only have one method and that's the find user by name method you call the function with a username and it Returns the matching user you found or undefined otherwise I've declared this function as asynchronous even if there is nothing asynchronous going on here here that's just for the example again in a real life situation you would most likely fetch the user asynchronously there is just one last thing you need to do in the users module it's to export the user service you need to do that because later when we implement the O module you'll need to use the user service in there now that you have the users module implemented you can go ahead and implement the O module with a controller and a service using the CLI you can generate an O module then you can generate an O service and then generate a controller in case you haven't noticed yet I love using the net CLI tool okay so now we have an O module and inside it there is an O service and an O controller in the O controller you can add a post login end point for now you can throw an error and leave the all Serv empty as is and I'll show you how to implement this just after we test the login endpoint you can start your application in Dev mode with the start Dev command I will be using HTTP files in combination with the rest client extension to send request to the app but feel free to use Postman curl insomnia yeah pick your favorite client if you send the request to the O login endpoint an error is returned because the endpoint is not implemented yet so let let me show you how to fix that let's start with the O service you can add a validate user method that takes an input object that contains a username and password and validate user will return a signin data object with a user ID and username if the input is valid otherwise you can return null for now let's just return now to validate the authentication input you can call the find user by name method from the user service and if the user is found then you can check if the password in the input matches the password in the user record remember in the beginning when we exported the user service in the user module so now this means that you can import the user module in the O module and inject the user service in the O service now you can use the user service to fetch the user by their username and validate that the password in the input matches the user data password if the input is valid then you return a signin data object with the user ID and username and do not return the password I repeat do not return the password so if the input is invalid you return n you can now add an authenticate method that takes the same input as the validate user method but this method will return an authentication result object object with an access token a user ID and a username to authenticate the user we first validate the user by calling the validate user method if there is no user returned this means that the authentication input is invalid and you can throw an unauthorized exception from the nestjs common Library if the user is valid we can return an authentication result object for now we will just send a fake token I'll show you how to generate a real token later in the video if you try to log in you still get a not implemented error back that's because we are not using the authenticate method in our controller yet first you need to inject the O service in the controller and in order to access the post request body you can add an input parameter to the login Handler and decorate it with the body decorator so we expect that body to have a user name and password then you can simply call the authenticate method of the Au service with the request body if you send the login request you now get the user data back with a token and if you try to log in with the wrong password you get a 401 unauthorized response and even with a valid password the app still returns a fake token so the next step is to generate a real token we are going to generate a JWT token and a JWT token is a string that can be decoded into a Json object and in particular that Json object has a payload that we can use later on and the token is signed with a secret key so that the server can verify that the token is valid but lucky for us we can use a nestjs module library to generate and validate ajt tokens so for that we first need to install that Library module which is sjs JWT then the next thing to do is to import the JWT module inside our oath module file and the JWT module has a register method that takes some options as parameter and in particular we need to pass a secret so the secret will be used to sign the token in this demo I have used a JWT secret that is stored in the typescript file and here everybody that has access to the source code can see it but in a real life application you should be storing these in a secure way and maybe go through a configuration manager or environment variables so the string can be whatever but the key point is that it needs to be stored in a secure way then the other option is the expires in so here I've set the option to one day this means that after one day of the token being issued it will be considered invalid and this string can be changed to other values like 30 seconds 12 hours 30 minutes whatever meets your need for your application and use case so here I'm just going to keep it to one day now that we have configured the JWT module we can go to the O Serv and inject the JWT service in this class and as usual to inject a service we just added to the Constructor and in this case we import JWT service from the njs JWT Library so remember we are still returning a fake token so this part we need to replace it with a token generated by the GWT service so let's add the signin method it will take signning data as argument so it's an object with user ID username and it will return the authentication result which is an object with the access token and the user information user ID and username we start by instantiating a token payload object that has sub a sub property which stands for subject it's a convention in JWT token payload and it will have a username as well and after that we will generate an access token by signing the token page payload using the JWT service and the last thing to do in this method is to return a authentication result with v access token the username and user ID and if we go back to the authenticate method instead of returning an object with a fake access token here we can call our new method by using this sign in and we pass in the user and this time we should be able to return a valid authentication result with the access token now if I send a login request I should be getting a valid JWT access token alongside the user information let's have a quick recap of where we are so far so we now have three modules the users the O and the JWT module and inside the O service we have the validation of the user and the token generation using the JWT service and this is how everything fits together so far so we have a client that sends a login request and that request is handled by the controller the controller calls the authenticate method of the Au service and in order to validate the user we will try to find that user by username using the user service and if that user exist and the password in the input is correct we will generate a token by calling the the signin method of the JWT service and the JWT service will take care of generating a JWT token then that will allow us to return an authentication result with the access token and user information to the controller and the controller will send that response back to the client if we take a step back and look at the entire authentication flow we can see that we have only implemented half of the story we can validate the user and generate a token and return that token to the client but we haven't protected restricted endpoint yet so in the next part we will create a restricted endpoint which is the O me endpoint to get the user information and we will protect that endpoint by expecting a token in the header and validating that token in order to decide if we accept the request or reject the request in the O controller we will add a new endpoint to get the user information but we want that endpoint to be only accessible to authenticated users and in order to do that we will use a guard I have added a new file the O guard file inside the guard folder and in that file I have the O guard class and that class implements the can activate interface from the nestjs common library and it has a can activate method that returns a Boolean if it returns true it means that the endpoint can be accessed and if it returns false it means that we are refusing access to that endpoint to whoever is sending the request that method takes a context as argument and we can extract the request from that context and from the request we can extract the header of the request and what we are looking for is the authorization in the header and it should be a bearer token header so in the next line here what we want to do is to extract the token part of the header this is why we are splitting the string using the space and we want the second element which we expect to be the token if we don't have a token we will throw an unauthorized exception so this will reject the request with the 401 status response in the http response if we do have a token the next step is to verify if the token is valid and in order to do that we will use the JWT service but before we can use that service we need to inject it in the oath guard class and that's done by adding a Constructor and adding the JWT service inside the Constructor parameters we can call the verify asnc function from the JWT service to verify the token that was passed as part of the header if that token is valid then this call will not fail and we will return through and if that token is invalid this call will throw an exception and then in the catch here we will throw an unauthorized exception to reject the request this is the minimum implementation needed to protect an endpoint that uses this oath guard but we can go one step step further and make and make additional information available to the controller that will be using the oard by adding additional information to the request object here so from the token payload returned by the verify assing call we can now add a user object to the request and in there we'll have a user ID which will be the token payload sub property remember when we passed the payload we used sub for sub and the username which is the token payload username now that we have the O guard implemented we can use it in the controller and we use it by adding a used guard decorator that decorator comes from the nestjs common library and we pass the O guard as parameter it's the O guard we just implemented now this means that this endpoint is protected by this guard Whenever there is a call to this endpoint the can activate method will be called and depending on the outcome we will accept or reject the request in order to return the users information we can get the request that we updated in the guard by using the request decorator from the njs common Library as well and instead of throwing an error we will simply return the request. user object if I send a request like this without any header we should get an unauthorized response and that's what we get we get a 401 unauthorized and in order to access this endpoint I need to pass in a header and the header we want is the authorization header and we need to pass in a bearer token and the token we need is whatever we get when we login so let me send another request to login now I get the ACC access token I'm just going to copy that access token and add it to the header of the get Au me request and this time we should get back the users information if the token is invalid so if I change any value here and I try to send the request I get an unauthorized response because this token is not valid let's have a recap of what we have just implemented so we still have our three modules but we have added a me endpoint and that endpoint is protected by the O guard and that o guard will verify the access token we get from the request header and here is the details of what happens when a client wants to send a request to a restricted endpoint so that client needs to add a be token in the request header and when our oath controller receives that request it will use an oath guard to reject or accept the request our oath guard will extract the token from the request header and use the JWT service to verify that token if that token is valid then there will be a token payload returned and we will extract the information encoded in that payload to add it to the request object and we will accept the request and our oath controller will return the user information that it extract from the request user object so you now have all the basics to implement an authentication flow from the validation of the user generating a token validating a token when someone wants to access a resed endpoint but from this basic implementation you could add additional logic to your o guard for example or your o service or even the user service depending on your requirements and your use cases you can enrich that logic to fit exactly what your application needs in some cases this implementation might be enough but you can go one step further and use the passport library to implement more complex authentication strategies and that's exactly what we will do right now passport is the most popular authentication library for nodejs and the reason for its popularity is that it supports a wide range of authentication strategy and it's easy to use and extend and the good news is that it's supported by nestjs through the nestjs passport package so let's reimplement the authentication flow but this time we will be using the passport library and we'll make some adjustments to our code in order to do that we need to start by installing the njs passport library then we can import the passport module inside the O module to demonstrate the use of the passport Library I have created a brand new controller and that controller will be under the oath V2 path and it will have the same Endo as the previous controller a login endpoint and a me and point to get the users information and I already know that I'm going to use the O service here so I've already injected it in this class and of course let's not forget to add that new controller in the controller list list in the O module the login implementation using passport is a little bit different than the previous implementation with passport we will use a guard on the login end point and that guard will use what is called a strategy and the strategy that we'll use is the local strategy and for that we'll need to install the passport local library so let's go ahead I have created a new local strategy file inside the strategies folder inside the O module folder so my local strategy will extend a class that is going to be generated using a factory function called passport strategy and that class comes from the nestjs passport library and it takes a parameter and that parameter is the strategy we want to use so in our case we want to use the local strategy so I'm going to import the local strategy from the passport local library so it's the strategy that we want and we pass this as a parameter of our Factory function in this class we need to implement a validate method that takes a username and password as argument and if the user name and password are valid we will return a user object otherwise we will throw an error and in order to validate the username and password we will be using the O service that we've already implemented so we just have to inject it inside the local strategy class as usual we inject the O service in the Constructor but this time we need to make sure we call Super to call the Constructor of the parent class we can use the O service to validate the user so that's the method we've already implemented before by passing in the username and password and if the user doesn't exist we throw an unauthorized exception otherwise we will return the user so the next step is to implement a guard that will use this local strategy but in order to use that local strategy in the guard that we will create in a few minutes we need to add the local strategy in the provider of our oath module so let's open the O module and add the local strategy to the provider list I have added the passport local guard inside the guard folder and this guard will extend the oath Guard from the nestjs passport library and it takes a parameter which is the name of the strategy we want to use and in this case we want to use the local strategy we use local because that's the default name of the local strategy from passport local and later on I'll show you how we can configure this parameter here with this guard we can now go back to our controller and add it to the login endpoint to do that we are going to use the guard decorator from DJs common and it takes the and it takes a parameter and that parameter is the guard we just implemented so I'm just going to copy paste it here and import it and instead of throwing an error I'm just going to return a simple string let's return success and now now that we have a partial implementation we can test it out to see how it works before we continue our implementation I just wanted to mention that passport relies on a few default behavior for example our local strategy expects exactly username and password in the body of the request if I wanted to use login and pass for example I will need to make some changes to my local strategy and inside the local strategy I will need to pass in a username Field property to the super Constructor and a password field option and that way I can send requests using these property names if I pass in the correct password then I receive a success response the other thing to mention is the strategy name so we've used in our passport local guard we've used local because that was the default name of our strategy but if I wanted to explicitly name my strategy and use and name it my local so if I wanted to use this name inside the guard I will have to go back to the local strategy and I can pass a second parameter to the factory function here and that will be the name of my strategy my local so this can for example allow you to create multiple strategies that are based on the same passport local strategy if you wanted to we can now go back to the passport of controller and instead of returning the success string we will return the authentication result and for that we are going to use the O service with the signin function we've already implemented so let's add that so we use the O service and we call the signin function but for that signin function we need to pass in a user as parameter and the good thing with passport is that it will add the user data on the request and that's because when we in our local strategy here when we return the user passport will take care of adding it to the request object and we can have access to the request object in our controller by using the request decorator from the JS common and we simply pass request. user to the sign-in function and if we send a request we should get the authentication result with the access token in the response time for a quick recap we have added a passport module to our application we have created a local strategy using the passport factory and a strategy from the passport local Library we have added the passport oath guard that we've created using the oath guard Factory and the strategy name and we use our passport oath guard in the new controller that we've added in order to do the authentication using username and password if we look at what happens when we receive a request from the client to log in the passport oath controller will handle that request and it has a passport local guard that guard will validate the username and password using the local strategy and the local strategy uses our o service to validate that username and password and if the user is valid is returned and the local strategy returned to user and by the way when the local strategy returned to user passport will automatically add that user to the request object then if the password and username are valid the guard will accept the request and in the controller we will sign that user from the request object through the O service and return an authentication result with the access token username and user ID so we have implemented the validation of the user generating a token and returning the authentication result now what's left to do with passport is to protect restricted endpoint with an access token and you will see that with passport it's always the same thing we'll have a strategy and then we'll add a guard and that guard will be used in the restricted endpoint just like the previous strategy the JWT strategy also has a validate function and this time the validate will take the payload of the JWT token as parameter and that will allow us to return the user ID from the payload subject and the username from the payload username property I have created a passport JT o guard and this time we pass the JT name to the O guard Factory what's left to do now is to add the guard to the endpoint and return the request user something to note is that this request user is not the same as this request User it's whatever is returned by the strategies validate user methods that's going to be set as the request user before you can test the endpoint you need to make sure that the new JWT strategy is added to the O module providers list now if you send a request to the O V2 me end point with a valid token you get the user info and if you pass an invalid token you'll receive an unauthorized response so we've seen how to implement an entire authentication flow with and without the passport library and you might have noticed in the bottom corner of my IDE I had copilot disabled so I have it disabled most of the time but when I do use it I try to make the best out of it and if you want to know how to make the best out of it too you can check out this video where I'll show you the best practices what to do what not to do and along the way I'll also give you some tips
Info
Channel: Tech Vision
Views: 1,395
Rating: undefined out of 5
Keywords: nest, nestjs tutorial, NestJS Auth, NestJS Security, nest js
Id: i-howKMrtCM
Channel Id: undefined
Length: 30min 59sec (1859 seconds)
Published: Tue Jun 11 2024
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.