Effortless Auth in Next.js 14: Use Auth.js & Drizzle ORM for Secure Login

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
nexo is a popular library for managing users in your nextjs applications and in this video I'm going to show you how to set up nexo with a next4 project using the drizzle omm adapter so users will be able to log into your app using a social signin like GitHub and the users will get saved to the database using the drizzle omm I already have this basic social media site set up where I can view posts and I can create a new post uh view people's profiles and basically Al do all of the basic things that I would need to do but I don't have anything in place to manage my users I've currently just put in two users manually into my database and then I've hardcoded the user ID in some places like here where I create a new post just so I can get these main features working but now I'm actually ready to add login and user management so let's use nextor for that and the first thing we need to do is install nexor version 5 because that's the version that works with next version 14 and the app rouer so there's installation instructions here but these don't actually work because as of the time that I'm recording this you need to install this using the beta version and everything's pretty new right now and this will change pretty quickly so check the description cuz I'll keep everything up to date right there but right now we're going to use the beta version so I'll install that and then if we go back we can see that we need to set up this starter code here in a file called or. TS and it doesn't really matter where we put this file but I'm going to put it in the root of my source directory so or. TS and paste in that starter code and this sets up next orth using GitHub as a login provider so someone can use GitHub to login and it exports an orth function that the rest of the application can use if a file or a component needs to check if the user is currently logged in and then this Handler's object is how nextto handles all HTTP requests so we need to go set this up so it can actually accept http request coming in and to do that inside the app directory we need to create a folder called API and inside that we need an orth directory and inside that we need a captal uh for next orth that's how it's going to handle it so any request that comes into SL API SL or SL anything is going to go into this route uh and in here we can create a route. TS file where we can import the the handlers from at slor and this handlers object has a get method and a post method for handling the HTTP request so all we need to do is export those two methods from this route and next door can start handling the HTTP requests and if you look at the rest API documentations you can actually see every mpo that we've now exposed in order for this to work so if we go back to the other docs we'll see that that we need to set up some environment variables for GitHub in this case to actually get the GitHub login working so I'm just going to copy these environment variables and put them into my EMV dolo here and now we need to go set these up but first let's take a look at the different providers that nextor offers so if we go to the providers list we can see that there is credentials authentication which would allow someone to log in with like an email address and a password and it is definitely dis discouraged to use this kind of login if you need someone to log in with email username and password do not use something like nextor use an authentication Service like Clerk or kind or orzero it is not trivial to get this right the other authentication they have is email authentication where you can get emailed a magic link and you don't need any passwords this is kind of cool but you need to set up an email service and use something like node mailer to get that working if we go to the oor authentication this is where next o really shines because we can have users log into our app using sign in with Google or sign in with GitHub or any of those things and if we look at all the built-in providers we have here it's really easy to just drop any of these into our project and allow a user to log in and out of all of these GitHub is the easiest service to set up so let's go set that up now first you need to log into GitHub and then from your avatar you can click that and go down to settings and then from here on the left you want to scroll all the way down to developer settings and then from here you want to select o apps and you're going to create a new O app here for every single app where you wanton get hub login so I'm going to create a new O app here and I'll just call this social threads app uh local and my homepage URL is going to be HTTP localhost 3000 and because your url needs to be known when you create the app you're going to have to create one oo app for local host and create another one when you publish your app on something like Vel and you have a public URL so right now we'll just create the local version then later on you'll have to create the public version uh we don't need a description here and the authorization callback is going to be this SL API or callback SL GitHub and the URL structure here is/ aior coolback followed by the provider that we're using so in this case GitHub uh and that's it now I can register this application and now I have a client ID so I can copy that and put it into the environment variable here so this is the GitHub ID then we need a secret so I'm going to generate a new client secret and this is where the downsides to using your phone as a camera really come into play because I now need to use this to authorize myself in GitHub so now that's authorized I should have a client secret here and make sure you keep this secret secret do not let anyone see it do not commit it it into your project I'm just going to delete this straight after so it doesn't really matter in my case but really make sure you keep the secret secret uh and put it in an environment variables file and make sure it's get ignored and that's all I need from the GitHub side there's also this orth secret that we'll need in production so I'm just going to do it now in development anyway if we go back over here where it has the environment variables that we need if I do a search for or secret I should see here a suggestion so on a Unix system I can type this command into terminal or I can even generate a Secret online so I'm just going to copy and paste that and this is just something that next door will use internally either for encryption or signing so now that's set up we should actually have GitHub login working which means that if I go to my application which should be at Local Host 3000 and if I go to SL API slor signin I should be navigated to this page with a default signning with GitHub button and if I click on that I should be able to authorize here and actually sign into to my local application using GitHub and that was kind of cool I logged in with GitHub but I don't really notice anything like what did that actually do with my application well if we open up Dev tools and go to application and select cookies we will be able to see that nextor has save some stuff into the browser some encrypted cookies here and this is the information store about the currently logged in user so if we go back into the application I'm going to use my slme profile page as an example here I'm just going to go to that in my next application so that is slme SL page and right here I have just hardcoded a user ID we're going to fix that in a moment but first let's just see what kind of information we can access once a user logs into our application so I'm going to import the orth function from at SL or and at the top of my component here I can say con session equals a wait or and just call that function like that and then this session object should contain all the information about the user so for now I'm going to comment out all of this and I'm just going to Json stringify the session object that we get back uh let's do null 2 and I'll wrap this in preag for styling and then when we go back to the browser we should just be able to see what kind of information we're getting back so this is the information I have access to I have the name email and Avatar image of the currently logged in user and if I were to sign out so let's go to SL API orign out then if we go back to slme I should be able to see that this session object is now null so basically if if I want to check if a user is logged in I could just quickly check if there is a session uh I might even check that there's a user object on the session and if there is no session or no user object on the session I know that no one is logged into my application so maybe I want to say that they can't go to the profile page because there is no profile but if I get past this then I know there's going to be a session. user object and I get access to that information from GitHub so I'll just go back and and log in here again and we can see there's the information about the user so already I have the ability to authenticate a user with GitHub and then authorize them to access different pages in my application and I'm going to improve this user experience just a little bit so if the user is not logged in what I might actually want to do is import the redirect function from next navigation and redirect them to to the login page so redirect SL API or/ signin and then we can actually pass in a callback URL here where after signing in they'll get redirected back to this page then instead of Json stringifying this information out I'm going to uncomment everything I had here cuz I already had this profile view set up and profile is expecting a user with a name a username and an image so I'm going to modify this to work with the new user object I have I'm just going to change username to email here and then on my page I'm going to pass in the session. userobject and now my profile component here that displays an image and the user's name should use the data coming from GitHub so I'm getting this invalid Source prop that's because I haven't allowed my application to display images from GitHub so let's go and change that in my next config I'm just going to add that as one of the trusted sites so we'll paste that in in here and now if I go back and refresh this page hopefully I'll see my information displayed from my GitHub account so this is now my GitHub user profile account so it doesn't take much code to get set up with nexto in this way however I think most applications need to go a step further because right now in my database I have users and posts and a post has a foreign key to a user ID and this is going to be pretty common in most applications every time I create some sort of resource is probably going to be linked to a user so what I really want is to have a user save to my database every time they sign up for my application and that way I can get their information from GitHub like their username email and Avatar image saved in my database and I get a primary key that I can reference from my other tables and since I'm using drizzle as my omm I can use the nexo drizzle adapter to just get this working really quickly so the first thing we're going to do is install the orth drizzle adapter and then we need to grab the updated schema so first pick your database I'm using postgress but you might be using my SQL or SQL light and then copy all of the schema code that it has on this page and I'll put a link to this in the description uh because we just need to copy this exactly as it is so right now in my drizzle schema I have a users table but it's really just basic it has the ID name username what ever uh I'm going to replace this completely with everything that I see here so not only are we keeping track of users we also have this accounts and sessions and verification tokens that's all managed by nexto we don't need to worry about this but we do now have access to this users table so that means I get access to this primary key uh name email and image and in my post table I don't think I even need to modify anything because this was just depending on a user having an ID that I can reference so that's all I need to do on that end but now I'll need to push this database update bun X drizzle kit push PG the data probably won't align it's telling me I have to delete the users table which is probably going to fail miserably here because I already have posts linked to my current user so what I'll need to do first is go into my posts and I'm just going to delete everything I have in my database right now it's fine it was just test data anyway uh and now I should should be able to push my schema updates perfect so now my database should have all of those updates in it so if I refresh here I can see all these tables but they're all empty so we need to add the next piece of code and that is right here so I'm going to copy in this code right here cuz I think that's all we need I'm going to paste it in and then I need to tweak this slightly so basically when we set up up our next orth object we have a bunch of options we're saying the provider is only GitHub but we can have multiple providers and we're going to have the drizzle adapter be our database adapter so that users are managed in our database using drizzle then I need to import the database which I'm actually doing in my application from atdb and that should be it so import the drizzle database uh use the drizzle adapter and now if I sign into my application it should store the users's information in my database so if I go back to my application it looks like it's already got me on the sign-in page let's sign in with GitHub and see what happens so there's an error here but I actually think this is a different error so I'm going to go back to drizzle studio and I should see there we go there is a bunch of information in here I really only care about the user it's given me a unique ID it's got my name from GitHub my email from GitHub and my GitHub Avatar so all of that has worked and it's put it into the database and the the reason that it's failing on this side uh let's actually just go and console log what we get out here so if I go back to the Mi page uh I'm logged in I've got the session let's just console log my session. user so if I am not logged in redirect me to the sign-in page if I am logged in let's just console log the user out and see what kind of information I get so over here I should see I guess there's a bunch of error messages but let's refresh and see if I can get that logged out um there we go there's the information so I get access to the name of the user the email and the image but really what I need access to is that user's ID because most of the code in my application in any application is going to rely on you having access to the user ID so that you can link that up to other tables in your app right now I have a post table but the way that is linked to my users table is through the user ID as a foreign key on the post table and this is really common so I need my session object that comes back from nexor really I need it to contain my users ID but right now it doesn't it just has name email and image and the reason it doesn't contain the ID is apparently for security reasons but those reasons have changed with next4 and the app Rowser and it might change in the future but right now the best way I can find to actually get the ID attached to the user session is to implement M it using these docs which say that we need to create a types slor D.S file with all of this code in it so I'm going to go back into my app and create that file and then inside of there I'm going to take this code copy and paste it and this basically extends the default session interface to contain custom bits of information so I don't care about an address what I really want is the ID which is a string in this case and now everywhere I try to reference my session I'll be able to have an ID property now I also need to go into my or. ts file and add more code in here so there's a callbacks property where I can add some functions that kind of act as middleware functions so here I'm going to use the session function I think this might be mostly accurate which is going to contain the session and the user so this will get called when a user logs in and we get access to the session object and the user object that comes from the database that has that primary key so what we're going to do here is assign the ID on the session object to be the user's ID and then return that session all of this is just so when we eventually want to use the ID of the user we can so in my me page right here I'm console logging the session. user so if I go and refresh my page now I will hopefully see that I have access to that ID so there the name email image and ID so from any page in my application if a user is logged in I'm going to have access to these four properties which is really handy now because the thing I care most about is actually the ID so if I go back to my code now and I look at the logic I shouldn't need to hardcode my user id anymore because I already have that information in my session I can delete this code too and this piece of code right here was actually to select my user information from the database given the user ID but I don't need that since the name email and Avatar image are all stored in the session anyway so really all I need to do here is I have this complex query to get the user's post from the database that joins the post to the user and all I really need to do here is pass in the session. user. ID and that should be able to run this query no problem so now if we go back to the browser I can see everything is working but I don't actually have any posts and that's just because I don't have any posts in my database I to delete them all so let's continue with this application a little bit and I'm going to actually create a post now so if I go to the create page I should see I've hardcoded a bunch of things in here too so I hardcoded that user ID which I don't need to do anymore instead I can import orth get the session from orth check if the session exists and if it doesn't redirect the user somewhere else so let's import redirect from next navigation and this this is one way of protecting my pages I'll show you how to do it with middleware in a moment uh but this works pretty well so I can only access the create page if I'm logged in which makes sense then I have this create post form that relies on some user information which already exists in the session so I'll just pass in the session. userobject because this form just displays the user's image and name here when you're creating a post just to add a little bit of customization there uh and then this form allows me to create a new post using a server action so in my server action again I've hardcoded this user ID and it doesn't matter if I'm on a server component or a server action or a serers side route all I have to do to access the user's information is import or and then call it to get the session and in this case in a server action if a user tries to submit a new post but they are not logged in what I'll do is just return message not authenticated because that's how my server action handles these kinds of errors and then at the bottom here when I go to insert into my post table the user ID is going to be the session. user. so when I go to create a new post First grab the session make sure the user is logged in then if they are create the new post and use the ID from the session to actually store that in the database now if I go back here and try to create a new post my first post it should or work I'm now on the homepage and I'm viewing that from the database and if I went and checked my post collection I'll be able to see that it linked up to the user ID no problem so if I go back to the app and I just manually sign out again I can't access my profile page and I also shouldn't be able to access the create page without logging in um I haven't protected any other route but those two pages I can't access unless I'm logged in so let's also add a log out button to my profile page cuz when I log in here there isn't an elegant way of signing out of this so I'm going to go back to my profile page and instead of trying to redirect to user because that's what I'm doing when I log them in I'm redirecting to that page where they can choose to log in with GitHub but for sign out I'm going to do it a slightly nicer way so in my or. ts I can also export a sign out function and then I can import that into my Mi page so right here I'll import or and I'll also import the sign out function and then what I'll do is I'll create a little sign out button here but I'm going to make that a client component so I'm just going to put a place holder in here sign out button and I'll just make this in the same folder sign out button. TSX this will be use client and I'll just export a default function sign out button see what it does with that so since this is a server action I'm going to allow the sign out function to be passed in here and we'll just call whatever is passed in so on button click we will just call the sign out function whatever that is out over here I need to import the sign out button and now I need to pass it the sign out function so sign out equals and this will need to be a server action so we'll say use server and I'll make this an async function and then in This Server action we'll just call sign out so that should work pretty well I don't know what I've done wrong here though uh oh lowercase oh that's fine whatever okay so that now should work so basically I have this sign out button when it's clicked we're just going to call the sign out function that comes from nextor and this also has a parameter that we can add to it called redirect 2 so that after we've successfully signed out let's just redirect back to the homepage so that we see a feed of posts rather than getting redirected back to a login page uh so now if I go here let's see actually put that in the wrong place let's just put this up uh maybe under the profile um yeah that looks fine so now I have my profile and then I have this sign up button if I click this it should sign me out it should redirect me back to the homepage it's not doing anything I think that's because I need to actually await this so now okay it signed me out let's sign in in I should be able to go to the me page all right and now if I sign out it should do that redirect me to the homepage and if I try to go to my profile page I have to sign in in order to do that so that all makes sense so this is working really well right now and I'm able to protect each page individually by first grabbing the user session and then redirecting the user if they're not logged in and this works pretty well I can do it for Server components uh server actions API routes but some people might prefer to do this in middleware instead rather than on the individual pages so in order to do that I can create a new file in my source directory and I'm going to call this middleware dots and I'm going to dump in this generic middleware code that's going to grab some logic from nextor and run it every time we go to any route within our application so back in the .ts I need to make a few modifications here the first is that I actually need to export this configuration object so I'll just export con uh or config and set that equal to what we already had there then I can pass that straight into next orth uh I also need to make sure that this satisfies next or config so that should be good there uh so now the middleware can use that that should all run but the next thing we need to do is actually add some logic in or. TS so in the callbacks I'm going to create a new callback function and I'm just going to dump in the function here and this function will get called every time someone makes a request to our site and we can check if the user is logged in and then we can protect some paths so if the user is not logged in and the path they're trying to go to right now is in this case the slme path or the SLC create path then we're going to redirect them to the sign-in page otherwise we just do nothing and that's what this return true does just absolutely nothing so if we wanted to protect a bunch of routes here or we can protect every single route on our entire site you have to be logged in in order to use this website then using middleware can be much easier in that case but basically this just allows you to protect certain routes all from one place and whether you protect your routes using middleware or not is really up to you so that's it for this video remember to check the description for any code and links and I'll put any framework updates in there too while you're down there consider liking and subscribing and leave me a comment to let me know what you thought of this video or if you have any questions about anything that I covered in this video
Info
Channel: Sam Meech-Ward
Views: 20,253
Rating: undefined out of 5
Keywords: auth.js, authentication in next, authjs, drizzle orm, next js 14, next js tutorial, next-auth, next.js app router auth, next.js app router login, next.js app router sign up, nextjs, nextjs 14
Id: -JnEuvPmt-Q
Channel Id: undefined
Length: 26min 28sec (1588 seconds)
Published: Mon Nov 06 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.