Build a password reset flow for your SaaS app! Next.js | Radix | Tailwind | Source Code

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
today I'm going to walk through how to build a password reset flow for your application I'm going to be doing this in nextjs using Prisma tailwind and radex however it doesn't really matter what framework you're using we're going to talk about how to do this at a general high level and what the flow is going to be and then I'll walk through the implementation for this particular example so if you want to grab the full source code you can do that in the description down below otherwise let's get to it the first thing I want to do real quick is talk about how password reset works at a high level this will set the stage for what we need to do at the database layer and how your application needs to function now password reset isn't super complicated to start out you have a user and your lovely little user here has forgotten their password and so they go to their app and they're going to request from your application a password reset and that's the thing that's going to kick off this flow so here they're going to say reset password now when they ask that they need to provide some piece of information that only the user has access to so they can get a one-time password code that code is going to ensure that when they reset their password we're resetting the password for this particular user so where do we send that code most of the time that's going to be the user's email which is a fairly common thing for a user to give you when the user signs up in fact this is one of the main reasons why we actually want or need a user to sign up with their email when you're doing a credential login if you never need to have an interaction like this then you could just ask for a random username the other reason you ask for an email of course is so you can send the marketing messages once the user has said they want to reset the password what your application needs to do is it needs to generate that onetime code and then send it to them to their email and so that's what's going to happen right here and let's go ahead and add in the user email so this is the onetime code that the user uses now the user knows what to do they're going to go ahead and check their email for that code and then that's going to be a link that the user clicks to go back to your application so now here in your application the user now has their code that they clicked and your application knows who this user is and that they securely got their email and they clicked the link and so at this point the user can set a new so at this point the user can set a new password and all of this is going to go into your application wh and so here your application can validate that yes this code is legit it's been received in a timely manner and that it goes back to the user which the code was attached to and that the password meets all of the password requirements you might have for your application and once all of those things check out the application can reset the password and allow the user to log in now with their new password to start off you need to have some place in your database where you're going to store these password reset codes now you could store them right on the user password reset token perhaps but then you don't have any sort of audit Trail built in for how many times a user has reset their password if it was successful or if there's anything else fishy going on so instead of putting it on the user what I like to do is I like to build out a separate table and this table will have all the information we need to do to do a password reset and it simply has a foreign key to whichever user we're targeting something like this will do great a password reset token table and so it has a primary key which is an ID and then it has this token the only thing the token needs to be is some complex unique cryptographically secure string okay so it needs to be a few things but we can simply mark it as unique and that's what's going to be the token we send to the user's email and then it's important to have a created atate so you know when this request was asked for and it's good to know when it was reset this could be like an updated time but if it's updated and any other way then you won't know exactly when it was reset so I like to call out specifically hey this was when the user actually clicked the link submitted a new password and when we reset the password now that you know how the password reset works at a high level and what the flow is let's jump into the code and go ahead and build out a full example for this full example I'm using nextjs with a couple of other technology sprinkled in so I'm using Prisma as my database omm you don't need to be using Prisma but it's just a great way to interact with the database in this case I'm using postgress if that matters to you for the UI I'm using radex CU I wanted to play around with their new rad themes and see what that looks like we're also using Tailwind for a little bit of styling help bcrypt to encrypt passwords of course and then to send the email we're using mail gun and the mail gun service now now you can use any sort of service provider that can send transactional emails it doesn't have to be mail gun sces anything in Azure or any other email provider that you're comfortable with the other thing that I'm doing here is I'm using the nextjs server actions to handle the server side logic so I'm not doing API routes which you're welcome to do it will work the same way except you'll have a slightly different file configuration and you'll be using the API routes instead of server actions but the general flow is going to stay the same so what's the first part of the flow the user goes to your login page they try to log in and then they realize they've forgotten their password this happens all the time much more frequent in fact than I expected it to happen when I've been building SAS apps so giving them a way to reset their password selfs serve means they're not going to be reaching out to you asking for your help to let them get back into your application you can name this page whatever you want however I think something like for forgot password page is probably a pretty good thing to do so what the forgot password page needs to have is basically just a form which is going to ask them to submit their email and then once they submit their email we'll go ahead and look up that user and send them the onetime login code here we have our forgot password page so we have a our main element which is going to take up the entire page and center the contents within it we have a card which is just going to envelop our form and then we're using some Flex to make sure the items inside it are a column and so here we have our form and this is just going to say reset password give some information and have a basic text field that asks the user for their email and then when they go ahead and submit it we'll go ahead and run the server action with radx UI this is what our page looks like and if we go ahead and send some sort of email here and click reset well nothing's going to happen because we don't have the action written yet but this is going to submit the form and that's where we're going to have to take over with our Logic the first thing we're going to do is add our Asing function reset password and make sure we use use server here which is going to allow it to be called as a server action and once we've defined it we can go ahead and wire it up into the form so this is going to be called remember the server action is going to take in the data from the form and it's going to be form data so the only thing we're really looking for here is the email and the attribute's going to be called email in the form data so we can go ahead and pull out the email from the data and go ahead and check if we don't have it or if it's not a string if they tried to submit something weird as the email we can return an error saying invalid email from a server action if we return a serializable object we can then pull out that data later if we want to so this is a good way to return errors that you might want to handle on the client side now if you do this we're going to have to change some configurations so we'll get to that later but this is a nice way to return errors once we know we have the email and it's a valid email or valid is we can go ahead and look up the user so here we're using Prisma to find the user where the email is what we pulled out so assuming we are validating emails upon a user signing up we don't need to do too much validation here to check if the email is valid because we're just going to look up the user if there is no user then it can't possibly be something that we care about and so we check to see if we have the user and if we don't we go ahead and return an error now this is something that you might want to do a little different here we're checking to see if that user exists and if they don't exist we're actually returning that as a possible error now if you are building an application that is very secure at econscious right you might not want to leak if a user exists in your database or not and so instead of returning an error here you can just return a success as true to pretend like you're about to send the email this way if an attacker was trying to use your system to validate emails or see if a user exists they wouldn't be able to get any information now I've actually seen this both ways including on things like banking apps so it's not always clear-cut the best way to go about and do this so you need to take a look at your threat profile and what your users are to decide if you want something that's a little more usable or a little more secure remember if the user can't get access to your application they're not going to be able to log in right so if they fat finger their email typing it in and type in the email just one digit off and you say hey yeah we sent you that reset email and then they don't get it they might just never come back to your application so it's a trade-off between usability and security so once we know we have the user what we're going to do here is we're going to create a password reset token and this password reset token is going to attach to the user as the ID and it's also going to have some sort of random token associated with it now our token here is generated by two random uu IDs and this is from the crypto Library so it's relatively secure it's a cryptographic pseudo random uuid for this example that's going to be plenty good enough if you want to generate the token some other way including in the database itself you can do that too now that we have everything put into our database the way we expect we need to send the user an email to send them an email I'm going to be using the mail gun service that's simply because I already have an account for them and for all of these demos and examples it's very easy for me to use what I already have set up that being said you're welcome to use anything you want to use here I'm just setting up the mail ma gun clients and the environment variables that we will need to use in order to send an email so it's important when you're doing this to try to use environment variables as much as possible so you can configure this correctly in local development in your staging environments and your production environments now we can go ahead and create the message that we want to send and then send it here is simply the format that we have to use to send an email with mail gun so we're sending it from some sort of password reset Security and this is at whatever domain you have set up with mail gun we're sending it to the user's email which we know should be valid and a subject and the text the important thing in the text is that we have this link set up so we have the protocol because on local Dev it might be HTTP and in production it should be https whatever domain you have set up again that'll change in different environments and lastly where you want them to go and we we haven't created this route yet but we're going to send them to the password reset route with the token that we've generated and so that's going to be the page that captures this information lets the user create a new email lets the user create a new password and then submits it to our application lastly we go ahead and send it and then finally we redirect to this forgot password success page now instead of redirecting what you could do is you could return success equals true and then have the client update if you do that you simply need to make sure that your page or whatever is receiving this information is a client component because you're going to have some State instead to keep it all a server component we can simply create a new page called success and this page can have our success message on it let's add that real quick a very simple card in the center of the page as before which says hey we set sent you the email check your spam folder if you need to and otherwise you can return to login let's go ahead and test out our flow if we type in something that is registered in this case my email because I actually want to get the email we can now reset the password and we should be sent straight to Our Success page all right that worked perfectly so we have our password reset congratulations if the email doesn't show up check your spam folder and then return to login here we're just going to go back to the homepage because we don't have the login set up so once you check your email you should have a reset password request this came from security came from the domain that's been set up and then says hello Ethan someone you requested a password reset for this account and this is a clickable link that we can go ahead and click and reset our password so let's do that oh right crap we haven't actually implemented this page yet let's go ahead and implement the actual password reset page and then we can go go ahead and test that part out all right so our user has now typed in their email gotten an email and clicked a link and this link takes them to password reset or something like that where they can submit a new password and then be able to log in there are two ways you can read the token from the URL you can use Query parameters in which case you want to do like question mark token equals whatever the token is or in nextjs it's very easy to also have it as a URL parameter either way is fine since we're only sending one particular token here and no other information I'm going to read it out as a URL parameter so I'll call it token and then make our page and this page is going to look pretty familiar to the last page except it's going to have two text fields in it allowing the user to submit their password all right here's our first pass at our new form so basic function exporting the page just as normal we have our main element just as before which is going to Center the information in here we have a card which has a form INE just as before and we're going to define the server action next now here we're letting the user choose their new password and giving them two text Fields so password and confirm and this is where they're going to type in their new password and then submit it if we refresh our page we can now have this form here here which is perfect looks good however we can't submit it yet so let's go ahead and build out the server action which will capture this information and actually reset the password same as before we're going to define a new function reset password that'll take in the data from the form now we also need the token but if you're defining this function in line you can simply read the token as the property from the page itself so you don't have to explicitly pass it in the first thing we want want to do is we want to get the password and the confirm password from the data and then if anything's not correct here we want to return an error so if you are going to tell the user what went wrong you have the error message that you can use then assuming that the password is correct we can go ahead and look up the password reset token when we're looking up the token we're also validating in the query itself if the token is valid so here we're finding a one unique token toen where the token is the token right this is what's passed in from the URL parameter and we're reading it right from here so pam. token and then here we're checking to see if the token was created at a date within 4 hours now I know this is a little messy but it's the date now subtract milliseconds seconds minutes for hours right times four and so we're saying only find a token if it was created less than four hours ago anything beyond that will not fulfill this query and so we're not going to find it and the other check we're doing is to make sure that the reset at is null and so this means the token hasn't been used yet either so somebody can't reuse a password reset request so if we don't find these then that means it was some sort of invalid request and the best thing the user can do is try the process over again all right so assuming that we actually found the password reset token and it is valid we can now create a new encrypted password this is using B Crypts hash function to Hash the password and we're doing it with 12 rounds you can use an environment variable here or have some other default but choose a relatively High number to ensure it is totally encrypted and then we're going to create the Prisma user update request where we're going to reset the password to whatever the user ID was of our password reset token now I'm not awaiting this request yet and I'll show you why in a second the other thing we have to do is we have to update the token to ensure that we've said that we actually reset it now if either of those fails for whatever reason we don't want this request to be successful right we want to ensure we've updated the user password and we've marked this token at as being used and so we're going to wrap these calls in a transaction call we're going to put the update user request and the update token request within this transaction so if either of these fails Prisma is going to roll back this transaction for us lastly at the end this means everything was successful we can go ahead and redirect the user back to the login page here I'm just redirecting them to the rout but you would want to send them to the login page in your application to test this out you can take a look at my pass password Here encrypted of course and it ends in easyy blah blah blah blah blah so we will know if this is successful is when I choose my new password and it's encrypted it's going to end in something different besides easyy we can also check to see the password reset token here has no reset at date yet so it hasn't been used let's go ahead and pick a new password something like password and try to reset my password all right we were redirected so that's a good sign let's check the database refresh and our token has been used the reset appdate was updated and my password no longer ends in easyy instead it's ctaw which means my password was updated as well and there you have it that's how you can build password reset into your applications this pairs excellently when using next off and the credential provider go ahead and check out the full source code and in the source code I wire up the server actions to return those errors and then use a client component on the client side to display the errors to the user I also add a nice little submit button to allow the user to see when the form is submitting and a couple other nics just to make the entire thing feel like a full application thank you so much for watching go ahead and give it a like And subscribe and until next time happy coding
Info
Channel: Build SaaS with Ethan
Views: 3,040
Rating: undefined out of 5
Keywords: next, nextjs, next.js, react, software, coding, software engineering, radix, radix-ui, shadcn, shadcn-ui, tailwind, tailwindcss, next js, pasword, password, password reset
Id: vu78olWoV0I
Channel Id: undefined
Length: 21min 25sec (1285 seconds)
Published: Thu Sep 14 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.