Next.js 13.5 Authentication: Mastering Role-Based Security with AuthJS/NextAuth

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
what's up Clarity coders in today's video we're going to expand on our next JS series and show you how to use next o or soon to be ajs to create your own login system we're going to cover role-based authentication on both the client and server side with ooth providers that you probably use already like Google and GitHub and then we're going to show you how to spin up your own credential provider so this is if you want to have your own email and login system without using Google or GitHub we're we're going to hook that up to mongod DB Atlas for free as well all in this video it's all inclusive let's not waste any more time and Jump Right In All right so here we are in Visual Studio code I'm not going to do a lot of setup you can watch one of my other videos for exactly how you can get some of this installed make sure you have no JS have some familiarization with nextjs projects would probably be helpful I also use a couple extensions that'll show really quick this es7 plus react redex code Snippets it's very useful I'm going to use it a lot in this video so you can go ahead and download that prettier as well for formatting code uh you can see the others in here that I use feel free to freeze and install those if you would like let's go ahead and spin up this project so I'm going to open up a new terminal you can code along with me I really feel that's the best way to learn so that's how I like to do these tutorials and let's do MPX create next app we'll go ahead and click yes to Pro Pro seed we can call this project our next off tutorial or whatever you would like I'm not going to use typescript so you're you're able to convert this typescript if you'd like I'm going to select no I would recommend if you're following along to do the same I'm going to use eslint Tailwind yes Source directory I'm going to say no app router yes and would you like to customize the default Alias no no so we'll let that spin up this project and then we have should have some boiler plate to work with great so now we have our project spun up you can see where it created the project file I want you to go ahead and open up that folder and then we'll continue on from there so if you're using visual studio code you can do file open folder and then pop open that window and you should end up with something that looks like this so now we have our project folder open I'm going to do one more install here so I've opened up another terminal let's go ahead and install next off because we're obviously going to use that so we'll do mpmi next- off and then let's take a quick look at our package.json here you can see my exact dependencies you can copy these if you want them to work perfectly so you can see I'm using 13.5.3 any version above this should be okay um if not try installing with this exact version and that could help uh same with nexo now let's get to cutting down this app a little bit let's expan the app folder let's go out to our Global CSS let's delete all of this in here and then I will save we only really are going to use two things let's go ahead and set it up just so we're done with it so we'll do at layer base bam we can do an H1 which we're going to use just to make things look a little more like we're used to we'll do font bold text- 3XL just for the correct text sizing just so it looks a little better then we'll also do an a tag we'll do at apply let's do underline and font bold and again this will just help kind of visualize what we're doing as we're doing it nothing too crazy here and then let's open up our page I'm going to go ahead androl a and just delete all this out and this is where my extension is going to come in handy I'm going to do RAF CE a lot that's that code snippet one that I showed you and then control shift L and this can just be our homepage so I'm going to change all these at once nothing crazy here and we can close that close our Styles we should be actually done with those honestly let's rename this to page.js just so it's consistent there and then let's create all of our other Pages for our application I'm going to Showcase some different types of authentication on some different types of pages so that's why we have multiple so let's do new folder here we are going to have a member page so this is a private protected page for only people who have signed up so we'll do a member folder and then a new file in here that we'll call page. jsx and then on that member page we can do RAF CE just like before control shift L and we will do member and then let's add in H1 tags here and this is going to be our member server session so remember if we don't add use client up above our import statements it's going to render the page on the server side so this is going to show how we can protect a server side page on our application so let's create a new folder and then as you probably could guess we're going to do a client member page so let's create a client member folder and then I'm just going to copy this page paste it in here we'll close this one out to make sure that we're using the right one and then you'll see this is our client member page so let's use a member client session for this one and then of course to ensure that this is rendered on the client side we have to explicitly tell that we want to use the client side so I'm showing you this this shouldn't be your default you should never just default to rendering a page on the client side but there are certainly occasions where you'll have to so this is our example of that we are also going to have a denied page so most of the time we're going to Route people to a login if they haven't authenticated but what if they have authenticated and they just don't have permissions in role-based authentication in that scenario we're going to direct them towards a denied page so let's just create another folder at that application Level for denied we'll create a new page. jsx inside of this here we can do rafc control shift l we'll do denied we'll use H1 tags for this again just because make it look a little better there and then we will do a tiny bit of styling now this is this is a stretch when we're calling this styling but let's say text- red- 400 so this will just be a red denied that we send people to if they don't have the correct authentication for our page and then let's do one other so this is going to be eventually our page that's protected by roles so only an admin eventually will be able to go to this page so let's create a create user folder and then inside of that let's create a new file called page. jsx and then r a c control shift L and we will call this create user we'll say only admins now this is eventually it's not going to work like that out of the gate but we have our structure in place I think that's most of the pages that we're going to need in this project as well so we should be good there let's create another folder so let's say new folder we're going to use uh open parentheses and then components o compon like that and then new file let's do our navigation. jsx I'm going to use a capital N with this this will just be our navigation bar up top that we can get set up now we can do our normal rafc that all looks pretty good now instead of here we're going to do a little more coding this time to get this set up and then we should be good to go let's do a header let's also do nav tags so for our header let's do class name we're not going to do we're going to do almost no styling but BG gray 600 and then text Gray 100 should be good and then on the nav side let's do class name is flex justify between items Center width is full let's do a px of 10 and finally a py of four that should be good let's close out of this terminal window for now just so it's a little less distracting and we'll go inside our nav tags here so for the first div let's just do my site and then for the second div and our only other component in here let's do a class name of flex gap of 10 that should be good then we're going to add in some links make sure you're doing the import as well so I'm going to tab complete here and that should do the import you'll see that it did import link from next link we need an href for this so we already created our homepage it's at slash we can call this home let's copy this so we got home we got create user member only client let's do a public page so we got home then next let's do our create user page now remember eventually this will only be for admins we'll do create user let's do our client member page so again this should be protected you have to be logged in you don't have to be an admin let's do our member page this is almost the exact same thing except it's rendered on the server instead of on the client side and then let's do a public page which is completely unprotected we'll do public here that looks pretty good now we can go ahead and go into our layout JS we're almost ready to get into nextjs this is the last tiny bit of setup here so let's delete out the Google fonts we don't care about that we don't care care about that and then make sure you kill the reference to the Google fonts as well should be good and cleaned up you can change this if you wouldd like we're not worried about it here we got our HTML we got our body let's do a class name of BG gray 100 so this is just the background let's import our navigation let's do na like that make sure you do the import here so I'm going to tab you'll see it imported our component from above we can self close so slash and then our greater than sign and then let's just wrap this in a div as well this these children here just so we can do one other tiny bit of formatting div this class name let's do a margin of two on it should be good to go okay so we did some coding we got the structure set up we're going to work in next off next let's open our terminal just make sure this runs let's do mpm runev we got no errors that is a good sign let's check it out in the browser we're going going to go to Local Host colon 3000 dang look at us web development who says I can't style applications this is beautiful you can tell what page we're on by the our little reference to that page so you see we have a create user client member member and our public page we didn't do anything with the public page let's fix that up and let's just check real quick we should have a denied one as well yes so we are all good there let's make a quick change to the public page just so we can reference that we're on it oh I didn't route to it at all did I yeah not found I didn't create the public page so new folder let's do public inside here new file page. jsx rafc control shift L let's do public delete that out put our H1 tags just like we did for the others I didn't for the create user because we're going to do something else that we're actually going to create users towards the latter half of this okay so we should be all good we got all of our Pages we don't have any authentication set up whatsoever but we're going to get into that next let's go ahead and jump into that so we know we need to create an API folder and that's going to be what next off looks for so let's create API folder inside of that we need to create another folder called op inside of that so I'm right clicking on off here new folder I'm going to use square brackets and then inside of those square brackets I'm going to do dot dot dot three dots next off and then inside of this you'll typically see one of two things so you might have a route well you need to have a route. JS and you might have an options. JS which are going to lay out the options of your project so we're going to follow that format so let's create a new file and let's do route. JS and then in the same folder so inside that next off off folder we're going to create options. JS as well let's start with the route. JS just so we don't forget it is the simpler of the two because we're going to import in our options so let's do an import I'm going to close out my terminal just because it's distracting let's do import next off tab auto complete you can see I'm getting next Das off slash nextt I don't know that I want that let's delete out the slash nextt let's let's just do next off from next off and let's do import options plural from slash options so this is going to import our file from our other directory let's do const Handler equals next off options and then we're going to export Handler as get Handler as post now this is straight from the next o or OJs documentation so we're just setting this up basically how they have it set up I'm going to close out of these other tabs I actually think we're done with route. js2 I'll close out of that and now we can set up our options here so this options file is going to be where you import and set up how you want people to be able to authenticate with your application so we're going to start with two and these are builtin providers by next off so we don't have to do or we don't have to do very much setup with them so let's do import GitHub Provider from next- off SL providers slash GitHub let's copy that paste it below let's do this is a capital G Google provider and we're going to do the same thing except at the end it's going to be Google as well then let's do export const options equals providers colon square brackets so here we can add an array of the different providers that we'd like to use in our case we want to use the GitHub provider parentheses curly brackets and then inside of here we can set up anything that we would like for our GitHub so we can add extra rols we can add extra features or we can leave it as plain Jane as we want and just ship it that way but we are going to show you how you can add rolles as well while we're doing this so let's go ahead and add in a profile and we're going to be passed a profile back for we're going to get a profile back then we can do curly brackets and inside of here we can add extra logic so let's say console.log let's log out our profile let's make sure we know what it is too so we can say profile [Applause] of GitHub and then we can log that now we want to add extra stuff to our session so one thing that we can do we can add extra logic that allows us to give specific roles to specific users now my user that we can use in this that I'm going to sign up for the application with is Jake at Clarity coders docomo role is going to be GitHub user so let's do let user roll equal GitHub user so this is going to be the role for everybody but me but I'm going to say if my profile exists and it has an email and that email is equal to Jake Clarity coders tocom and you want to use your email here an email that you can actually test this at your current location to give yourself admin rights to our test application so Sub in your email here so if our email equals Jake Clarity go.com or whatever your email is then our user Ro is going to be upgraded to admin then we will return our new profile we're going to do dot dot dot so everything that was in the profile before we want to still be in the profile however we want a new role of our user role and that's it you see I controlled s there it got the format all right thank you prettier this is our GitHub provider so let's copy this so we're copying everything on the GitHub provider and let's paste it below and then let's set up our Google provider so for this one we're going to handle things pretty close to the same the only thing we don't want any extra logic so let's change this and let's say this is our Google profile and you don't have to log these out this is just so you can see what's on them and that sort of thing so we're going to console.log that out no special roles for this and one small change with the Google profile we need an ID field and the Google profile does not provide one so let's set our ID equal to profile. sub this is going to be the idid that they pass back make sure to add a comma after this as well so we should be set up there as well now for each of these we have to have a client ID and a client secret that we're going to set up with these providers so we can utilize them in our application so on the level of the profile here after that profile we're going to hit enter here and for our GitHub one we're going to have a client ID colon and we're going to set it equal to process.env dog hubor ID let's copy this now these are Environmental variables that we're going to set up that are not set up yet so this is going to be our client secret and this will be GitHub secret make sure to put commas after these and then we can copy them and down here on the Google one so we're after the profile we're going to add these in here except this will be a Google ID and a Google secret perfect so now these are set up that is looking good and we're going to make one more change out of the box here to ensure that we have our role available for us in this application we're going to set up some custom callbacks and this will essentially add our roll to the Token so we can utilize it in our programs so notice I'm after the square bracket comma I'm going to enter some callbacks here we are going to do async JWT parentheses curly brackets we're going to get a token and we're going to get a user and from that we can say if we have a user let's say the token. roll is equal to the user. roll so this will allow us to add that token add that Ro onto our token so we can utilize it on the server side however we also want to be able to utilize this on the client side as well so we will do async session and here we can use session and our token and here we're going to essentially do the same thing we're going to say if we have a session. user let's set session. user. roll equal to token. roll and this will allow us to use it there then of course we need to return our session so we have it and then up here as I have forgotten let's also return our token and of course comma here awesome so now we should have this set up so we got our providers set up we got our sessions set up everything looks great there now we need to set up our provider on the provider side so let's create a new file and this is going to be at the base level so it should be at the same level as your Tailwind config or the readme or whatever let's set up a new file let's go do env. local in here we can set up our environmental variables so remember they're going to look like this we want to utilize the same verbiage here so let's do Google ID Google secret like that and these are going to have equals afterwards we're also going to need a GitHub ID and a GitHub secret as well so we're going to set both of these up now all right so now if you are logged into your GitHub account you can go to github.com settings slapps from here we're going to select oo apps and then we can create a new oo app we can name it whatever we would like so we can do next off video tutorial we can set up a homepage for for it so we're going to do HTTP colon localhost colon 3000 and then we also need to utilize a callback URL so we're going to copy that paste it down below here it's going to be 3000 API off callback SL GitHub you need to type it exactly like I did here and then we can register our application now from here we're going to get a client ID and a client secret ensure that you do not use mine that won't work and make sure you don't have anything weird any spaces or whatever so we're going to copy this client ID and remember this is GitHub don't get them confused so we're going to do our client ID there and then let's generate a new client secret you might have to authenticate I'm going to copy that secret and then paste that as well so we now have our get iub provider set up that's really it it's relatively easy and then we're going to do the same thing on the Google side of things so you're going to go to Google's developer console so it's console. developers.google.com and then from here we can go to credentials and we can create credentials and we're going to do an oo client ID we can say web application I don't know if it really matters we'll do a next off video tutorial don't miss this so we need an authorized redirect urii let's add that as you can guess it is HTTP colon localhost colon 3000 slash API SLO SL callbacks call back rather slash Google API off callback Google let's go ahead and create so here you can see our client ID and our client secret let's go ahead and grab those so let's grab our client ID paste that in and let's do the same with the secret so we'll grab that and paste that in as well awesome so we should be all set up to authenticate with our application with whatever user we would like however we don't currently have any authentication set up on the individual pages so we don't really have a way of knowing if we're authenticated or not and we also don't have a way to log into our application yet so let's go ahead and set that up first so inside of session down here tab complete from next off and then we can utilize that to see if we have an ongoing session right now at the moment so what we can do is right in here we can say const session equals await get server session and then we need to pass in our options so you see I'm going to autocomplete here but these are inside our API off blah blah blah that whole directory you can see it imported correctly and it's mad because I'm using an async function so I up here need to add a sync perfect so now we should have a session if we have a session which we don't right now because we haven't logged in so after the public page let's add curly brackets and let's check to see if we have a session so if we have a session we want to add in a link and we want the href to equal and this is going to be our log out functionality remember if we have a session we're logged in let's do slash API slof slash sign out and I'm going to add this so I'm going to add question mark call back capital u RL a capital u then lowercase RL equals slash so this means when we sign out it's automatically going to send us back to the root of our directory let's close that link tag and we can call this log out now we're doing conditional rendering here so it's only going to render this again if we have a session so now we need to handle if we don't have a session which is what we currently have so let's do colon and then let's paste this in I'll save it so it wraps and you guys can see a little better so if we don't have a session then we want to be able to log in so we're going to go to the same page we're going to go to API SLO SL in this time so now we should be able to tell if we have a session because we'll either have a log in or a log out button we should not have both hopefully so let's go ahead and check this out so now back on our site so we have our site here loaded up you'll see we do have a log in button we do not have a log out button which makes sense right because we do not currently have a session let's hit log in you'll see we get this nice built in now you can customize this that's not what we're going to cover in this tutorial but you can customize this but you see we got our two providers that we're able to log in with now remember if I log in with GitHub I am authenticated with my Jak Clarity coder so that's going to be assigned in admin the other one will be a normal session it won't be my admin session so let's authenticate with GitHub you're going to have to authorize the first time you do it and you'll get redirected back to your application if this didn't happen correctly double check that you added that call back URL correctly to GitHub and likewise when you test it with Google the same way there you're going to have to make sure that you have a valid session there as well so now you'll notice that I did log in that did work correctly but I still have my login options so it's not showcasing a session at the moment so we did have the terminal still open you can see what our issue is it's throwing all kinds of Errors so what it's saying here more or less is that we haven't provided a token for our session so we have no secret in order to verify that this person is correctly signed in with our application and they didn't just create some bogus session themselves so what we need to do is inside ourv Loco We need to add a next off secret here so this is going to be called all capitals let's do next oore secret and we will set this equal to and then we're going to create one now you can type in random characters here and that would be fine but we want to make sure that we have a certain level of security here so open up your terminal if you don't you can kill your session if you running it still from before and let's generate that secret so let's do open SSL Rand base 64 and we want 32 and then here you can see we generated a nice little secret don't use mine just create your own and then we have our next off secret here so we should be good now once you have that we can go ahead and spin up our server again so let's do mpm run Dev so now back on our site if you get any weirdness make sure you're shutting down your server turning it back on you can also try reloading this page and one other thing that I have to do from time to time when I'm playing around with cookies sessions and things like that hit control shift I if you're on Chrome and here you can go to the application Tab and under local storage session storage and cookies you can clear those out and that will sometimes help if it's caching an error anything like that that's happened in the past pass I didn't have it this time but just so you know you can go through that process obviously we don't have a session right because we didn't have that next off Secret in here so that aired out so let's go ahead and try and log in again you can do it with whatever provider you would like I'm going to try with my GitHub again and you'll see that now we have a log out so we do have a session here which is awesome so if we go we don't have any protected pages right now at the moment so you can see our client member our server session we can still get to we can obviously still get to public let's go ahead and try and log out sign out and that goes back to the home directory and I have my login so everything looks good but nothing is protected so let's get into protecting our routes we're going to do this in three ways right now so I'm going to quickly show you how to protect a server side page a client side page and then with our create user we're going to protect it with middleware for right now and then we'll EXP span that to protect it to make sure you're an admin on top so let's go to it's really not that difficult let's go to Our member page first remember this is our member server side session so let's take a look here and we can set up a session so we're going to need to check to see if there is a session so we need to make sure you're logged in so session equals await get server session we'll do the Auto Import so I'm going to tab here and then I'm going to spit in our options remember let's tab complete here so it did those two Imports for us this also needs to be a sync that looks great then below here what happens if we don't have a session so if we don't have a session in this case so if there is no session then let's do a redirect and slide down so let's use the one from next navigation make sure you're looking at the redirect from next navigation we'll import that and we can redirect to/ AI slof SL signin question call back capital u RL equals SL member so what this is going to do it's going to redirect us to the login it's going to leave the Callback URL so if they do log in they'll come back to this member page so that's going to be a nice look nice and easy look so below here let's do just a little more let's add a P tag and let's say inside of here let's do curly brackets so if we're here we do have a session so let's say session question mark. user question mark. email so we'll spit out the email let's do that again and this time let's say roll so remember inside of our options on all these we're setting up a a role of some sort so for our Google provider we are not setting up one so that's going to cause an error I'm going to minimize this so here we're setting up a user r on our GitHub side of GitHub user unless you're me J Clarity coders and then you're going to get an admin roll on the Google one we didn't so luckily we didn't try to O off with that if you did I'm sorry and we can paste in let user rooll and let's say Google user paste that down there and now this should be working as well we'll test out Google just to make sure it works here down the road um but for right now that should be good and on the member page we should be protected now let's try it so let's go to our application you see we can go to public if we try to go to member it asks us to log in now make sure you're not already logged in um in this case let's do something different let's sign in with Google just to make sure that works since I did just find out that it probably isn't for you guys pick a Google account and you can see that our member session it has my email address and Google user there as well great so that works let's go to our client session now let's protect this it's going to be a little different because this is rendered the JavaScript is rendered on the client session on my client browser so we can't use the serers side get that we were using before and I'll show you why before we go there I'm going to copy these two P tags let's go to our client member page we can paste them below so this will be if we have a session let's do get server session so if we do that you can see that we get all kinds of crazy errors well part of it's probably because I use this let's control slash I'll just comment this out for right now and then let's navigate to that page we'll go to create user and then I'm going to go back to client member session so now if we actually try to use get server session to get our session so let's do con session equals await get server session pass in our options you see we need to do an async function so let's do async and we save it we can uncomment this save that and let's go to our client member and you'll see we get an error async AWA is not yet supported in client components okay so we see our problem there that's why we can't do it the same so we can't do it in this format which is fine we have other options here so instead of this so let's do import use session and we're going to do from next off SL react so this is going to be how we grab our session and then here we can grab const we're going to set data equal to session we're going to do equals use session parentheses curly bracket it's required equals true and then on unauthenticated so what do we want to do when we're unauthenticated we want to redirect make sure you're using next navigation again for your import redirect and we're going to do something very similar to what we did on the other page so we're going to do slash API SLO SL sign in question call back Capital URL equal SL client member just like that so we're good and if we control save this you'll notice that we're getting new errors right use session must be wrapped in a session provider member so this is a little complicated so the use session needs a session provider however the session provider has to be a client component as well so let's create a new file inside of our components and we will call this oth provider. JS and this is going to use clients we are going to import session Provider from next off react so you'll see it did the Auto Import for me and we're going to do const o provider equals we're going to pass in our children then inside of here we're going to return our session provider and don't worry kids putting you right back in there we need to export default off provider like that so now we have our session provider in a client component so we're good to go there and we can go back to our layout page and here's where we need to wrap this bad boy to get our session on the client side now remember this is only if you need to do authentication on the client side if you want to do it on server side we're good already so here let's utilize our off provider this should be coming from components directory and then we're going to wrap this around our children div here like so now I don't have to wrap my nav in it because I'm not using client side on that you could certainly it might even be better actually to wrap this around your entire body just in case we can do that I didn't need to for mine but you might down the road so we can wrap this off provider I believe around the whole body like that and that should function as well and then if you have any client rendering here on the nav side you can do that now again shy away from client side rendering if you can but if you have to um you can go that route so now we have an off provider here so our client member should have a session now um and it should you should need to be authenticated so let's go back to this let's just go back to Local Host you'll see we got home uh we are logged in so if you go to the member you can see that we're still my email a Google user if I go to client member now you'll see it has the same so both member server session and client session are the same on both authenticated form now if I log out and I go to client member it is protected now so that is good okay what's middleware so if we don't want to protect our Pages inside of each page we can set up mid middleware on our project and that will allow us to add authentication rules in one spot that we can easily find so let's create a new file this is going to be on the same location asmv DOL and let's call it middle wear. JS we can do export and then default this is not what we're going to stick with but we can do export default from next off SL middleware so this right now will protect everything so everything on our page will be protected then we can export con config equals let's do a matcher and then we can pass in an array of URLs that we want to test and here we can protect create user okay so right now we have it set up so our create user should be protected now you can see that's a lot easier than adding it on the page or anything like that and if we go here let's go ahead and log in just with my Gmail so remember I'm a regular user right now okay now back to our application let's see if our create user is now protected so you'll remember we're still logged in so make sure you're authenticated if you're not you can tell because you have your print out here let's go to create user and you'll see that I can get to it now I'm not on my admin account but we haven't done that yet so it's just protected from normies so let's go ahead and log out sign out and now we don't have any session so if I go to member it tries to log me in if I go to create user now it tries to log me in so that is great so we've successfully protected this with middleware however we still are not protecting it from normal users we only want this to be admin so let's make that change let's go ahead and log out and then we'll go back to our code here so we're going to do some new import so let's say with o I'll tab here you can see my import that I'm going to get with off above and let's also do next response and this is coming from next server now we should have all of our Imports We need oh my gosh I was on the wrong page don't do that so let's leave our create user alone if you had that open I apologize let's close out of all of these I don't make that mistake again so we're back on our middleware page so on our middleware page we're going to do those same two Imports that we just did so we're going to do with off and next response so let's save that and you can see that we got our two Imports done and then we're going to make some changes to this file I'm going to leave the matcher on here because I only want this to apply to that page I'm going to delete this out and start fresh so I'm going to do export default with off now inside of this I want to create a function for my middleware and I'm going to get a request when I visit a page and we can console log out some information here that might help you with future requests or things that you want to do custom wise so let's console.log let's do our request. next next url. paath name so this will show you what our path is so if you're trying to do some logic here that may help and then let's also use or at least log out next request. next off. token. roll and that could help us do some troubleshooting as well now let's actually Implement our logic so remember again you can you can do any customization you want here um anything that makes sense with you but this is what we're going to do to protect this one create user page and make it only admins so let's say if in here we want to use our path name so let's copy this we're going to say if request. path name so if we add other paths it might not you know it might have different logic or whatever so let's say if it starts with create user and we want one other test here and we want to make make sure our roll does not equal admin then let's do curly brackets let's save this just so it formats I'll fix that okay so here's our logic here's our if statement so we want to say if we're going to the create user page and you're not the admin so if you're not the admin and you're trying to go there let's just return next response. rewrite let's do a new URL and we'll go to the denied page request.url so what we're going to do is we're going to send them to that denied page we created earlier now we could send them to a login page but they're already logged in this is for if they don't have the correct Ro so they shouldn't probably shouldn't be able to just log into a different account so if that is the case we're going to send them to the deny page if not this will continue on and they will be authorized so let's add a comma here let's do call backs colon curly brackets let's say they are authorized if they have a token and that token is not null so we want to make sure they have some sort of a some sort of a token here we actually need to wrap this in curly brackets as well I believe I'm going to highlight this wrap it in curly brackets and save and you should have something that looks like this so now we have our callback function set up we have our middleware all set up actually so we should be good let's take a look let's go back to our project we got all kinds of Errors let's refresh So currently it does not look like we're logged in let's go ahead and log in with a non-admin account so if we log in with our Google account in my case yours might be different we go to member you can see that our role is Google user if we go to create user you'll see that now we get a denied let's log back out and this time I'm going to log in with GitHub you'll see that I do have an admin session here which is great and if I go to create user you can see that I do in fact get to the create user page so we should be good there so what we have now is we have ooth provider set up for GitHub and Google and we have role-based authentication for both server rendered pages and client rendered Pages as well as ro-based authentication so we are good there now you might be asking yourself well what if I have a database already or what if I want to create a database of users that allows people to either use their email or pick a username and a password and set that up that isn't covered in a lot of these tutorials because it's a little more advanced and you have to factor in things like validating email or whatever it might be but I'm going to show you how you can set that up connect it to a database relatively quickly and get it at least spun up and in place we are not going to cover Val validating emails so you need to keep that in the back of your mind that this is more like a username not necessarily an email address I can pick Jake at gmail.com even though that is not my email address let's go ahead and kill our terminal and then we'll do the remaining Imports for this project so let's do mpmi mongodb mpmi Mongoose and MPI B Crypt awesome now we got that set up let's go ahead and sign up for a mongod DB Atlas database now this is free so if you haven't used it already go ahead and sign up for a mongod DB Atlas account you should come to a page like this you can authenticate with very similar providers to what we're setting up you can see how popular they are so I'm going to go ahead and log into mine now if this is your first time you might get some popups wanting to spin up a database right away blah blah blah you can skip all that hopefully eventually you'll come to a page like this and what we want to do is up in in at least right now in the top left is to create a new project we're going to call this next off video tutorial in my case I'm the project owner let's create our project then once you get that created we can create a deployment so let's do create you can do the free tier and then hit create you're going to want to set up a username and password here so on the next screen it gives you an option to set up a username and password note these down so inside your EnV you can note these down I have admin and then a password below so if I go back to my code inside. EnV let's just paste these in here for right now let's say admin and then our password you'll want to add your current IP address as well so we're going to add that ah I forgot to hit create user Make sure you hit create user and then finish and close go to overview and now you should have your database set up so we're going to go ahead and hit connect if you don't have this there should be a connect button somewhere or you might have to let provisioning finish so once that's finished you can hit connect we're going to hit drivers and then we're going to copy this string down here now back in our code we're going to paste that string in here pretty long you'll notice it already has admin switch that out for your username if you didn't use admin I did so I'm not going to change it and then we're going to copy our password and paste in our password use your password not mine obviously and then here we're going to call this in all caps mongod dbor urri we're going to set that equal to the string if we slide to the back I'm going to delete this back half so after the question question mark and I'm just going to call this app DB like that that should be it so our EnV is set up there that looks good we can go ahead and close out of that now let's create a new folder so in parentheses we're going to say models inside of here we're going to create a new file called capital u user.js so we're going to Define what a user looks like in our system so we're going to import mongus and curly brackets scheme a from mongus and we're going to use mongus doc connect process. env. mongod dbor mongus do promise with a capital P equals global. promise then let's do const user schema equals new schema do curly brackets so let's just have a name and that's a string an email and that's a string a password you guessed it that's a string after this curly bracket let's do parentheses this is something I always do with my models let's do time stamps equals true this handles the created at and updated at dates so it's there for you if you ever need it and then let's create our user model so con user equals mongus models. user or so this is if it's already defined or we use mongus model and create the user from our user schema then let's export that so export default user spell default correct that looks good okay so we got our model we got our database set up now all we need to do is create our API for creating a user and then I'll show you how you can verify against your existing users as well so all we have left to do is to create the frontend form that can submit a new user then we'll create the API to add a user to our database when someone submits the form and then we'll add the credentials provider and show you how to validate that password let's create our user form now this is a little unique because we want this form to use client side rendering because a form interacts with the user with the JavaScript so you might have validation on the front end or whatever it might be user form. jsx inside of our components directory Factory and then on this user form we are going to use client we're going to import use router from next navigation not next router this mixes me up sometimes so let's change that to navigation then we're going to import react and use state from react const user form equals recreate our function like that and then at the bottom we'll export I didn't use my short keys I wondered what was weird about this export user form see I can do it guys all right then inside this we are going to have a con router equals US router have the const form data this is where we're going to store all of our form data and we'll have a set form data and we'll set that equal to use state with curly brackets inside so our form data is just going to be an empty object originally then we'll do const error message so we're not going to do much here but I will check to see if that user already that email is already in our database and then we can send a duplicate user type of error response back so we have an error message and a set error message equals use State this will just be a blank string to start out that makes sense we need to handle our change in our form and if you've done this before it's pretty straightforward we're coding it so it's pretty Dynamic doesn't really matter what our form values are I always do const value equals e. target. value copy that and I'll do const name equals e. target. name and then I will do set form data I'm going to pass in the previous state we want another set of parentheses there and then open in curly brackets that looks better so when we change our state so this is anytime the state changes we're going to say previous state we're going to expand that so it's going to take everything from the previous state before and we're only going to overwrite the one change that we're making so whatever field on our form we're changing and whatever value we're setting it to we're going to override it with and that's our handle change that's it now from here we can do a const handle submit this equals an async function we're going to do e. prevent default we're going to reset our air message so if we previously had a air message we're going to set it back to blank at the start of submitting our form basically starting with a clean slate we're going to get a response from our own API we haven't created yet so we're going to do con response equals a weit fetch then we're going to do API SL users with a capital u remember we're creating this in just a moment parenthesis nope sorry comma and then curly brackets we'll do our method equals post we're going to create a new user our body is going to equal json. stringify we're going to pass in the form data just like that we'll do content type and that equals application SL Json okay almost done believe it or not so we're going to check to see if our response has an air so if our response does not equal okay then we want to get what the actual error was we're going to pass back an error here so we'll wait our responsejson and then we will set airor message to whatever was in our response. message and we'll show you how we're going to set that in just a moment so we're going to pass back some sort of an error we're going to catch it here and then we're going to set our error message this is only for an error remember we'll add an else so otherwise we want to do router. refresh and router. push and we'll push to the root directory so this is just going to send us back home and that's it so pretty straightforward nothing crazy here let's continue on and here we're going to do a return and let's do open and close let's do form tags inside of those our form is going to be pretty straightforward we're going to do on submit and we've done all the leg work now we're going to do handle submit that we already created our method equals post and our class name just a few Styles here going to equal Flex Flex call let's save this just so it formats and that's my prettier extension that helps me do that Flex Flex call Gap three let's do a width of a half let's do inside of this let's do H1 let's say create new user then below that we can start creating our inputs don't worry they're going to be very short let's do a label and that label is going to be full name then we'll have an input let's say input our ID equals name our name equals name on change equals handle change like so let's make it required equals true add another my closing tags there and then I also need a value which equals form data. name saving that so it formats beautiful and last but not least let's do a class name and we're going to copy this so M2 BG of slate 400 rounded watch any of my videos I'm not very good at styling so I just round corners like crazy I'm going to copy the label and the input paste it below this one is going to be our email so let's do email for an ID email for the name change the form data. email here as well everything else looks good so that should be good to go let's copy all of it again paste it below do password ID of password email form data and this actually has a type of password let's add that in these probably should have a type as well let's do a type equal to text for our email and also for our name okay that should be good to go there the last piece is our submit button so let's do input the type equals submit the value will equal create user and the class name let's style this just a tiny bit blue 300 with a hover of BG blue 100 self closing tag so we'll do slash like that slide over so you can see again so we got our input button in the form we got the form you might have wondered why I wrapped these all in closing tags remember the whole return has to be wrapped in some sort of tags and I'm actually going to do something below the form here so that's why I did that let's do p tags and then inside of here let's just print out our air message now this will print out a blank string um when there is no error but that's not a big deal to me so we're going to leave that there and then we'll change it to text red 500 this looks good I think we're pretty good there should have closed out of that terminal a long time ago we got our user form created let's create the API so inside this API directory you see we have API off and then next off inside of API so I'm going to rightclick on API I'm going to create a new folder and inside this new folder I'm going to call it users and inside of that user folder I'm going to create a new file called route. JS now we're only going to do one function in here just to show you how a post would work I'm going to pull in next response from next server I'm going to use our user model so I'm going to pull that in as well you can see how it does it up here with the at symbol tab that now we got our user model and then finally we have to do a encrypt before we store it so we want to do import bcrypt from bcrypt now remember this you know we're not validating an email so in my mind this is like I run a company and I'm inputting a user that I know has access to that email address and that sort of thing you wouldn't want to do this in like a SAS application without validating the email address because someone could steal someone's email address that they don't have access to so let's export a sync function post and we're going to get a request let's do this all in a try catch and for our catch let's console.log our error that might help us if we make a mistake I'm sure we won't let's return um next response. Json in here we're going to use that message so we're going to use message and then error let also return the error that looks good got a comma here another set of curly brackets let's also do a status of 500 if something goes wrong okay that looks good now our Tri block we're going to grab our body so we're going to const body equals a weight request. Json so we're going to pull off the information we're going to do const user data equals equals body. form data let's confirm that our data exists the fields that we need so we want to say if we do not have user data. email or if we do not have user data. password so if we don't have the those fields we are going to return a message so we want to copy this we'll paste it in here we're going to say a status of 400 and then our message can be let's not send the error our message can be all fields are required so this shouldn't happen because we're sending it with that one form but but down the road you change something now you'll know if you're getting information missing information so let's check for duplicate emails now so let's do a const we'll call it duplicate equals a weight we're going to use our model from above so user find one so we're going to see if we can find any email that has our unit user data. email do do lean and execute now we can check so if there is a duplicate then we want to send another response and we're doing kind of meaningful responses here too right so that's uh that's kind of a cool setup that we have going we can do a status of 409 and we can do duplicate email now it's probably worth noting here that we're also you might want to be careful and make sure that you're using um that you're checking all your emails case insensitive so you might want to lowercase all of them before you add them to the database or something like that because right now you could have unique emails that just have capitalization differences or whatever it might be so keep that in mind close this that looks better so now for here we know we don't have a duplicate user we know we have all the fields we need let's create our hash password equals await bcrypt do and then we're going to pass in user data. password you can do as many salt rounds as you want I always see10 so you probably shouldn't use 10 but that's what we'll use const and and then user data. password so now I'm overwriting the plain text password with our new hash password here so now our user data contains our hash password we shouldn't be saving plain text passwords and we can await user. create and we can pass in our user data dang was longer than what I thought but we got her done so we created the user data let's copy a response we still want to send a response our message delete this can be user created and our status can probably be 2011 awesome so this should be all we need to create a user so let's go ahead we've done a lot let's go ahead and test that make sure user gets created successfully and then we can try and authenticate with that user after we add the credential provider which we haven't done either so mpm runev okay so we got our website here I got to see who I'm logged in as I am logged in as an admin so if you've added this authentication and it's rule based you have to have admin now to go to create user if you haven't done this you can assign admin to someone else I'm go to create user this is only admins duh cuz I didn't update that so back on our create user page instead of only admins let's render our user form see it did the import for me self closing tags that is pretty who says I can't style okay so let's create a new user Bob Bobby Bob bob.com password test of course and create user kind of looks like it worked Google does not like my password it's been used in a security breach so that's worth noting um but it looks like it worked okay so let's go back to our database and I will click on database now we didn't name our collection so it's probably going to give a test collection which is fine oh no we did appdb we have a user you can see we do have a user now awesome so we have Bob Bobby Bob bob.com and a nice hashed password so one step left here we have to be able to authenticate with Bobby now so let's log out we don't have a credential provider at all so we can't log in with the username and password let's change that so we're going to open up option let's go ahead I'm going to shut down this terminal just because I'll forget it's running and then we'll be on the wrong thing and close out all these just for you guys so below our Google provider let's do a credentials provider yeah let's do a manual import though let's copy this one delete off the Google let's do cren chills provider just being careful so I spell things correctly can cause some weird errors and then this isn't Google this is credentials with a lower case C okay so now we should have a credentials provider to use down here let's use that credentials provider let's do parentheses and then curly brackets so inside of here we need to First tell our credentials provider what they're going to use to to log in so let's call it credentials then let's set our credentials equal to they're going to enter an email could be a username could be whatever you want labels going to be this is just like filling out the form honestly they're just going to generate it for us so we can label it email we can say the type is text and the placeholder is your email put a comma below that I'm going to copy this change this to password and we'll say we want a password and then your password and the type I bet we can put password here too let's try that so we got a form that it's going to create when we try to log in with credentials provider of email and password and then we have to explicitly tell it how we're going to authenticate so we could say async authorize you're going to get credentials and then we'll do curly brackets okay the one thing that recommend here right away is when you're creating your authentication put it in a try catch block because if you don't nexo is going to spit you out a generic error on the login form that would be meant for the end user we don't want that so at the end of this if we don't return a user if at no point in this try catch do we are we successful at the very end we want to return null which means they're not going to be authenticated and then inside of here inside of the air let's just do console.log and then we'll do air for that one so now we should get an error if we mess up while we're creating it right now that sort of thing so what we can do is then we can use const found user so the first thing we need to do is based on the email someone tries to log in with we need to see if it exists so we're going to do user which we're going to so I'm going to Tab and it's going to do the import at the top see we got our model so we're going to wait user. find one we're going to pass in email or we're going to look for an email that has that is from credentials. email same as before we can do lean dotc like that so this should find the user now if it doesn't so if we have a found user we can move on to the next step if it doesn't find a user it's going to go down to this return null and we're going to just give that air that the credentials are incorrect we don't want to give too much information you know we don't want to say oh we couldn't find that email address or whatever so we're just going to say that the credentials provided were not valid regardless of if it's a bad email bad password what ever so inside a found user then for your sake our sake we can say the user exists you want to remove this after you're done testing we do const match so now we're going to test to see if we have a good password so let's bcrypt remember it's an encrypted password and they're obviously going to type the plain text one so we'll do bcrypt do compare we're going to compare credentials . password to the found user. password it looks something like this bp. compare so this is going to use our salt and compare it we need to import that as well so we're going to import the Crypt from bcrypt we're real close now guys so if we have a match so if match so if our passwords do match then we can console.log good pass again this is just for us remove it after you're done and I also want to delete from our found user the dot password we don't really need that anymore so I'm not even going to keep it around and then we can give our found user a a roll just like everyone else and this equals just a reminder I know I've overstated this but this is an unverified email user and then only if all that's successful we return our found user that should be it guys so that looks pretty good let's open up our terminal mpm runev that looks good remember I'm not logged in right now so and I don't want to be because I'm just going to test my credentials let's hope I can remember this so it's Bob bob.com lowercase and then test lowercase sign in with credentials you see it looks like it's good so if I go to client member you can see Bob bob.com unverified email and then create user it's denied I'm not an admin so that makes sense awesome this looks great this is functioning let me know if anything's missing from this video in the comments and I'll certainly make a follow-up adjusting and giving you whatever you would like to see remember inside this authentication you can do whatever you want so you could even hardcode these somewhere these users somewhere if you wanted to give out secret passwords to your application or whatever it is be careful play around see what it's like before you go a full production app let me know in the comments any other videos you want programming applications anything you want to see that you haven't seen around let me know I'll create it if you haven't been to the website check it out we have a cyborg blog as well it has posts that are half human half me we're creating articles doing reviews thanks for watching and until next time keep coding
Info
Channel: ClarityCoders
Views: 4,195
Rating: undefined out of 5
Keywords: NextAuth, AuthJS, Next.js 13.5, Role-based Authentication, Server-Rendered Authentication, Client-Rendered Authentication, OAuth Providers, Google OAuth, GitHub OAuth, Custom Authentication Provider, MongoDB, Coding Tutorial, Web Development
Id: V6w64-_X9QU
Channel Id: undefined
Length: 85min 52sec (5152 seconds)
Published: Tue Oct 17 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.