NestJS Authentication: JWTs, Sessions, logins, and more! | NestJS PassportJS Tutorial

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey how's it going today we're going to talk about an sjs authentication specifically we're going to discuss how do you get a user logged in how do you protect your apis with things like jwt or perhaps you want to use sessions all of the different strategies involved with any of that we're going to discuss in this video so make sure to stick around and as always if you find this video helpful at all i appreciate a thumbs up and a subscribe if you want to see more that said let's get started so as you can see in my desktop here i created a new sjs project using the cli if you're completely new to nesjs i do have a crash course in my channel make sure to check that out and that'll walk you through exactly how to set up a application from scratch so i won't cover that here so we're just starting with a very basic skeleton app here so just as a quick intro in general how authentication works in sjs is really via passport.js so those of you that are coming from the expressworld passport has been around forever i'm sure you've heard of it if not i recommend looking into it a little bit before you continue into this video but the the gist of it is that passport kind of provides a think of it as like a single interface to doing authentications but it offers you the ability to use different strategies so for example if we go into search strategies here you'll see some of the popular ones right so like there's one for maybe facebook logins google logins maybe your basic local username and password which we'll do in this tutorial you know maybe you're doing auth0 jwt the basic idea is that passport kind of gives you this layer and then you can kind of just switch your authentication strategy to whatever you want which is really nice and the nice thing about nestodis is they basically just created like a a light wrapper on top of passport so that they're not really reinventing the wheel here they're just kind of augmenting it to fit the nest landscape so basically if you've used passport before a lot of that knowledge will transfer over into nestjs all right so back in the code generally what you need to do to get started you know assuming you already have your sgs application is you're going to install nest js passport passport itself and then the strategy that you're going to use in this tutorial we're going to try passport local passport local is sort of just this very basic authentication strategy where we want to allow the user to provide a username and password in their request and that's how they log in right and then the implementation of what happens after that is dependent on how we're going to implement it you also often have to install the types for your strategy so usually you should be able to find that on types and in the name of your strategy you know this is assuming that the strategy was originally written perhaps in javascript and then the community provided types for it all right so with that installation out of the way let's first kind of manipulate our initial controller to provide some basic routes so that we kind of have a direction of where we're heading towards let's imagine that we are implementing a a login route so what we have here effectively is we're providing a route for logging in and once that user is logged in we're going to allow them to proceed making other calls like this protected one and the behavior we want here is if they're not yet logged in we want to make sure this route is protected so that when they call that and they haven't been logged in we're going to present some kind of http error all right the first thing we're going to do actually is initialize a couple things so that we can sort of simulate a environment where perhaps we have a database of users we're not going to set up databases in this tutorial that's a little bit it's a little bit out of scope but we're going to try and create a mock of that so i'm just going to do in the terminal nsg module users to generate a new users module and then i'm also going to do nsg service users to create a user service so you should see that you have a new users folder here with a user's module and it has the user's provider added in there so what we're going to do here is let's create a basic type of a user just so we have a sort of a definition of what's the shape of this user you know let's say that this user has an id which is just a number you know usually entities in a database will have some kind of unique id and then let's give them a name just a string and then since we're trying to do local authentication we're going to do username which is also string and password all right so imagine that those are the the fields that we would be saving in the database now within the user service itself i'm just going to create a an array of mock users in here as you can see it has these fields that we have id name username password i should call out immediately that obviously this is a tutorial right so you should not be storing passwords in plain text like this i'm just doing it this way because i'm trying to teach but in a real environment you should make sure that you're not doing that you know perhaps you want to use patch like this decrypt to hash your passwords you know do what you have to do to actually do the real security bits of it all i'm doing here is kind of teaching you the fundamentals of how to get all these things kind of wired up together but you still have a little bit to learn on your own you know to make sure that you're doing things in the most secure way possible all right with that out of the way i'm just gonna add a basic find one method here which allows us to find a user by their username this should return a user or undefined if not found so since we just have a pretty basic simple in memory array here we're just going to do this.users.find and for each user we're just gonna check if the username matches the given username all right so pretty basic got a user service we got some mock users and we just have a method for finding a user by username now going back into the user's module uh kind of giving you a quick hint here is we're going to be using the user service in another module so we need to provide the user service as an export if you didn't know that's kind of how you allow a service from one module to be used in another module all right from here we can actually start doing some of the auth stuff so we're going to create a new module specifically for auth and similarly i'm going to create a service for auth as well all right now i'm in the auth module like we said we're going to be utilizing that user service that we just created so we have to make sure to add that at the users module in here now let's take a look at our auth service all right so knowing that we're gonna be using the user service here let's start with a constructor that allows us to inject that so i'm just gonna do private user service of type users service now what we need to add here is a method for validating a user and this is something that we're going to utilize later down the chain when we actually start using passport we're going to use this method to do the actual validation that hey this is an existing user in our database so we're also going to need to pass in the username here and their password so imagine that we did call this method we passed in a username and a password and we'll get there in a little bit here but just bear with me here imagine that we are calling this and we're passing in a username and password where we go from here is we need to try and find that user using our user service which we just provided a method for that recently right so we're going to do await this that user service that find one and remember that that takes in a username so we're just going to pass in this given username now to kind of add some really basic validation right we're just going to check if the given password that's passed into this method matches the one that's in our quote unquote database which if this actually found an existing user with that same username this user object is going to have that password and we just got to make sure that those matches were the one that's given so if we found a user and user that password matches the passed in from the arguments now what we want to do from here is actually return the user itself assuming that their passwords match so we're kind of saying yep this user exists in our in our database let's return that person however remember that our user record has also the password and the username in it so we kind of want to pull that out so we can do that with some simple destructuring you know let's pull out password and username and then we'll just do a spread here to get the rest of the user when i do that from the user object itself and then we'll just return the rest here right so if we look into remember we created a basic type here if we pulled out the username and password what we have left is just id and name so that's what the shape of this rest is going to be it's just an id and a name now if we didn't find the user or the password didn't exist we're simply just going to return no all right from here we're going to add a new file called local dot strategy dot ts now what this file is gonna be is it's gonna represent basically the uh the strategy from passport which we install which is the local one so this extends passport strategy and within this we need to pass in the actual strategy that we're trying to use right so in our example we're using the strategy from passport local so we're going to take that strategy we're going to pass it in here and this is actually going to be from nessa's perspective it's going to be a provider so we're going to make this an injectable now this is actually where we're going to be using the auth service so we want to again create a constructor and we're going to do the injection in here so we're going to add odd service of type auth service the one we just created now within here notice that there's red squigglies here because it's looking for you to call super here don't forget to do that this is also the place where if your strategy requires other configuration this is where you you pass in that configuration so before we continue let me show you guys some examples so passport local itself doesn't really require much more configuration than just that than what we have but if you look at other strategies so for example let's take a look at the passport facebook here you'll notice that this strategy requires that you pass in client id and client secret right so if you were using this strategy instead of passport local maybe you're trying to implement facebook logins or google logins that's where you will pass that object in here with the client id client secret etc so again i can't show that to you because local strategy doesn't really require much if any configuration but know that if you're going to be using a different authentication strategy perhaps you're using a third-party identity provider this is where you're going to configure it initially all right next what pretty much every single one of these passport strategy classes require is a validate method and in the case of local strategy passport local this is gonna have username which is a string and password which is a string and you're probably starting to see where we're heading with this is we're gonna have we're gonna look up the user by first using our auth service which i actually misspelled they should be auth service up here we're going to validate our user by passing in username and password right and remember it kind of just goes through that whole thing if the user doesn't have the right password or that user doesn't exist in our database you know you're not going to have a user so if there's no user we're going to throw a new unauthorized exception and you might have seen from my previous nest crash course video you know a lot of this you know exception handling is done by nest automatically you kind of just need to throw specific exception classes and it'll do the job of figuring out what status code goes along with that and we're going to see this in action in a little bit here now what's missing here is we need to return the user itself and i also misspelled async here i'm sorry and from here you might be wondering why are we making so many different services right you know we have the auth service we have the user service and then we have this validate function you know why is there so many different services well the idea is separation concerns right so the odd service is just in charge of authentication stuff validating and stuff like that the user service is specifically in charge of you know dealing with the user object finding it maybe creating one if you had a database the basic idea is just separation of concerns by the way if you're familiar with passport already you might be familiar with the verify callback which you can check the docs if you'd like but this is basically equivalent to that this validate that we're having here and in the strategy is effectively mapping to verify callback right so kind of like in this documentation here it says when password authenticates a request it parses the credentials containing that request and then invokes verify callback with those credentials as arguments in this case username and password same thing we're doing here right it's go it's ultimately going to invoke validate automatically as it goes through the authentication flow and it's going to provide the credentials in here at least for this very specific example if you were using a different strategy which you'll see another one in a little bit in this video you'll probably have different arguments in this method and then you'll also see in the docs where it kind of does this you know done user that's the same thing as what we're doing here except that instead of using a done method we're just returning the user directly and nest kind of maps that correctly on behind the scenes for passport you'll notice that in passport they have this first argument reserved for potential errors right so if you pass an error into done that's how it kind of tells hey passport something went wrong but in our case we're kind of just utilizing nests you know its own exception handling so if there's an error you just throw the correct exception all right with this strategy in place what we need to do is to go back into our auth module and we need to add a couple things in here first of all you need to add the passport module in here it's one of the things we installed earlier and then now that we created that remember that our strategy over here is a provider it's an injectable that means that we also need to register that here as local strategy all right now that we have all that plumbing in place what's really left is we need to be able to provide something to this login route here a way for it to say hey log the user in and basically we want to invoke passport in some way and the way we're going to be doing this is by utilizing guards in sjs if you haven't heard of guards before it might be worth doing a little bit of reading on the documentation but the point of it is effectively a guard as the name implies is it kind of protects a route it adds logic between the request and the route handler and it has the responsibility of checking things like authentication authorization is a user authenticated or authorized to continue if so keep going if not run some other logic or throw an error in our case we need to have a guard that says if the user is not logged in trigger this entire authentication flow so the way we're going to achieve that is in our auth folder we're going to add another file and we're going to call this local auth dot guard dot ts and similarly this is just going to be a class which extends auth guard from passport sgs passport and this is where you actually provide the name of your strategy so in our case this is local and similarly to the strategy class this also needs to be a provider right for now we don't really need to add much more to this uh we can go ahead and just add it so in our login right here we're gonna add use guards and we're gonna import our local auth guard now real quick about passport before we continue again ideally you'd know passport already to really utilize this information well so i recommend you look into it a little bit but just to give you some some things to understand you'll notice that in passport right you you have the different strategies and the way you tell passport which strategy you're using is by providing those those strengths like right like so for example if there was a twitter strategy you do authenticate twitter or facebook that's what we're doing here in the guard with the guard this string right here is us telling passport that we're going to be using the local strategy that we registered and the way that gets registered is effectively when we use it as a provider in our module it kind of automatically registers it as an existing strategy from passport's perspective the guard just allows us to tell passport yeah we're gonna use that strategy so the important thing to understand here is you can implement multiple strategies at the same time if you'd like so that's why you need to have these strings to kind of specify which of the strategies are you using now for passport local specifically you'll notice that in the documentation it tells you that you know you have to do passport local password authenticate local that's how we know what string we need to provide to our guard here and if you check the documentation of other strategies usually they will tell you which one you need to use so as an example if we took a look at this twitter one for example there should see there should be some documentation in here which shows what string you need to use just look for password that authenticate and in this case you would be providing twitter right so just to summarize if you were using the twitter strategy you would make your own twitter guard and you're going to add the string twitter here using the auth guard from password right hopefully that's not confusing um let me know in the comments if if it is the other thing to add with how passport works is once the user kind of logs in and everything is valid within this verify callback or validate method the the user that returned that your return actually ends up in the request object so if we were to add request here and make sure to import that from sds common it will passport will actually save that object and request that user and this is how you can kind of utilize all right once the user is logged in how can i get the details of that logged in user throughout the the request lifecycle right so what i kind of expect here is when i call log in and assuming that it's successful it's going to return to me the the details of that person that logged in so let's go ahead and test this real quick remember that i have these mock users so i should be able to log in with my name and this password so i'm going to open insomnia here you can also use postman if you prefer that so i'm just going to create a new login request here and this needs to be a post i'm going to do create and i should probably run the application let's not forget to do that so we're going to do npm run start dev and this will run in localhost 3000 all right no compilation errors let's go ahead and go back to insomnia so in my request i should be able to go to http localhost 3000 and we're gonna do if i were to just call post on login here and send you'll see that it fails immediately because i didn't pass in the username and password so let's do that in the body we're going to add json username my name and password right and when we send we get back the object so let me try to summarize what happened so far we sent a post request with a username and a password right so that goes into our app controller it lands here and then before it even gets down to down here right like we don't yet have a request that user it's gonna go through the guards to do that authentication which it's going to go through the logic of hey passport use passport local which this is effectively going to trigger our local strategy right so it's going to run into this behind the scenes eventually it's going to call validate with the username and password that we passed into the body right and it's going to use that username and password to validate the user and eventually go into user service to find the user make sure it exists and then goes back into the odd service to validate make sure the password matches at the end of that you get back a user assuming it exists and the password is correct and then the user is returned and when the user is returned from here passport will save that user and request that user and then finally we actually get down to our handler and by the time it gets gets down to our handler request that user is already filled in with that logged in user so that's how we're able to get that response back all right now we need to switch gears and talk about how are we going to go about protecting this route right so we got the logged in user but this route is not protected yet if i were to go into insomnia and create a new request i'm just going to call this protected and it has http get if i were to make a call to slash protected and hit send i'm able to get my hello world response here right so it's not at all protected yet we still need to do that also another thing to point out if i were to try and return let's just say that we wanted to access who the logged in user is down here as well right so let's just do the same thing of um return request.user here let's take a look at what happens so even if after i logged in and go to protected and hit send here again it comes back with an empty request set user because it effectively lost that login so how do we deal with that so from at this point of the video this is where it kind of uh forks into two different ways that you can accomplish this protection that we need to talk about so one way that we're gonna do first is you can introduce sessions so that when the user logs in we're gonna save this request that user in the session so that any subsequent requests after logging in we just grab the user's details from the session and we should be able to access it easily like this and then we can set up that session to expire you know so that you don't have a user that is always logged in so that's your option number one is to utilize sessions now the problem with sessions is obviously it introduces state and you need to think about where exactly you're going to store that session right so for example maybe you store it in redis or something similar you know some kind of data store but let's say that maybe that doesn't fit your requirements you don't really want to introduce another session store or any store and perhaps you want to keep your api completely stateless right so the typical solution for that is introducing jwts which we're going to cover in more towards the second or later half of the video and i'll add some timestamps to the description of the video if you need to jump around between the two strategies but i think it's important to understand the difference between the two and what's the pros and cons i don't think i can really discuss in full detail what the the pros and cons are so i i would i would suggest that you do your own research right to see what fits your your requirements so for example just some quick notes that i can provide is with sessions you kind of keep a lot of the information to yourself you're kind of you're kind of protecting the information of that user from going beyond the server right the server side the back end so it's a little bit more secure in a sense but then you have sessions to manage right now when you go to the jwt side you don't have sessions to manage it's it's more stateless however you also need to watch out for the fact that oh what if someone created a fake jwt right so you need to kind of check is the is the jwt valid so there's an extra validation step so different kind of approaches depending on what you're trying to do that's the basics of it i highly recommend doing a little bit more research to really decide which way to go and obviously there's other ways to do authentication right but i'm just showing you the two typical cases that i've personally seen all right so let's get started we're going to first start with the session strategy the first thing we're going to do is we're actually going to create a new guard which allows us to do that check of hey is there a session for this request for this user that's making this request so i'm going to create a new guard here and i'm just going to call it authenticated dot guard i can't really think of a better name there and this is just going to be export class authenticated guard and you didn't know if you're making custom guards you got to do implements can activate and similarly to what we've been doing to other things make this injectable and we're going to define a can activate method and it's okay if you don't completely understand what's going on here i'm gonna try and type some stuff out and i'll explain it all right so here's our basic guard what this is just doing is remember that guards are kind of just in between the request and actual the actual handling of that request so it's able to kind of intercept what's happening with that request and kind of do some initial logic before it goes to the handler in this case we're just grabbing that request from the context and then we're checking if it's authenticated and this is actually something that comes from passport automatically which is what it's going to do is assuming you did set up sessions it's going to try and look for that session and say does the session exist for this user if so keep going so if if there is an existing session for the user this is going to return true and it's going to allow the it's going to allow the request to keep going down to the handler so to kind of see this in action we're going to add that here similarly using used guards so i'm back in app controller i'm going to add the new authenticated guard in here so what i kind of expect to happen here is obviously we didn't set up sessions yet so i expect it to always fail at this point so if i go into my insomnia again and make my protected call you'll notice that it's going to come back with a 403 forbidden resource because the user is not logged in now there's a missing piece right ideally when i do a login here and then go back to my protected it's gonna notice that hey this user is logged in but right now it's not doing that so that's where sessions come in so that when we log in we're going to save the user in that session and we're going to be able to access our protected route so first things first back in the terminal i'm going to do an npm install express session nice thing with express is because at least by default it's kind of just a layer that's working on top of express that means that you have a lot of you know reusable things in the express ecosystem including things like express session so again if you're coming from the express land a lot of this stuff probably is familiar to you so in here i'm just going to add some imports from express session and passport and by the way i'm in main.ts sort of the root of our application and within here this is where we're going to configure our session so for example here is some configuration that i copied from the express session documentation make sure you look into the documentation by the way um something that i think people make the mistake of is they don't really read into uh the best practices so for example this secret the session secret you know you should probably put that in an environment variable or something like that you know don't put that in in the code also by default express session uses an in-memory session store that does not work well in production in fact it's gonna have memory leaks if you use it in production so make sure that in in a real environment you utilize something like redis or a database wherever you want to save your sessions make sure to not use the in-memory one it is not meant to be used for production and that's covered in the documentation alpha express session so make sure to look into it now if you didn't know the way express session works is um when a new session is created for a user it kind of comes back in the form of a cookie but all that cookie has is effectively a key to the store so imagine as an example let's say we have a redis store for our session when the user logs in they get back a cookie which represents their key to that store so the cookie itself does not have the user information it just represents a key to the record that we have in the store itself so one benefit there is we're never really exposing the user information directly with the client we're just exposing a very basic cookie as a key to the store and that cookie you can add some some configuration to it make sure to check the docs you know uh one that's really uh important i think is adding a max age this is i believe an hour in milliseconds could be wrong but basically you know you don't want a cookie that is never expiring you want to make sure that it expires at some point all right next thing we're going to add here is we're just going to add passport initialize and passport session these are sort of just the things that you need to make sure to have if you're going to use sessions with passport this is also covered in the passport documentation if you'd like to learn more and then finally we need to create a new file here to do our serializers so i'm going to create a new file called session dot serializer dot ts and let me just type out some code real quick and i'll explain what we have all right so we've got a session serializer here notice that it's extending passport serializer the the idea here is before it goes into our session you we need to serialize the user object and as it comes out of the session we need to deserialize it so it's up to you how you do that logic so for example in this very specific example the user that we return from validate in our local strategy so this user basically the user with just the id and the name that's what ends up being passed into the serialized user so as it's trying to save that object into the session it goes into this method and then it's up to us what we specifically actually save in the session store so right now we're simply just saying we're just going to save that whole object which is just the id and the name you absolutely can set this up to just be like the id if you want like that you know if you're maybe trying to keep the session record small or you're trying to not expose too much information there you can do that and then within this the serialized user right you can also inject user service in here via constructor and you can do something like you know constant this dot you know user equals this dot you know user service you know dot find by id or whatever pass in your payload id right and then you can take whatever you get back and return it like that so that's that's a scenario where perhaps you again you want to only save the id into the session but on the serialize maybe you want to get the full information from the database or another api call it's up to you but for this tutorial i'm gonna go back to what i had before we're just gonna have just straight up serialize the user object that we have which is already pretty simple anyways and just deserialize that and get it back all right so what we save is what we get back pretty simple all right one last step that we need to make sure to do is back in the auth module we need to register that serializer so we're going to add session serializer here in our auth module providers and then for the passport module we need to add register session true actually to be honest from my testing i'm not sure that this is directly necessary but i assume that that register method is there for a reason so you might as well set the session through there so basically you should understand that session is false by default unless you set it up that way actually i was wrong there is one more thing that we need to change within our local auth guard the missing piece here is we still need to tell this to trigger an actual login via creating a session and the way to do that is let me add some code here so i added a custom can activate here which similarly just gets back the the request and in this case we're just going to trigger this login which we passed a request in alright so this is kind of just one gotcha that you need to remember is if you're going to trigger if you want sessions you got to remember to trigger this super login this is the step that i always just keep forgetting so with that out of the way so back in our app controller i'm just going to update this login to respond with uh something different maybe just a message right because i actually want to return i want to show that we're able to get the request user that's in the session down here in the protected request all right so back in insomnia in our slash login post route i've got my username and password i'm going to hit send and it'll tell me you got logged in but there's something new here you'll notice that the cookie now has this thing right remember we talked about how with express session it creates a cookie but that cookie just represents the key to the session store it doesn't have much more information beyond that so this cookie effectively if you were doing this on a client the cookie gets saved in the browser and the rest of the requests going forward is going to utilize that cookie to kind of simulate that this user is still logged in and remember that you can have those cookies expire at a certain time interval that you set so you'll notice in insomnia there's a cookie section here which is going to have this cookie that i got back from here right and it's going to automatically attach that to the rest of the requests and it should work similarly in in a browser environment it'll just kind of automatically provide it in subsequent requests so when i make a call to my protected route here when i hit send we get back the user object so let's break down again kind of what happened is back in the code we went into login and then as it logs in it goes through that passport loop of what i talked about earlier but the step we added is after after it goes into this local strategy and returns a user this user is saved into our session store it's serialized and then what happens is we along with this login we return back a cookie which now we can which the client can now utilize for subsequent requests and when it goes into another request that requires the user session in our case we added an authenticated guard so what will effectively happen here is this guard will check if the user is authenticated by first looking to see or do you have that cookie with a session id it's then going gonna use that session id to see um is there an existing record in our session store with that same key and it's also gonna check things like is that session expired or maybe already got deleted and garbage collected but basically if it doesn't find the session record in the store it's gonna assume you're not authenticated but if it does find it then it's gonna assume you are authenticated and then it lets you pass through so after it returns through here it eventually gets to our get hello handler here and actually accesses that session state that we had which is just the user object so that kind of just completes the flow of now we're able to log a user in and we're able to utilize sessions to keep a user logged in and again remember in main.ts we added session config here which you should customize for your needs again move the secret to somewhere secure like from the environment variables you know make sure to set a max age on your cookie so that it's able to kind of expire itself and kind of ask the user to trigger a new login because hey your session expired and there are if you check that documentation there's a bunch of other things you can configure for cookies regarding security that you absolutely should look into right so i'm just i'm showing you the fundamentals here you need to take initiative and understanding the rest in terms of security all right so that wraps up our first path direct that i mentioned earlier of taking the session based approach now our second path again is going the jwt route of not having sessions we're just gonna have this this access token that we kind of pass around and that's effectively where the state is gonna live so before we get to that part i'm gonna do some cleanup to basically get our code back to what it was before we added sessions so i'm going to remove this use guards which is doing logic 2 to check if a user has a session in the main.ts i'm gonna remove the session configuration and i'm gonna remove this passport initialize session stuff you don't actually need that anymore and then in the auth module i'm gonna remove this register with the session true from the passport config and i'm going to remove the session serializer one more thing to clean up is in our local auth guard remember that we added this can activate which again this super.login is effectively triggering passport to use sessions we don't actually want that with jwts because it's sessionless it's stateless so we're just gonna delete that and we're just gonna keep this as a simple class like so all right so from here we're back to sort of where we started where we still have that login but now here we need to kind of switch this up to to do on login return ajwt access token right and then over here we need to do something where we need to do basically we need to require this to have an access token and then we also need to make sure that that token is valid okay so that's where we're gonna start and we're gonna do the jwt stuff all right the first thing we need to do is back in the terminal we're going to install a couple of things here first of all we're going to install an sjs slash jwt and passport jwt so nasty sjwt is kind of just a companion library that you know the nest guys made which helps with things like creating jwts and stuff like that and then passport jw is a specific passport strategy specifically for jwt so what it does is effectively it looks for a jwt in a request and then it does things like verifying to make sure that oh this token is valid so we'll go and discuss that a little bit more but first let's get some things installed and hooked up so make sure to make sure to install both of those and again you probably need to install the types now i should mention that this video is not really the best video for understanding what jwts are or when they are best to be used over sessions i mean i gave some basic thoughts earlier but i think if you don't already know what jwts are i would recommend you spend a little bit time reading about it maybe before continuing this video i kind of just assumed that you have some basic knowledge of what it is i'm just showing you the specifically the how to do it with nest and passport all right so how we're going to get started in here is within the auth service we are going to import a jwt service from that dependency that we just installed now the service is something that we can inject into this auth service via constructor just like any other service all right so what we'll do in this odd service is we're going to create a new login method which takes in a user and effectively what we're going to try to do here is create a new jwt and to do that first we need to create some kind of payload that has the information that we want to save in our jwt in our case we're just going to save the name of the user and then typically with jwts i believe it's sort of common practice to have this sub property to be the id and what we're going to do is we're just going to return an object which has an access token and to create that actual jwt access token we're going to use the jwt service and we're just going to use that to sign our payload all right now that we have that in place what we actually need to do back in the auth module is to register the jwt module again coming from sjwt uh and we're gonna register something here we need to provide a secret which i'm just gonna create a very basic string we'll add some basic options here like we can set up our jwts to expire in 60 seconds now real quick note about this again just like how our session secret i mentioned that ideally you are not storing that in plain strings like this you know put this in your environment variables right this is this is extremely important this needs to be something that is not exposed to anybody this should be and i'm going to explain in a little bit why this is important but for now know that that is an important string which allows us to validate our jwts and we'll discuss that a little bit more and since we added a new login method to our auth service we're actually going to be utilizing that login method in the app controller that we have so that means that we need to make sure that our auth service is in the exports here so that we can import the auth service in the app controller so we're going to add auth service in exports now back in app controller now that that odd service is in the exports i can actually import it in here i'll just replace this app service that we're not really using i'll do auth service auth service right now we added a to do here that we need to return the created jwt access token so what we're going to do is we're going to return this dot auth service dot login and remember that that login method requires that we pass a user into that and how do we get that user via request that user right so this should take care of our to-do now let's just go back in insomnia and kind of see if this is working so back in my let me clear this history back in my login route i'm going to hit send and now the response that we get back is an access token now what is this right if hopefully again you know a little bit about jwts but if we were to copy that and put it into something like jwt.io i can paste my jwt in here and you can see that to some degree it's be we're able to decode it and we can see that it has that name and my the sub id which is a user id and it also expires at a certain time period but notice that it's saying it's invalid signature because it's not able to completely uh kind of decode it because there's a missing piece in our signature which is the secret right so if i typed in our super secure secret here notice that now the signature signature is verified and that's how we're going to be doing jwt verification is we're going to be utilizing that that secret to do the verification that's why you need to make sure you keep that string secure you don't want to pass that around because when you pass that around then people can just kind of make fake jwts for your application if it were somehow you know stolen from you all right so we've got that working we're able to make a jwt what's the missing piece the missing piece is is have protecting this route with that access token so how do we do that so remember that we installed a jwt strategy for passport so in our auth directory we are gonna create a new strategy file in here called jwt dot strategy dot yes and similar to our local strategy this is going to be a class we'll call it jwt strategy which extends passport strategy and again you need to you need to provide the actual strategy that we're using here in our case we're trying to use the jwt strategy the passport jwt strategy right notice that they all kind of export a strategy object like this that we pass in here and then make this injectable because it's a provider and remember i said earlier that to configure your strategy you do that in the constructor via the super right so within here is where we provide configuration for the jwt the passport jwt strategy you know if you look in the documentation of this password jwt you'll notice that you know it takes in an options object so for example it allows you to pass in a secret or key right and that's going to be our super secret string again i need to keep calling this out you got to protect the string pull it from environment variables or wherever else you want where it's not completely exposed or hard coded in any way in your code right obviously this is a tutorial so i'm trying to make it simple for you to follow so we're just using the you know the string like that you know you can add other things in here like ignore expiration which we're going to say is false right because you this is where this is part of the security where if you set up your jwts to expire for in our case i think we said 60 seconds here but let's say in your case you do an hour or one day you know you don't want a token that forever has access you want it to sort of expire or at least be invalid at some point in time we also need to tell this exactly how to get that access token and you do that via this jwt from request and this extracted jwt from auth header as bear token right so it's gonna look in the headers of the request and it's gonna expect that there's a bearer token in there and that's gonna be our jwt and it's probably important to discuss why again are we using the same secret here well remember that if we go into the jwt io the only way it's able to verify that our jwt is valid is by decoding it and making sure that it has the correct signature by you know checking what algorithm it's using and what secret it's using it kind of makes a combination of that to determine is this a valid jwt which is why you need to protect this value because it's what's going to determine if the passing jwt is something that you created your server created not some other you know fake server right so part of this as it goes through this constructor it's gonna extract the jwt from the headers and then it's gonna do its validation and then once it's done validating it goes into this validate method and it just passes in the payload the payload in this case is the decoded jwt so so going back into this jwt io notice that it's encoded right the decoded value which is just the json payload is what ends up being passed down to this validate so that's what this payload is going to be so if we go back to our auth service and put that side by side here remember that it's effectively equivalent to the same payload here so we should have a so we should expect a name and a sub property and we're basically just going to translate that to be what do we want our user object to look like well we're just going to translate it to id is the payload.sub and the name is just a payload.name and again the way passport works is whatever your return in this validate method is what's going to be saved in request.user so we'll see an example that in just a little bit here but real quick what you probably what might be confusing here is it says validate but we're not really validating anymore it's just returning the value well that's because the actual validation already happened up here as it goes through the strategy it it again it extracts the jwt from the request it verifies it it validates it and once assuming that that all goes correctly it's gonna decode the payload and pass it down here so there's really not much more we can do in the valley here other than just return the object now one thing that you can do is maybe you have more information about the user in the database right so for example you know maybe we just saved the id and the jwt and nothing else because you don't want to expose that to the external world so maybe here you do something like you know you know maybe you import the user service in here and you get by id of some something right and then you can return that user right but we don't have that we're not going to do that so we'll just keep it simple but this is where you can add logic like that where um once you validate that this is a correct jwt and you've got enough information to find your user object whatever that is maybe it's in your database you can make a call to get that and then you can return that and then again whatever you return here becomes available and request that user so the missing piece that we have here is we need a way to kind of trigger the strategy when we call this this protected route we need to trigger doing that extracting the jwt from the quest and validating it so similarly that we to what we've done before we're going to create another file here remember that you have to create guards which effectively represent your strategies your passport strategies so we're going to create a jwt auth guard dot ts and let me add some code in here save you some time but you've seen this before right so we're creating an auth guard from passport and again we're providing that string to kind of signal passport which strategy are we using in this case we're using the jwt strategy and we do have to register this strategy in the auth module before i forget so we're going to add that in our providers here we're going to add jwt strategy right so this kind of registers it and then the guard allows us to utilize it so now the only missing piece is to use that guard on our protected route so we're going to do used guards and we just made a new jwt auth guard make sure that's imported all right now let's go see if this works alright so i have a fresh slate here insomnia if i if i try to access that protected route right now it should tell me 401 unauthorized because we don't have the jwt access token if i go into my login with username and password and i hit send it's going to generate me a new access token remember we set this up to expire in 60 seconds so i'm going to copy that i'm going to go back to my protected route i'm going to add that as a bearer token paste that there hit send and now we're back to getting our user object so just like before let me try to break this down step by step to explain what's happening so what first what we did in insomnia was we called the protected route without the jwt so it goes into this guard and it kind of triggers this jwt strategy and this is gonna try and extract a bearer token out of the request but it didn't have originally we didn't provide a token so it's just gonna straight up fail and it just goes back with the 401 now we eventually did call the login route again and again this goes back into how the uh passport local works to log us in but the thing we added this time around is this new authservice.login which just takes that user that we got back from from logging in with passport local and then we just turned that into a payload which we signed and turned into a jwt and then eventually we return that jwt to the response to the client and then the client can then take that jwt access token and provide it as a bearer token in the authorization header so that the last time the third or the second time that we called protected in that case it did have a jwt so again it goes into this uh strategy it extracts the token from the request it make sure that it's not expired and then finally it checks again this is where the secret comes into play it checks that it checks that the jwt that we have was something that we made our server made right because in our auth module we provided a secret here and that secret is utilized in our signing in the jwt service that sign in the auth service so so the secret over here just verifies that it came from us and not somewhere else so this is very important and then eventually it says that yep you got a valid token it's going to take that token and it's going to decode it into a payload a json payload and we're simply just getting what we need out of that payload which eventually gets saved into as we return it here it gets saved into request.user eventually we make it down to the actual handling of the request which simply just accesses that request.user so then we return our user object back right so i hope all that made sense uh it might be worth it for me to kind of show you what happens if uh let's say there was another you know malicious server that tries to create a fake jwt but they didn't have our secret right this was hidden somewhere so perhaps they used a different secret string here so if we were to generate a new access token right so imagine this is now a uh you know a hacker trying to make their own access token but they don't have our secret if we were to take that access token and try to use it in our protected route let's see how this behaves we hit send and it's going to say 401 unauthorized so it's very important that the secret that you use to sign your jwt is the same exact secret that you use to verify the jwt so again make sure you put that in a common environment variable or something so that both of these places can utilize the same exact value right so in the auth module and in the strategy configuration all right folks that's really it that's the fundamentals of doing you know typical authentication there could be you know depending on the strategy that you're using it could look a little bit different in your case you know you might be utilizing a different session store or maybe you're using a a different identity provider right instead of doing username and password and maintaining that in your own database maybe you're using you know azure or google or facebook you know any of those typical oidc or oauth patterns you should be able to use that in nest as well but if you have the fundamentals of how do you utilize an existing passport strategy how do you create sessions if you need it and how do you do things like jwt signing and verification that's really most of what i think a huge portion of a huge majority of people would need anyways guys this was a super long video i hope that this was useful definitely make sure to like subscribe you know if anything if you find it useful make sure to uh hit like and maybe add a comment or two or whatever just for it lets me know if the work that i'm putting out provides value to people and also when you do that stuff it helps youtube promote my content to other people so that you know other people are getting that same value out of my content as you are anyways if you want to see more stuff like this i'm definitely going to be making more node.js and front-end content so make sure to subscribe that said i will catch you in the next one [Music] thanks
Info
Channel: Marius Espejo
Views: 17,747
Rating: 4.9722991 out of 5
Keywords: nest, javascript, nestjs, nestjs tutorial, nestjs auth, nestjs authentication, nestjs authentication jwt, jwt, jwt authentication, jwt token, jwt security, express session, express session node js, passport js, node passport, passportjs, node auth, authentication, node, passport js jwt, passport js tutorial, passport js google, passport js react, nestjs passport, nestjs passport jwt, passport local strategy nodejs, passport local strategy, nestjs passport local, nestjs guards
Id: _L225zpUK0M
Channel Id: undefined
Length: 66min 10sec (3970 seconds)
Published: Sat May 08 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.