Verify an email during user registration with Next.js 13! Mailgun and React Server Components!

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey everyone today we're gonna add email verification to user registration in nexjs13 in the new app directory email verification is a great way to ensure that a user's email is well valid it's going to cut down on spam emails and spam accounts and people signing up without real emails it also ensures that you can reach these people if you have to communicate with them this could be for your marketing newsletter or for important things like a password reset the best way to verify if an email is valid is to send an email there are lots of other ways that you can try to fake it but frankly email is complicated the internet is big and the best way to say if an email can actually receive email is to send them one hi I'm Ethan a principal software engineer who's been building and developing software as a service for over a decade I sold my previous SAS company in 2020 and now I'm helping people like you build yours so with lat make sure you like And subscribe and let's get started before we dive into the code let's do a brief project overview I'm kind of starting in the middle because a lot of my other videos cover some of this content here we have a next js13 application and what I've done is I've enabled the experimental server actions we're going to use that to handle the registration of a user we're using Tailwind for styling and we're also using my own new Lotus UI Library so I can get a feel for how it really looks and plays out in real life I'm using Prisma to manage the database and the schema you don't have to be of course but it's a pretty straightforward way to do it and then in our app directory here our main page is nothing special and we have a register page that we've created this is the outline from one of my previous videos but it basically just puts a nice little floating box into the middle of the screen I've also set up next auth we're not really using that much of next auth in this video but I'm using it to show how you can stop a user from logging into your application if they aren't verified for this we're using the credential provider and the user needs to provide their email and their password the last thing you're going to need is a way to send emails there's lots of different ways to do that on the internet I prefer to use transactional email Services these are companies that allow you to quickly send one-off emails to your customers a good example of this would be mail gun or Amazon's AWS uh simple email service feel free to use any of the ones that you're comfortable with you're going to have to sign up and configure your domain so that you can send emails the first thing we want to tackle is our Prisma schema now this is going to be pretty similar to what we had before but we have to add in the email validation so how email validation works is the user is going to sign up with one of their emails we're going to save that and we're going to send them a secret link and the link is going to include a long encrypted secret secure token the token can basically be any string at all the fact is that it's totally random and basically impossible to guess and the idea is is that we email it to them they have to click the link and then it's going to load a page in your app that will then look up that secret token in the database so the only way the user can possibly guess this long complicated secret token is if they actually got the email and so in that way we're going to be able to prove that they successfully got the email and that that email is valid now this doesn't stop people from signing up with like a temporary email service that's kind of outside of the scope of the video but it will make sure that at least for that moment they can receive emails so our Prisma schema is going to have the user model like normal email password name we're also going to add an activated or not Boolean so we can know if they've clicked that link or not really quickly on the user model itself and then we're going to add a second model and the second model is going to have the activate tokens and so this is going to have the long secure token that we're going to email them and it's how we're going to look up what that token is later and which user it belonged to so to start we're going to add a model user and here we're going to start with this so it has an ID it has a email which we're ensuring is unique it has a name and it has a password and then created that update app and we don't need that we're not making the blog so that's our user model and we're going to add in an active bit and it's going to default to false so we don't want them to be active to start so when we first create the user we're going to say you cannot log in until you've verified your email then we're going to do uh let's say call it activate token maybe activation token and here it's going to have an ID a token a creator that updated that it's not super important but importantly it needs to have unactivated at date time and that's going to be the date the the time that they clicks the link and we got it there could also be an expired time but for this we're going to say a token is valid for say 24 hours and we're just going to have that in our application hard coded it could be an environment variable that's totally fine better even um so we'll use the created add date and say if it was created less than 24 hours ago then it's valid otherwise it's invalid you could put an expiration date in here but you can kind of tweak all those numbers in other ways so it feels a little redundant lastly make sure it hooks up to your user and Prisma automatically connects everything together so this is our database schema and where we're going to start from go ahead and create the migration and migrate your database and then we can get going to using it here we're just going to validate that it worked refresh so we got our user with all the stuff and activate token with all its stuff great one of the reasons I like to separate out the activate token instead of just putting the token field on the user is this will give you a built-in audit Trail for how many times a user has tried to activate this account in your SAS app it's very likely that a user is going to sign up get distracted not click the link and then they're going to try and sign in later and you're going to say hey sorry this isn't a valid account yet you have to verify your email and maybe you'll resend the email at that point as well right well if they do that a couple times and they're never clicking the link and then they email support being like hey my account's not active it's really helpful to have an audit trail that you can look up to see where did this email go from what user what times did it happen how many times have they tried to send it and all those sorts of pieces of information so you can tie together what might be happening and causing the users and problems so we have our schema in place we have our Prisma client all set up now we need to go through and register our user our registration page is nothing special right now in fact we don't have a link to it we just have to go to register and here we go create your account link to sign in and there's no form yet now in the last video uh creating a custom register page I ended up building a client-side form and we used that to register a user now with next js's server actions we don't have to do that everything we're doing can be rendered server side and we can use a server action to handle the forms registration let's dive in so on the register page we get out of form and now within the form we have to add our text Fields we're going to add one for name one for email and one for password so now we can take a look at roughly what this looks like we have our form we have an input for name we have one for email and we've one for password We'll add in styling in a second but the important thing is we have these inputs now and we've added the names and so what we can do is we can now initiate and build our server action which will handle the data server actions are brand new they have to be async functions and they're going to take in in this case the form data automatically async function let's do uh register user and this is going to be form data from the form and we have to Define use server as the directive in this function then for right now we're just going to console log the data so we can see that this is all wired together and so the register user now becomes a part of the action of the form if you get any type errors make sure your typescript and your type definitions are all up to date this is brand new in The Cutting Edge of react so if your type react package isn't up to date it's going to yell at you in weird ways lastly to submit the form go ahead and add a type submit button or register jump back to the form and here we can click register and nothing's going to happen at first but if we look at the logs on the server here is the data that the server got name Ethan value for my email name password value testing very secure so here automatically by just doing form action and defining This Server function next JS wires these together and allows this register function to execute on the server even though it's defined here like in the jsx now we aren't using use client at the top of this page right so this entire page is being rendered on the server it's a react server component and that allows us to inline the definition of the server action next.js defaults in this way so this is the way we're going to go ahead and do it if you have to use client components and mix them match them with server components there are ways to do that check out the next JS docs to see exactly what you have to do but the important thing here is that this register user function is running on the server which means we have full access to everything we need on the server like Prisma and our connection to the database so with all that being said the main thing we have to do here is we have to register the user and then of course we don't want to store an unencrypted password so encrypt it because the data you're pulling out of this can be either a string or a file or some other piece of information we're just going to cast it to be a string because we know what the user is inputting fantastic we now have created our user and by default it sets activated to false before we move on I'm going to clean up the form a little bit and use my Lotus library to make it look a little prettier the great thing about this is because the components I'm adding are just fancy inputs it's going to automatically be wired up exactly like we have our basic inputs and button wired up so here I can do text field from Lotus and we'll do name and label as all the same and I will do the same thing for the other fields and now let's go ahead and test the entire thing out and see if we created a user in our database we have a much prettier form now let's go ahead and fill it out with some information hit register and our logs suggest that the user was created check out our database and there we go my email my name and encrypted password the other thing to note is that active is correctly set to false which means I should not be able to log in let's give it a try here we're just using the very basic next off credential login page and oh right I guess we signed in successfully the authorize function hasn't been updated yet to check to know if user is activated or not let's go ahead and add that in our API route for the credential provider here we're looking up the user and then checking the password so if we don't find a user based off of their email we return null as an error but we also want to say if the user is not active we need to throw an error now you could handle this in the same if statement and check both cases but they're not quite the same thing right the first is saying we didn't find the user at all there was no valid user for this email your credential is probably incorrect but the other thing is well we found the user we haven't activated it yet and so a good user experience is going to differentiate between those two and inform the user of what the next step should be in the first case it's check to see if you typed your email correctly in the second case it's that email is correct but the user hasn't been activated yet so maybe you want us to send the activation email again or check your email to see if you have an activation link something like that I'm not going to go through and build all of those edge cases but I am going to make sure that if user is not active they can't log in so let's go ahead and try and log in again with my inactive user now when I go to login I should have an error saying sorry unsuccessful and you can see up here the errors user is not active which is the error that we threw so a better experience spend some time on it is going to be to update this sort of error page to make sure you handle that error and tell the user what the next step should be well the next step should be to send an email and allow them to click a link so they can activate their user for this I'm going to be using mailgun mainly because I already have an account I know how to use its API and it's relatively user friendly their documentation is a little lacking so feel free to use some other email service the important thing is that you can send transactional emails which are like one-off emails and you know make sure they get delivered successfully so what we're sending is going to be an activate token so we need to create that in Prisma and then use the token in the email that we're gonna send so the data needs to have a token with it and it needs to connect to the user ID the token in this case is going to be let's see random uuid from the crypto Library and let's go ahead and concatenate two of those together and this will be two V4 uuids which is pretty long and then we're just going to remove all the dashes from it all right the random uuid function generates a random RFC version 4 uuid generated using a cryptographic pseudo random number generator pretty secure pretty random and we took two of them slammed them together pretty sure nobody's gonna guess this so now we go ahead and save that token into the database the Creator that date is going to be populated automatically so we don't need to worry about that now we need to send this token to the user and so this is where mail gun steps in we're going to use their SDK to fire off an email guns documentation is a little lacking but here is basically the entire thing of what we need we need an API key and a domain from where you're sending the email that's all configured in your dashboard so make sure you sign up and get that configured and then we have to import mail gun and form data I don't know why they make you import form data I think that's silly create the mailgun client here create your message data which is pretty straightforward who are you sending it to who is it from what's the subject what's the text exactly what I would want in an API and then you fire it off handle any errors and you're good to go so we're basically going to take this and toss it into our next JS application so the first thing we're going to do is we're going to grab the API key and the domain from the environment let's let you have a test and a production one we're then going to create the mail gun client import Forum data from form data and we're going to create the client with our API key and now we can create that blob of what we're sending as our email so here are message data from example email this can be whatever you want this part is going to show up as the name of the person and this will show up as their email so here I'm using hello at the Domain you've configured but you can basically set it to be whatever you want who are we sending it to is the user's email this is proof that they can get the email the subject please activate your account and here's the text now you can send HTML and plain text and you can make fancy templates and you can do all of that have fun for this we're just going to send plain text and we're going to say hello please activate your account by clicking this link now the link is important for this video I'm simply hard coding in localhost but you want this to be whatever environment it's sent from and so it's a great idea to read this variable from the environment in Purcell you can use the environment variables to know if you're on a preview Branch or production uh in production you can just set this to be whatever the production URL is in local development well I'm only doing local development at the moment so we're sending it to local and then we have to send it to a specific route this is going to be the part of the application that receives the request that the user clicked the link in the email and they're loading your app this is going to do the actual activation of the user account we're going to build that next so for now I'm putting it at Route activate and then I'm appending the token as a URL parameter and then we go ahead and fire off client messages create and that's going to send the actual email all right Let's test it go ahead and register looks like it worked successfully go ahead and check my email success hello Ethan Mick please activate your account by clicking this link you can see it points back to localhost and has the exact token that we want so now when I paste that in oh right I guess we have to build that page so the handle the user activation we need to have a page in our application that looks at the token in the URL finds that user in the database make sure the token is valid make sure the user is valid and go ahead and set the user to be active there are two ways to do this the traditional way to do it is to make a page that loads it looks at the token in the URL it makes an API request and it's going to do all that work on the server side in the API Handler but we're using next js13 and we can just create an API route that goes ahead and handles that directly and then after it handles it it can redirect the user now there are pros and cons to both of these approaches but I think the second one's pretty neat the first one you have to load the entire page and then make an additional API request and then you can handle the response and then you can tell the user what to do next the pro to that though is that if some email provider is sniffing around the links it's going to load this HTML page first and none let logic is probably going to fire now if you build an API route the API route will accept the link directly activate the user and can then redirect the browser to where the user should go next either the login page saying hey you've successfully logged in or an error page saying that didn't work maybe your link is expired and so it's fewer Hops and fewer Network requests and the great thing about next js13 is that our API route handlers can be embedded within the app so the actual route that you're defining can look really nice I'm going to go ahead and do it the second way but feel free to go ahead and do it the first way the logic's the same it's just how you handle the request so we're going to create a new file we're gonna call it activate and it's going to be a route but there's going to be a token parameter and Route dot TS all right so this is an API route and it's activate and there's a token and so that matches the URL that we have here activate token so this is an API route so we're going to export async function get it takes in a request which is a next request and then the second parameter are the parameters in the URL in this case params token string so now we want to read the token from the URL wrong read the token from the URL go ahead and find the user it belongs to and then activate everything we want to activate here we want to find the user in which the token belongs to this user and the token is not expired so it's created that date was less than 24 hours ago so to do that we're going to have activate token and we're going to find some that the activated date is null so they can't have activated it yet and the Creator that date was greater than this and token is the token that they passed in so of course if there is no user then the token was involved in some way now here you can also check to see what way the token might be invalid and so you can display a different error message for if it's expired or if they couldn't find a user or something now assuming we did find a user we know all of those checks are already complete we know that the activated date was null so they haven't activated and used this token yet we know it was created less than 24 hours ago so it's valid there and it matched the token so all we have to do now is go through and activate the user and mark the token as used and now we want to Mark the token as used as well to make sure they can't use it again so when we finally get to this point remember the users clicked this link and it's loaded this API route so now we want to redirect the user to the login page allowing them to log in let's go ahead and test this out it's not going to be the most user friendly when it throws an error but I'll leave that up to an exercise for you ideally a redirects the user to some error page to say hey this failed the easiest way to test it out for this very first time is going to be to reload this page because it didn't work in the first place and success kind of fanfare list now our user in the database has been activated and if we look at the activate token it has the date time when it was activated and that's really all that that it does now it has data so now if you try to do it again you're going to get a 500 error and if you look at the logs it says oh invalid token so user can't try to activate their account twice for whatever reason with the same token and now if we try to log in it was successful because our user has been marked as active so there you have email verification built into your application it doesn't use that much of next auth it really plugs into the registration flow you can use this to make sure users have valid emails or you can use this to perhaps gatekeep features in your application until a user validates their email it's a really powerful way to make sure that your users aren't spamming your application thank you so much for watching if you have any questions or suggestions feel free to shout them out in the comments and until next time happy coding
Info
Channel: Build SaaS with Ethan
Views: 6,789
Rating: undefined out of 5
Keywords: next, nextjs, next.js, react, software, coding, software engineering, mailgun, email, users, next-auth, credential provider, credentails, validate email, valid email, lotus, lotus-ux
Id: g6sTypMUx48
Channel Id: undefined
Length: 29min 20sec (1760 seconds)
Published: Wed Jun 07 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.