NestJS & Google OAuth2 with Passport

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
all right welcome back to a brand new video in this video i'm going to go ahead and teach you all how you can set up oauth 2 with google with the nest js framework so that way your customers will be able to log into your platform using google as their third-party provider so i also have a checklist of things that we'll need to do because there are a lot of things that we'll need to definitely cover so that that's why this video will be uh fairly long but we'll try to go through every single point concisely and without wasting too much time so the first thing that you want to do is go over to the google cloud console and you're going to want to create a brand new project so i'll do that by just selecting this drop down i'm going to see a pop-up over here where i can select my projects but i'm going to go ahead and just click on new project uh whoops and i'm going to go ahead and just click on create you can just leave the project name alone you can name it whatever you want but i'll just leave it as this for now so it should take a couple seconds and once it's done i'm going to go ahead and just click on uh select project okay so i'm now working with that project where i actually didn't even select seems like it didn't even create okay there we go just had to refresh the page so i'm working with this project right now so i'm going to go ahead and go over to the search bar i'm going to go ahead and search for credentials so i'm just going to type cred credentials and you're going to select the one that has the api icon not the one with the google maps right because we're not working with that select this one apis and services click on that oops uh click on that and then go ahead and you're going to see this interface over here now since we're working with oauth 2 we're going to need to configure the oauth consent screen first so we can just click on this button or just go over to the oauth consent screen tab and we're going to need to configure this so because we're not working with google workspace there's no point for us to even select internal so for the user type we're just going to select external that means external users like any user with a google account will be able to test this application and our application by default is in test mode just making sure that you all know about that so let's go ahead and just give our app a name let's just call this nest js google oauth 2. for the support email i'm just going to enter my email right over here and i'm going to just go ahead and add my developer email and i'm just going to go ahead and click save and continue you can fill the other fields if you want to for the scopes we're going to go ahead and add only two scopes that's only going to be the email and the profile scope you can obviously add more depending on what your application needs to do on behalf of the user but we'll just only add email and profile that will give us the basic information like the user's email address and any uh personal info that is public so like the full name the the profile picture etc so let's click on update and let's click on save and continue and then for test users because our application is in test mode in order for us to actually use or test our application we need to add the email addresses that's going to be testing the application so i'm going to go ahead and add my email address so and of course if you wanted to test your application with different email accounts you have to add you would have to add those email addresses as well all right and we are done with setting up the consent screen so let's go over to the credentials tab now and we can then create our oauth2 client id so you just click on create credentials select oauth client id go ahead and select web application name it whatever you want and for the authorized redirect uri i'm going to go ahead and just set this right away but basically this is going to be the url that google will call once the user has finished authenticating so whether it fails or it succeeds it's going to go to this url now we don't have that configured yet on our server because we don't have an sjs server running or implemented just yet but i'm just going to set the url right now so we're also working in development mode so i'm going to be using localhost obviously so the port is going to be 3001 so we'll make sure that our nest server listens to requests on that port so it's going to run on port 2001 and the endpoint is going to be slash api slash auth slash google slash redirect and like i said when we implement the endpoint we're going to need to make sure that this endpoint is actually registered like it actually exists that nest js recognizes it okay so uh let's go ahead and just click on create because we only have one redirect url and we are done so we have our client id and our client secret i'm gonna copy these later but you can go ahead and just copy this and save it to like notepad temporarily we're gonna have to i'll show you later on where we need these values to uh to put inside our code base for now just ignore them for now but of course make sure that uh your client secret is kept secret and if you always want to get the values back just click on the oauth client application and you can just look for them over here and if your client secret ever gets exposed or leaked you can always just reset the secret okay so let's go ahead and actually create the nest js application now so i'm in my windows powershell right now i'm in my documents directory and what i'm going to do is i'm going to go ahead and generate a new nest project so i'm going to go ahead and use the nest cli so i'll type nest new and then the project name and i'll call this google oauth2 tutorial and i'm going to go ahead and hit enter and for the package manager i'm going to choose npm you can choose yarn or pnp if you want to but i'm just going to leave it as npm and this will take some time to finish installing so i'll get back to you all once it's done all right so our project has finished generating so let's go ahead and cd into that folder okay so google auth 2 tutorial and let's go ahead and open up visual studio code okay so we're going to have all of our generated files we're not going to really be working with these so if you want to you can go ahead and remove them but i'm not going to remove them i'll just leave it there for now but it shouldn't really affect your application in any way what i'll do is i'll go inside the main.ts file and we're going to go ahead and just change the port to port 3001 because as i said earlier we're using port 3001 and also our endpoints are also going to be prefixed with api so instead of having to manually set the endpoint ourselves to be prefixed with slash api i'm going to reference the app instance so the nest application and i can call the method sets global prefix and then it's going to go ahead and be api so now by default every single endpoint will be prefixed with slash api let's also just run our application just to make sure that there's no issues i'm going to run it in dev mode so we'll type npm run start colon dev and it should be running on port 3001 so i'm going to go to my browser and i'll go ahead and visit the application so let me just show you so we have our application up and running let's go to slash api and you can see that this is the app controller response because we have a control over here and this is the the most base the most of the most base level path right so it's just going to give us hello world okay so ignore that though don't worry about it we're going to go ahead and set up the authentication module so we're going to go into uh the source folder and you could use the nest cli to generate this if you want to i'm not going to but um i will go ahead and do this the only thing that i will generate is a module so i'll go ahead and do nest g module so g stands for generate and we want to generate a module schematic and i'm going to call it auth so i'll generate the module but i'm not going to go ahead and generate the controller or the service class because i personally like doing those things myself or creating those things myself so inside the auth module folder you can see that it created a new folder for us i'm going to go ahead and create a new file called auth.controller.cs and we're going to create a class whoops i'm going to call it off controller and i'm going to use the controller decorator which will register this class as a controller it marks as a nest controller okay and the controller basically allows you to receive inbound requests and produce outbound responses okay so i'm going to go ahead and name this controller auth so this will actually be the actual um endpoint resource right so when we mark this as whatever name we want to every single request that we have afterwards right is going to be prefixed with off and because we have the google prefix set to api that we configured here we need to prefix our auth resource with slash api all right so now that we have our controller let's go ahead and make sure our module recognizes it so inside off.module.ts inside the early braces that is inside this module over here okay we're going to go ahead and add the controllers property and this is an array we're also going to do the same thing for providers which is going to be for things like the service class the strategy and a bunch of other providers so for controllers we're just going to pass in the auth controller class which is going to be imported from the same folder so it just imported from over here and that's pretty much how you can register the controller if i go to my command prompt if i look at the logs you can see that our application restarted and you can see that auth controller was mapped and you can see that this slash api slash auth route has been registered okay so these are some helpful debug logs if i were to remove it if i save you're gonna see that auth controller is not being mapped at all so that's a good way to know if your routes are being registered okay so we're pretty much done with registering the actual controller now let's go ahead and set up two routes that we'll need so we're going to create a method so i'll call it uh let's do handle login so this is going to be the method that's going to take care of handling logins for users okay and we're going to need to decorate this with the get decorator so that comes from the nest js common package and we need to call this method because it's a method right decorators our methods and we're going to pass in the path so that path is just going to be google slash login so the way that this works right is we're basically saying okay the user has to visit slash auth or slash api slash auth slash google slash login and then that request will be sent to this method which is going to be used to handle it so i can go and just return uh something like message google authentication something like this right and i'll show you how this works so let's do that real quick and you can see that i visited slash api slash auth google login and we get the response back okay and we'll also have to do the same thing for uh the redirect url because these are really the two euros that we're building out we're building the main url that the user is going to visit to uh to log in so this url is going to actually have to redirect the user to the google's 2 screen so when the user or the customer visits this endpoint it's going to redirect them to google okay and once the user successfully or fails to authenticate it's going to read their google is going to redirect them back to our redirect url so let's go ahead and use the get decreator and we need to go ahead and define the endpoint as slash google slash redirect and remember it needs to match the redirect url that we configured in the oauth consent screen earlier okay so let's name this function or method handle redirect and i'll just return a message called okay okay so uh i'll just leave a comment here that also just says that this is the slash api slash auth slash google slash redirect endpoint and we're running on port 3001 so if you put everything together it's the same exact matching url that we said earlier okay and you can change it you if you if you want so you don't have to have it exactly the way i have it okay so we're done with this controller for now let's go ahead and exit out of here all right so the next thing that we'll need to do is we'll need to actually configure a service file for the authentication module but because that's not really super important right now we're going to uh skip that we're going to go back to it but i'm going to go ahead and configure the google strategy first just so that you can actually see how passport is going to handle everything okay so what we need to do of course is we need to install the passport package and it's not just one it's actually three patches that we need to install and i'll show you so we need to install at an sjs slash passport okay and passport is basically a library that allows you to implement authentication for your application it's really it's been around for a while so it's pretty much the go to so we're going to install at sjs passport we're going to install passport itself and then we're going to install the strategy library for passport so in our case we're using google oauth2 so we're going to type passport slash google slash or not slash dash oauth2o okay so these are the three packages that we're installing okay so like i said this last one because we're using google we're going to install the corresponding package for the google strategy which is passport google dash oauth20 so we're going to hit enter and we're going to install all these packages and after that's done we're going to install the types file so npm i hyphen d at types passport google oauth20 so we're installing the types file only for passport google oauth20 okay so that's done with we're done with that okay so let's run this again and we're going to go ahead and create a strategy class for our google for google strategy so i'm going to create a folder i'll call this or so inside the auth folder i'll create a new folder called utils and i'll create a file called google strategy dot ts and i'm going to create a class called google strategy and we're going to need to import some interfaces and something from the passport libraries so what we'll do first is we'll import passport strategy and this is actually a function that we're going to import from the nest js passport module and basically what we want to do is we want to actually extend so extends passport strategy and we're going to call this function so this function itself actually returns a class and we're actually we're actually extending the class that it's returning okay i know it sounds a little bit weird but bear with me for a little bit so the argument that we'll need to pass into the passport strategy function is the strategy class that comes from our strategy library which is google oauth20 so from google or passport google oauth to xero we're going to import the strategy class and we're going to pass this into here okay and once we're done with that we're going to go ahead and set up a constructor and inside the constructor we need to call the super classes constructor okay so we do that by using the super keyword and then follow with a pair of parentheses and we're not done yet because inside the superclass constructor is where we need to actually set up the oauth client properties so things like the client id the client secret the callback url and the scope those all need to be configured inside here so go ahead and pass in the client id and paste in the client id for your google application same thing with the client secret same thing with the callback url and then for scope okay so i'm gonna go ahead and grab my client id uh so let's get the client id over here let's paste that in there now i would not recommend just pasting it into the code as is i would suggest you use environment variables but i'm not going to do that right now so just for the sake of this tutorial i'll just leave it hard coded in the code but i encourage you to look up the nest js config package and you can there's actually documentation on the website that you can read about and it teaches you how to use environment variables so i'd encourage you to uh look that up okay but like i said just for tutorial purposes i'll just leave it in the code as is so i'm gonna copy my client secret and i'm gonna go ahead and get my callback url paste that in there and then for the scopes we only have two and they're the same ones that we had configured earlier in the oauth consent screen so it's just profile and email and we're done with this okay now we need to implement the validate method okay and this is basically the method that is going to be invoked right when the user has successfully authenticated themselves and it's going to give you three parameters the access token or this method takes in three parameters and you can get the access token the refresh token and the profile and the profile is actually the the social connections uh details right so for example if you're using google which we are this profile object will give you information about the user's email the name their profile picture link and a bunch of stuff if you're using twitter it'll give you the same thing if you're using discord same thing so you get the idea and the type for this profile is profile but it comes from the package the strategy package so since we're using passport google 20 we're going to import the profile interface from that package over there okay so what i'm going to do is i'm going to go ahead and just console.log everything because we're not really doing anything with this just yet because we don't have sessions or a database so we can't really do anything yet but we'll do that after we see what's going on so i want to actually show you how we can test this okay before we continue though we do need to obviously label this class as an injectable so we're going to use the injectable decorator and then we need to go ahead and add this as a provider in our auth module so in the array for providers we'll pass in google strategy and that's going to be imported from the utils folder right there okay so we're not done yet we do need to invoke passport because right now uh we're not able to invoke passport and the question is how do we do that okay because i mentioned earlier right we have these two urls this google slash login url is going to be the url that customers will be uh that you'll send the customers to and the purpose is supposed to redirect the user to google but in order to do that we need to actually invoke passport because passport's actually the middleware that's going to handle the redirection so how do we invoke a passport well what we need to do is we actually need to set up what's called a guard so it's done differently in sjs if you've done this in express before in sgs it's done a little bit differently so we're going to need to configure a guard okay and i'll show you how we can do that so inside the utils folder we're going to create a new file called guards.ts and pretty much guards allow you to protect certain endpoints in your application so if the user is not authenticated you can just send them a 403 or a 401 okay so let's go ahead and implement our guard so the guard is just going to be a class so i'll call this google auth guard and we're going to extend the off guard right over here and this is a function that also returns a class as well and the type we're just going to pass in the provider or the uh the oauth provider which is google like that okay and then what we want to do is we want to implement the async and activate method and type not typescript vs code is going to auto complete a bunch of this stuff i'm just going to remove this annotation because we only care about the context and let me remove this observable import up top here so inside here what we want to do is we want to go ahead and get the value that is returned from can activate or the super classes can activate method call so we're going to reference the super class and the super class has a method called can activate we're going to pass in the context okay and this is an asynchronous method call so we're going to await this okay and pretty much what we're going to do here is we're also going to do one more thing we're going to go ahead and cast this value as a boolean okay then we're going to go ahead and get the request so the execution context actually gives you a lot of different methods and properties so i would suggest you read more on execution context if you want to learn more about it on the next chance talks they have well documented stuff on the official sks docs so just google execution contacts next js and you'll learn more about that but what we want to do is we want to switch the protocol to http because we're using http and we want to call the get request method okay and then what we want to do is we want to go ahead and call super dot login and we want to pass in the request object there so in a way similar to express it pretty much uses this method to actually log the user in okay and then finally we want to return the value of activate okay so we're pretty much done with this off guard don't worry so much about what's really going on here all you need to know is that this off guard acts as like a middleman and it takes care of actually passing in the request so that it can actually authenticate the user okay so we also want to make sure that we label this or mark this as an injectable and we're pretty much done with this next thing that we'll need to do is we'll take this off guard and we're going to go ahead and uh use it for both of our hana login and handle redirect methods so we do that by using the use guards decorator so on top of our method we'll call use cards and we want to pass in the google auth card class so google auth guard and we want to do the same thing for the handle redirect method you want to do it for both okay so now that we've done that let's go ahead and actually test out our implementation so let's go over to our console make sure our app is running and let's go over to our browser and let's go ahead and refresh everything and make sure that everything works so let's refresh so you can see that it redirected us to our google oauth2 page okay if i actually were to just get rid of the used cards right if i save and i were to go back to that endpoint again slash api slash auth slash google slash login nothing's gonna happen okay so it's this used guards decorator along with the actual guard itself that walks us through like the whole middleware that takes care of redirecting us to the google oauth 2 page okay so now we can go ahead and revisit this url and what we can do is let's go ahead and look at our console we can see that nothing is here currently but when i go ahead and log in to my account you're going to see that one it's going to redirect me to the redirect url rightfully so right though it's giving us this 401 unauthorized and it makes sense because we don't have anything like sessions enabled uh we aren't returning anything from our strategies validate uh method right we look in the validate method we're not returning anything so since we're returning undefined right that's a false value so it's going to treat that as if the user did not actually authorize themselves but let's look at the console and let's take a look at what we actually logged so you can see that all this stuff over here is being logged from this validate method so this valid method was invoked okay and we were able to successfully get the access token the refresh token is undefined i would encourage you to actually read more about why that's the case um because there's a specific reason why so you can look that up on google and they'll tell you how to handle it but we're not going to get into that right now we have the profile object and you can see that we have the id we have the display name we have the name emails photos and a bunch of different properties okay and you can do a lot of stuff with this you can save this to your database uh you can you know use this to this you can send this back to the user so they can you know get information about themselves do whatever you want okay but that is pretty much the whole gist of the google auth 2. now are we doing anything meaningful with this data no right we don't we don't have a fully working authentication system yet but right now what we've done so far was we have google oauth 2 being correctly invoked the next step and for the rest of this video we'll need to actually work on setting up sessions as well as setting up a database right the reason why we need to set up a database is because we need to connect to the database first and then we need to actually save the user to the database okay once we have the user saved in the database we can then start doing stuff like serializing and deserializing the user upon every request and then we can save users to the sessions whenever the user is logged in and that's important because if you want to implement a login feature where it's persistent and users can stay logged into the application then you must have something like sessions okay so we're going to go ahead and move on to the next part of the rest of this tutorial which is configuring all that stuff all right so let's quickly look at our checklist real quick so we've done a lot of stuff so far in the first part of this video so we were able to create an oauth 2 app we're creating this project the next js project we create the auth controller we create the strategy we also create the off guard so the only things that we have not done yet was create the auth service and there's there was a reason why i decided to actually delay that because i wanted to actually show you all how to invoke passport but we'll create the auth service in this part of the video we're also going to go ahead and connect to a database and i should also write that we're going to go ahead and create a type orm entity because we already we are going to be using type rm and then we'll configure sessions so if you go over to the nest js docs and if you click on databases right over where is it database over here okay uh there's actually a lot of different uh integrations that you can read about so for example if you want to use mongodb you can and they do have docs on that if you want to use it with mongoose that is um if you want to use something like prisma if you want to use microorm sqlize they have a bunch of documentation on how you can set this up now i personally like using type rm i've been using it for all of my projects and i absolutely love it so i can advocate for that okay so we're going to be using type orm alongside with mysql now it doesn't really matter what database you use because in the end the data is going to be saved in the database regardless okay so you can choose whichever one you want but for this specific video i'll use type rm with my sql so what we're going to do is we're going to install nest js slash type rm we're going to install type rm and then we'll install my sql 2. so let's go over to our application and let's install at sjs type rm type orm and then mysql2 and once that's done installing we'll configure the type orm module and so i'm here i'm going to have to pass in my credentials for my sql database i'm using a localhost database so it doesn't really matter but you can also connect to a remote database if you want to okay so uh once that's done we'll go ahead and import the typeboard module so it's done let's go ahead and do this so what i'll do is inside app module.ts i'm going to go ahead and import the type orm our module right over here it's going to be imported from at nsjs type rm and we're going to go ahead and call for root and this is where we're going to pass the type or module options so i'm going to set the type to be my sql because we're using my sql if you want to use something like postgresql you can okay but i'll use my sql for the host it's going to be localhost uh and i'm going to need to pass in the username password and the database name so give me one second i need to grab that so for the username i have test user with the password it's test user 123. for database we're just going to we're just going to call this google oauth 2 app um and i think that's actually most of the ones that we need i think we need a couple of others oh we need to specify the port let me do that after the host the port by default for a mysql server is 3306 and uh we also need to specify a entities property and i'll show you how we can create a type or nc and then we'll set synchronize to true okay so we're done with that okay now let's go ahead and just run our application and let's just make sure that the connection actually works it is going to throw an error because our database does not exist so we'll have to manually create it oh yep so it's able to connect to the database or it's unable to actually because the database does not exist but the username and password though do work fine okay so that's good so how do we actually create database in my sql well assuming that you are using my sql all you need to do is just log into your mysql server and you can do that either using the cli tool or you can use mysql workbench however you choose to do it it doesn't matter but right now i'm in my and i'm in my mysql console okay and i'm going to go ahead and just type create database and then the exact name google underscore oauth2 underscore app and then semicolon and then we're done so that we run the app now it's going to go ahead and show you that everything is going to work fine perfect so our database has been successfully created so what's next so the next thing that we need to do is we need to create what's called a type orm entity and basically the entity is going to allow us to map actual javascript objects into actual records in the database right so we had a regular user a database table that had fields or columns such as id username password email etc etc all of those columns would have to map over to an actual javascript or typescript object in our case right so and that's the beauty of orms is because they take care of all this for us okay so we're going to go ahead and create an entity so i'll create a new folder called type rm and inside this folder i'll create a folder called entities and i'll create a new file call it user.ts and what i want to do is i'm going to create a class called user and i'm going to annotate this class with the entity decorator and this is going to take in some options and i can name this whatever i want but i'll just leave it actually i'll just leave it as users actually because i think by default it'll just be called user all in lower case so it's whatever you name the class it's what this is going to be named by default but you can always configure that okay and what we're going to do is we're going to define uh properties for our user now here's the thing though um you obviously want to make sure that your user is going to have relatable properties right and since we also are using google oauth 2 we kind of need to make sure that the properties for the google oauth 2 application relate to our actual user entity in some way so for example when we authenticate using google oauth2 we have properties such as the google id we have their display name we have their profile picture we have their email address right so you have to determine which of these properties are very important so i personally think that the email address is important so we'll create a field for that okay and i'm going to go ahead and set this to be a column by using the column decorator just like that okay now uh what i also recommend for your entities is every single entity has what's called a primary generated column and this will act as a primary key okay you can also have your own unique ids too but it's important to have well i'm sorry let me let me rephrase you can have unique ids that come from third party providers but it's also important for you to have your own unique primary keys for your own local database and not using just like you know google's ids for example and using that as a primary key okay so uh let me go ahead and actually do this i'm gonna actually just relog in because i don't remember what properties exist so let me do that real quick okay uh if we look in the console okay so this is what i was talking about you see over here this id this is the google account id i don't want to use this as our application's primary key or our user's primary key now you could use this and display to the user right or you don't even need to save this but it's only if you need it you can save it but i'm not going to bother saving that though what i will save is i'll save the display name um i could also save emails as well um and i'll also save you could also say the pro picture but i'll just i'll just say the display name and the email okay so we'll do our own primary key email display name and i think that is it yeah like if you need more you can always just add more properties if you need to but this should work just fine for now okay so now to actually uh register our entity and have it actually be created as a table in our database we need to actually pass in our user class into this entities array so i can go ahead and just import that from slash type or m c user okay and if i save let's just make sure everything's good okay let's go into our database so i'm going to go ahead and type use google off to app because we need to step into the database and if i type show tables you're going to see i have my users table and you can see that we can describe the table and you can see that literally exactly what we had created for our entity as a typescript class it was converted into a table and generated inside our database which is great and that's why rms are beautiful and powerful okay so that's pretty much done we're done with a created entity now we need to do is we need to actually create the actual user so we're going to actually have to write some code for this and to do this we need to also create our auth service so let's go ahead and do that so inside the auth folder i'll create a new file called dot auth.service.ts and i'm going to create a class called auth service and i'm going to go ahead and label this as an injectable because this is going to be a provider okay and the auth service class is going to take care of authenticating the user well not only authenticating user but it will take care of actually uh validating the user is what i just say right the authentication actually is controlled mostly or primarily by the validate method right because over here if we return a truthy value that means the user is successfully authenticated if we return a false value that means the user is not authenticated so the value method is actually the primary method that controls the authentication mechanism the auth service is really just to implement business logic so it's mainly used to connect to the database check to see if the user exists if it doesn't create it if it does return that user okay so first let's go ahead and create a method called validate user and this valid user method we will need to take in details right we're going to need to take in the user email and we're going to take in all the properties that we want to save to the database so what i'll do is i will create a new let's see my cranium folder called utils and i'll create a file called types.ts and i'm going to go ahead and create a type called uh user details okay and uh what i want to do is i want to mock the user right i want to label i want to like design what the user is supposed to look like so the user is going to have uh or actually it's going to have something like this it's going to have an email which is a string it's going to have a display name which is a string as well and it's going to primarily have these two values okay and that's going to be all we'll all need for now so let's go back to our service and we'll pass in a parameter for this method so this method is going to take in one parameter and the type is going to be user details okay and this valid user method is going to need to clear the database it's going to need to search for a user with a unique value and we know that email addresses are going to be unique okay because everyone is because the email address itself belongs to only one person right so we can use that to search the database to see if that user exists if it doesn't then we'll create the user if it does then we'll just update the existing values and then we will return we won't update the values though we'll just return the existing user but you guys can take care of updating the user if you want to okay it is a good recommendation that i uh that i encourage you to do okay so let's just go ahead and console.log auth service and we'll console.log details just so that you all can see every single step that we are doing and the next question is how do we actually query the database inside auth service right so with a framework like sjs the way with the way that we need to query databases is we don't just install some kind of database client and then just like use like a raw query string or like a like a raw statement to query database we actually use what's called repositories okay we use the repository pattern and the nestjs docs actually has a lot of information about repositories and they show you how you can actually use them okay it's actually very easy so what we need to do is inside our auth service class we're going to implement the constructor and we're going to use the inject repository decorator and this comes from the nest js type arm package [Music] and then we're going to pass in the user entity just like that okay and then we're going to do private read only and we're going to call this field user repository and we can type annotated as repository which is going to need to be imported from the type rn package and this is actually a generic type so we can actually specifically uh pass in the user type itself like that okay so now we know that this is specifically a user repository and not just a regular repository because it's generic okay now we're going to get an error and the reason why is because we need to actually um add the user as an actual um provider the reason why we haven't gotten the area is because we have not added the auth service as a provider so we need to actually do that before we actually do anything else with it so let's do that real quick so i'm going to go ahead and pass in an object and this is how you should actually register your providers by using tokens so how what do i mean by that so you're going to pass in an object and you're going to set the provide property and this is basically going to be the token that you're going to be using when you want to inject your off surface wherever you need to inject it inside your application so i'm going to call this token auth service and then we're going to set the property use class and that is just going to be the auth service class so our service class so it's looking like this and now you're going to see that we're going to get the error right because we just registered our provider so it's not complaining about the way that we registered the provider it's specifically complaining about the user dependency and if you read the logs it tells you that uh it cannot resolve the dependency of the auth service class and it tells you if user repository is the provider right is it part of the current auth module and we haven't actually registered the user as a provider yet if i were to delete this part over here you'll see that the error just goes away okay so let's fix this error inside our off module so basically any module that you use type or entities you'll need to actually configure those entities and the way you do that is inside a module inside the object you're going to go ahead and specify the imports property which is an array and you're going to go ahead and import the type orem package and you're going to go ahead and call the for feature method and this takes in an array of entities for type rm and all we're going to do is just pass in the user entity so we're going to have to import that from the type orrm folder and i'll just auto import that and if we look at the logs the error goes away which is great all right so i know we've done a lot so far but let me just recap what we did we basically uh injected the user repository the repository is going to allow us to actually query the mysql database on behalf of the user table okay so we don't have to worry about writing actual sql statements we can just go ahead and reference this.user repository and then we can call a bunch of different crud related methods like find one find buy create delete a bunch of other different methods right to perform operations on the users table okay and then we also just registered the auth service as a provider and we also need to configure the type aura module inside this off module because we're using repositories okay because it's part of a dependency okay so what do we do next the next thing that we need to do is we need to actually call validate user and then inside validate user we're going to need to query the database by invoking the user repository find method so let's go ahead and piece everything together so let's go into our google strategy file and inside the google strategy file is where we're going to need to inject our auth service class because we've created the class but we haven't done anything with it yet okay so we're going to inject this off service class so inside the google strategy class we're going to go ahead and inside the constructor we're going to go ahead and use the inject decorator and this is how you would inject any service you'll use the inject decorator and then you're going to pass in the token so the token is literally what the value of provide is right so it's off underscore service and remember if you register multiple different services you want to make sure that the provide value is different right because underneath the hood and sjs uses a container to keep track of every single dependency and it it uses the token to make sure that it maps to the correct instance of the class okay so if you have multiple classes or multiple dependencies that are mapped by the same token there's going to be problems okay so we're going to pass in the auth underscore service token and then over here we're going to just set this to private read only and then we're going to call the property user service and then we'll type annotate this with our class so off service just like that okay let's look at the logs no errors now watch what happens if i actually remove this as a provider we should be getting an error and it's going to say that uh it cannot resolve the dependencies of google strategy and that's because auth service is a provider and we are not using dependency injection when we are trying to inject this in our google strategy file okay so just make sure you have this configured otherwise you're going to get that error all right so now that we've uh injected our user service actually i'm sorry i didn't mean to call it user service i meant to call this off service my apologies okay so what do we want to do now well we want to go ahead and call validate user so inside the valley method we're going to go ahead and call this.authservice.valid user and we're going to pass in details so what do we want to pass in exactly well we want to pass in uh the email as well as the uh i think it was the uh the display name right but how do we get those values though so let me actually re-login again because i don't remember the values so let me just do that real quick okay so uh to get the email so this is an array so i guess what we'll do is we'll reference profile dot display name and then for emails we'll do profile uh and then we'll just hard code it by doing subscript zero so here's what i'll do so let's do this for email we'll do profile dot emails subscript zero and then for display name we'll do profile dot display name like that uh let's see what's going on over here i guess it doesn't like that um oh wait do i need to oh value there we go whoops my bad okay so profile dot emails at subscript zero because emails is an array dot value okay so remember the valid user method is going to be responsible for returning a user and the thing with oauth 2 is that since we're using third party providers it actually never really uh is supposed to actually fail uh on behalf of your platform's application if it fails it fails because of the oauth 2 provider right because for example if you're using username and password right uh it can fail if the user if the user provides an incorrect password right but for oauth2 it only fails when the google servers returned an invalid response and that could also be because they may have entered the wrong credentials on the on google platform okay but realistically with oauth 2 it's always going to return a user okay so we'll still have to but we'll still need to obviously implement the corresponding logic to handle situations when it does end up being undefined but theoretically it shouldn't though but you'll you'll see just a second so let's just assign the value to this variable called user whatever the return value of validate user is and now let's go ahead and look at the logs so i'm going to go ahead and go to the login route and if i look at the logs you're going to see that we have auth service and the user details being logged so that's good we haven't done anything yet so we need to go ahead and query the database now so let me go ahead and add the async keyword in front of this method because we are going to be using async 08 so what i'll do is i'll go and do const user equals await this dot user repository find one and we're gonna search by the email so let's do find one email and then details dot email okay and what's gonna happen is if the user if the user is found will return the user okay and if the user is not what's going on over here let me see email string is not assignable to parameter type find user options uh and email the why is it saying that i'm kind of confused give me one second all right i believe that the actual method that we need is fine one by might have been a new update with the package i'm not too sure because i've always used find one before but i think we need to use find one by and that's fine so specify the email you can see that now email actually shows up uh as a searchable as a property for our query so we'll reference details dot email and it makes sense to use the email to query the user because the the google user cannot change their email address okay so that means that you'll never have to worry about it about getting the incorrect user so uh okay so let's go ahead and log whatever the value user is so if the user is found like i said uh in an actual application you can just update the user's properties and then return the new user but i'm not going to do that because i don't want to prolong this video but i would encourage you to as an exercise to do that yourself and update the user yourself before returning it okay but if the user is not found then we'll create the user and to create the user what we do is we go ahead and call the create method and then we just pass in the properties so email and i can just actually pass in details like this because details contains the same properties that match the type orm properties as well the id is auto generated but email and display name need to be set okay so this method does not need to be awaited because it's not an asynchronous method okay but we do need to go ahead and actually save the user so we can do return this dot we're not this new user not just a or i'm sorry it's return this dot user repository dot save new user and this will basically now this is a an asynchronous call but that's fine because we will await that call and we are doing that okay and when we return this value it's going to give back the new user okay so let's go ahead and write some more logs so let's do console.log user not found reading i just want to write these logs out so you all see the whole process and how everything works okay and then what we'll do here is we'll say return user we'll use the ternary operator so or actually we just do this um we can do if we can just do return user or no and since null is a false value that means that the whole authentication failed we're not really filled but like it went through and the user was not authenticated which means that it's going to give them a 401 okay so if the user was found return it if the user is undefined or a false value return null and i'll console log everything like i said so you all see how everything works so let's go ahead and test it out now so you can see that now it's giving us a 500 and that's fine it's something to do with these sessions okay but you can see that if we look at the logs you can see that we have we have null being logged and that is coming from right over here because the user was not found it says user not found creating and then it logs validate and then it logs the user itself okay and uh right over here in the valley method it's returning the user itself okay and right now the reason why we're getting this error is because what's supposed to happen next is it's supposed to take care of serializing the user uh into the session itself so the user can actually stay logged in now that is actually the very last thing that we'll need to do is just configure the session there's also one more thing that we'll need to do which is configuring the session store but i have videos on my channel that show you how to configure that so i'm not going to go over that in this video because we're already at the one hour mark anyways but i will show you how we can configure sessions it's actually really simple so what you're gonna do is you're gonna go back to your console and you're going to go ahead and install the session package so let me go ahead and show you where you can read more about sessions so if you go over to the sjs docs under techniques session it teaches you more about this you can read more in depth about it if you want to but we're just going to install the express session package and then we'll install the types as well okay and once we've installed that we just need to enable sessions in our application so we'll go into the main.ts file and we'll import the express session right over here like this so import asterisk ask session from express hyphen session okay now one more thing is that we will also need to import passport because we are using passport with session so whoops it should just be passport okay and what we're going to do is we're going to go ahead and enable sessions first so let's do app.use so it's similar to how you would enable session middleware in express.js so app.use you call session which is a function and you pass in options for the session so for the secret this is just going to be some random string which is used to encrypt and decrypt the cookies um i'm going to go ahead and set save and initialize to false as well as resave to false and then we can go ahead and specify the cookie property which is an object and inside this object i'm going to set the max age of the cookie i'll set it to be let's just do one minute you can set it for however long you want and i think that is for the most part majority of the properties that you need set um yeah that's pretty much it okay so now that we have sessions enabled we do need to also enable passport with sessions so all we do is just call app.use passport.initialize and then app.use passport.session okay and then we'll also need to do this we need to go into the auth module throw right over here or i'm starting on the auth module the app module and then we need to actually register passport as well so what we do is right over here we're going to go ahead and import the passport module from sj's passport and you're going to go ahead and call register and you're going to set session to true like this okay and that should take care of enabling sessions with a passport okay um and i believe that should be it for everything else we've initialized it we've enabled sessions uh and we also enabled sessions as well um and yeah so that should be it with sessions okay now we're not quite done yet the last thing that we need to do on top of all of this i know it's a whole lot of process but like i said it's it's it's a long process but hopefully it's not that difficult what we need to do next is we need to uh set up the serialize and deserialize methods so again it's similar to how you would do that with express okay but um it's just done differently so inside the auth module inside utools folder i'm going to create a file called serializer.ts and we'll create a class called session serializer and this class is going to extend the passport serializer class like that okay and what we want to do in this class is we want to go ahead and do this we're going to inject the auth service same way how we did it in the google strategy class it's like this okay and then what we need to do is let's call this superclass constructor as well we need to go ahead and implement the serialized user method as well as the deserialize user method and basically similar to express right the serialization method is used for serializing the user into the session so it does that whenever you log in okay and the d serialize user method is used for restoring the session so it takes the session and it figures out who the user who the session belongs to okay and inside this d serialize user method we actually need to uh find out who the user who the session belongs to and we do that by searching for the user in the database so there's actually going to be one more method that we'll implement in the auth service class but we'll do that in just a second first thing i'll do inside the serialize user method is i'll go ahead and call done pass in null and then pass in uh the user object and instead of passing in instead of type annotating this user object as any we'll type annotate it as user the user entity and it's giving me this linting issue i'm just going to go ahead and ignore this for now okay and for the d serialized user method uh the payload is also the actual user object itself but i'll just leave it as any for now because it could it could theoretically be anything okay and also disable the uh the eslant for this line too whoops not that one uh it should be let's see this one i'll just disable for the entire file there we go okay so inside this method we will need to call a method that's going to search for the user so we're going to need to reference this the auth service and right now we only have the validate user method which only searches for the user but or i'm sorry it only it only evaluates the user by searching for the user and creating it if it doesn't exist right but what we want to do is we want to just call a method called find user that we're going to implement right now that's going to take in the user's id so we're going to do payload dot id like this now like i said this is this is obviously going to be a user uh instance so if you want to you could just actually type annotate this as a user if you want to but i'll just leave it as payload id and remember the user instance itself has the id property it's the primary key okay you're going to use the primary key to search for the user and what we're going to do is let's just await that call and then we'll store the return value into this user variable okay and if the user exists so we'll use a terminate operator so if the user is truthy we'll go ahead and return null for the error because the first parameter is the error object if there's no error you just pass a null and for the second parameter it's the user instance itself however if the user was not found we pass in null and null which is basically means that there was no error and there was also um no user found which means that this would actually fail uh which means that the user is not authenticated so it would say 401 okay because we passed null for the user but let's go ahead and implement this find user method inside auth service real quick and we need to take in the id which is a number and what we'll do is we'll go ahead and create a variable called user and we will reference the repository and we'll call find by or actually i think we just do find one and we should be able to pass in the id i'm not sure why though it just doesn't let me specify options but i guess we'll just do this we'll do find there there used to be a method called find by id but i think they got rid of that but would you do find one by and we'll pass in the id like that okay and we'll just return let's see i think we'll just return user and um if and then we'll have to just check to see if the user exists or not which we are doing over here which is good okay and i'll also just go ahead and log everything as well so let me log everything the serialize user and then serialize user and we'll also need to label this class as an injectable and we're going to need to add this as a provider in the auth module so let's do that session serializer okay and that is going to be it for the sessions so let's run the application again and let's test everything out okay so let's go into our application let's log in and you see how it says okay okay and you can look at the logs and let's look at what's going on you can see that it says serialize user or serializer user okay now if i were to actually visit any endpoint right now let's go to slash api you're going to see that every single time i visit any endpoint it's going to say deserialize user because every single request is going to take the request it's going to figure out who the who the request who the session belongs to right and it basically it basically validates if the user is logged in or not it validates the session itself okay so if the if the user was actually no longer like logged in if they were not if they did not have like a valid session it would know that and it would tell you it would tell you that the user is not logged in okay but uh that is pretty much how you can handle sessions in this situation so uh for the most part our whole implementation works okay and if you wanted to protect your endpoint you could very much do so very easily actually for example inside the auth controller uh if you were to actually get uh the users object so let's go ahead and do this let's create a quick little method uh user and what i'm going to do is i'm going to get the request i'm going to get a request object so i'm going to import that from the express library and i'm also going to use the request decorator that comes from sjs common and if i were to go ahead and log request.user okay and i'll go ahead and do return request.user like this well let me do this actually let's do if rec.user return authenticated else return message uh on author uh not authenticated let's just test this out real quick okay so let's go ahead and visit slash api auth slash i think it's users what we call it right no we call it status you can see this is not authenticated so let's log in now so let's log in it redirects us to the redirect url now let's visit the status url again you can see it says authenticated okay and the reason why this works is because the session has been serialized and when it serializes session it actually attaches the user object onto the request object again if you've done this with express before the concepts are very similar it's just the way that you do things are a lot different okay so that is going to be pretty much it for this whole video again all the code will be in the description so if you have any issues uh you'll have like a reference okay again i know that the video was really long but i didn't want to break this up into separate different videos because i know some of the feedback that uh people have given me was that it's really hard to find out the next videos um so i figured you know what i'll just put everything in one video and see how everyone likes that so if you have any questions i have a discord server or you can just leave a comment down below but the best way to get help is by joining the discord server and asking your questions there i'll be more than happy to help you all okay so go ahead and go on the github link uh that's it the link is in the description download the code play around with it modify it to your needs but you have a fully functioning uh oauth2 with google uh working right now i'll make some follow-up videos on how you can actually use this existing template to configure other oauth 2 mechanisms such as twitter discord and a bunch of other stuff so once again thank you for watching and i'll see you all in my next videos peace out
Info
Channel: Anson the Developer
Views: 13,855
Rating: undefined out of 5
Keywords: nestjs, nestjs oauth2, nestjs google oauth2, nest authentication, passport
Id: OitgkKTxht4
Channel Id: undefined
Length: 72min 18sec (4338 seconds)
Published: Mon Aug 15 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.