{
  "video_id": "1MTyCvS05V4",
  "title": "Next Auth V5 - Advanced Guide (2024)",
  "url": "https://www.youtube.com/watch?v=1MTyCvS05V4",
  "transcript": "Hey there, my name is Antonio and welcome to the Auth Masterclass, where we are going to be learning AuthJS, the new name for NextAuth version 5. In this course, you'll learn how to set up Auth using both credential and social providers like Google and GitHub. But we are not skipping the must-haves either. You'll learn how to set up forgot password and email verification. And yes, we're doing two-factor authentication and showing you how to manage different user roles, like admin and user. In this course, we're not just learning concepts. We're actually building a whole toolkit. We're going to create a set of reusable components, hooks, and utilities that you can use everywhere. In server components, client components, models, pages, API routes, and server actions. Think of it as building your own toolbox for authentication. We'll start with the user-friendly login and logout buttons, and then move on to robust registration forms. After that, we're going to tackle how to handle errors and user verification. But we're going much further than that. And for the curious minds, we'll take a peek at how to use the Next.js middleware, and also how to tweak Next.out sessions, callbacks, and events. It's like giving your out system a personal touch. In this course, you'll see real examples using server and client components and how to handle admin-only stuff. We're talking about showing different things to admins and keeping some API routes and server actions just for them. Finally, on the settings page, we'll go through how to change emails, update passwords, and even switch user roles. plus turning two-factor authentication on or off. So without further ado, let's get started. So let's get started and let's set up our project. Here on the right side, I've prepared the Next.js documentation and installation instructions. And here on the left side, I've prepared my Visual Studio code, which is going to be my editor of choice for this tutorial. So the first step is to confirm that you have the necessary system requirements to run Next.js. So make sure that you have node 18.17 or later. So let's go ahead quickly inside of our terminal. So if you use Visual Studio Code like I do, you can go into the lower left corner and you can select the terminal option from here. And then you can simply write node-v and you're going to get the version of your node. If you have lower than 18.17, you have to upgrade your node. And you can do that simply by clicking on this link right here or use the link into the description to go to Node.js homepage and install long-term support. Now let's go ahead and let's set up our project using the automatic installation command right here. So I'm going to copy this command and we're going to paste it inside of our terminal again. But before we press enter, we have to give our project a name. So let's do the following. Write the command as follows. So npx create next app at latest. And then go ahead and give your project a name. So I'm going to call this auth tutorial. And now let's go ahead and answer some questions. So we are going to be using TypeScript in this project. So make sure you select yes. You can also select yes for Eslint. Make sure you select yes for Tailwind CSS because we're going to be using that to style our components. Select no for the source directory. And very important, select yes for the app router. So we're going to be using server components. So make sure you select yes for that. And lastly, it's asking us whether we want to customize the default import alias. I recommend that you select no because I'm going to select no. I'm going to leave it as it is. And now just wait for all of those to install. After your project has been installed, you can go ahead and open it in this location right here. So I'm going to go ahead and do that. I'm going to click on open right here. And there we go. I have a folder called Auth Tutorial. I'm going to click open. And if you get this big model, you can feel free to press yes, I trust the authors. And inside of here, you're going to have a couple of config files like Tailwind config, tsconfig, nextconfig, but also the app folder. And inside of the app folder, you're going to have a page.tsx, layout.tsx, and globals.css file. Before we run the app, I want to add one more thing inside of this project, and that is ShatCN UI. ShatCN UI is going to be our, well, not exactly a component library, but as it's written here, a collection of reusable components that we can copy and paste inside of our apps. So go ahead and visit ShatCN UI. You can use the link in the description or go to UI.ShatCN.com. And now what we're going to do is we're going to go ahead and follow the installation instructions. So once you go to the installation here, make sure you select Next.js for the framework option. And now let's go ahead and see what we have to run. So you can see that we've already run this command. We already created our project, so no need to do this again. The second thing we have to do is open our project inside of Visual Studio Code and then run this command right here. So I'm going to copy this for npm and let's go back inside of our terminal. Make sure you're inside of your project and run the following command. npx chat cnui at latest in it like this. And now we're going to have to answer a couple of questions as well. So again, it's asking us whether we are using TypeScript or not. Since our project is using TypeScript, it makes sense that our components will also use TypeScript. So select yes. For the style, go ahead and select New York style. And for the color, go ahead and select slate color. It's asking us where is our global.css file. If it's telling you app slash globals.css, that is correct. So you can just press enter. It's asking us whether we want to use CSS variables for colors. You can select yes here. It's asking us if we're using a custom Tailwind prefix. You can just leave it blank. So just press enter. It's asking us where is our Tailwind located. And here's a little trick here. So this is actually an incorrect placeholder here. Because if you take a look in our project, we have Tailwind.config.ts, but not .js. So what you have to do is press the tab key and then modify this to go to .ts and simply press enter. And you can leave the import alias to be exactly as it is, at slash components. So just press enter and same thing for libs. And it's asking us whether we're using React server components. We know that the answer is yes, because we selected the app router option in the beginning. And finally, confirm all of those options. And now wait a second for all of those things to install. Great. After Shadzian has been initialized inside of our project, you're going to see a couple of changes. So primarily you're going to see a new lib folder with the utils inside with a very simple cn function. We're going to be using this throughout the project to dynamically add classes. Next we have a components folder which for now is completely empty but later here is where our buttons are going to be. Our models, our dialogues, our drop downs, everything else. And inside of the app folder, inside of globals, you're also going to notice a change. but if you didn't open the file from before, you're probably not going to know what changed exactly, but it doesn't matter. All you have to know is that now we have chat-cn inside of our project and we can now follow the components from that package. Great. So now let's go ahead and let's actually run our project. So let's go back inside of the terminal here and let's do npm run dev. This will start the project on localhost 3000. So let's go ahead and visit our project. Go to localhost 3000 right here and you should be seeing a similar landing page, something like this depending on whether you're zoomed in or zoomed out. Great. So what I want to do now is clear this entire screen. So because I know how routing works inside of Next.js, I know that I can find this file inside of the app folder inside of page.tsx. Don't worry, I'm going to go ahead and explain in the next module how I know that this is the exact page that I have to modify. So we are going to have a small little crash course on the routing inside of Next.js. Of course, if you're already familiar with that, you can just skip or speed up that part. So go ahead inside of the app folder page.tsx right here and go ahead and select everything inside of this return function and simply remove it. So starting with the main element right here, all the way to the bottom, select everything and simply press delete. Great. And then you can go ahead and write a little paragraph here and just write hello out like this. And you can remove this import for the image like that. Let's go ahead and refresh this page. And there we go. We now have hello out right here. Now let's go ahead and test our tailwind a little bit. So I'm going to go ahead and collapse this and give this paragraph a class name of font semi bold. And that will make the font bold, as you can see right here. And you can see how when I hover on this, I have a little tooltip which explains me which class name, sorry, which attribute has been given with this class name. If you're not having this, you can go ahead and go inside of the extensions and write Tailwind CSS IntelliSense. select the first one and install and that's going to help you with writing Tailwind. You can also see that when I write some classes I also have auto-complete so if I'm not sure which one exists I can use the auto-complete option like this and one cool thing is that when I give my paragraph a color for example text green 500 you can see how I have a little box to indicate which color it is. Great. So make sure that your tailwind is working like that. And the next thing I want to try out is how to add a component from ShadCN UI inside of our project. So ShadCN UI is not like your usual component library, because, well, as you've just read from the introduction, ShadCN is not a component library. It is a collection of reusable components that we can and paste inside of our apps And it based on the Radix primitives So let go ahead and find a component that we are going to need First of all I already know that we going to need the button component So go ahead and find the button component right here. And in here, we have the option to manually add it to our project, or we can use the command line interface. So let's go ahead and do that. I'm going to copy this command right here. I'm going to go ahead inside of my terminal here. and if you want to you can shut down the app or you can just open a new terminal for myself I'm just going to shut it down and go ahead and write npx chat cn dash ui at latest add button and press enter and now that is going to initialize the button inside of our project so if you've shut down your app make sure you run npm run dev again so you're running on localhost and now let's go ahead and look inside of our components folder and in here you will see the ui folder with button dot tsx. And this is the cool thing about chat CNUI. Instead of storing the component inside of nod modules, like most component libraries do, this one is inside of our components folder. And we have full access to the TypeScript. We have full access to the props. We have full access to display name. We have full access to component name, to button variants. We can change every single class name in here. We can add new variants. If destructive outline, a secondary ghost and link are not enough, we can go ahead and add a third one to be whatever we want and modify it and style it how we want. Same thing is true for sizes. If we need an extra large size, we can go ahead and simply write an additional class name for that. So that's why I love Chatsy and UI. It is a true library. Well, I don't know if they prefer to be called a library, but it is the best solution to write your own component library inside of your project. Great. So just make sure that you have this little button here and now go back inside of the app folder page.tsx right here and let's go ahead and import that. So whenever I import something I'm going to be using the import alias. So I'm going to start with the add sign and this is the equivalent of me being in the root of my application. So the next folder I can go into is components slash UI and then I can find my button and now I know that I have to import the button export like this. Now let's replace this paragraph of with that new button here. And let's write click me. Let's go ahead and revisit our local host. So just refresh right here and you should be seeing a button which says click me. Yours might be a little bit smaller if you're not zoomed in like I am. And now you can go ahead and play around with the properties that this button has. For example, you can change the size to be large. You can go ahead and change the variant like this. For example, let's give it an outline variant. and there we go. And let me just show you how easy it is to modify this. So if you go back inside of your components UI button right here, and if you go inside of the variants, go into the single variant object right here, find the last one, which for me is link, and you can simply go ahead and add your own variant. For example, I'm going to call this custom, and I'm going to say bg sky 500 and text white like this. So just a very simple new variant here. Make sure you have a little comma here after the link variant. And then what you can do is go back inside of your app folder page and take a look at the variants. Now you can see how it auto completes our custom variant here. And when I select it, there we go. My custom variant is here. So that's why I really like this package. And I think it's going to be of great use of us of great use for us inside of this auth project. So just go ahead and remove this custom variant, we're not going to need it. And now you can see how we immediately have an error here. So you can remove that from here as well. Great. So I hope that this kind of introduced you to Next.js and our component library. If you want to, you can explore all the other components which Shatsian UI has, because we are going to be using quite a few of them here, but don't worry, we're going to go through all of that together. In the next module, I'm going to be explaining how routing works inside of Next.js. So if you are familiar with that, you can speed up or skip that part. Great, great job. So now I want to go over and explain how routing works inside of Next.js, more specifically, the app router. If you already know this, you can speed up this chapter or skip it completely. So how did I know that in order to modify this screen and add a button, I need to go inside of page.vsx? That is because I know that Next.js uses folder-based routing and has the following file convention names for specific layouts, pages, errors, and stuff like that. So I knew that if I want to find the root page of my application, which is just localhost 3000, which is technically localhost 3000 slash, right? I know that that file, that route segment is represented in the first page file that I can find in the app folder. So that's how I know that this is the file I have to modify to modify this screen. And if you're wondering where did I find that out? Well, it's in the Next.js documentation. And if you plan on exploring this documentation yourself, just make sure that here in the sidebar, you select the app router, because if you select the pages router, you're going to be viewing at the old documentation. So in here, you can see a list of some other file conventions, like the layout file, which we also have here. But this is the one that I was talking about. Page is a file which makes unique UI of a route and makes the routes publicly accessible. So let's go ahead and scroll a bit up, because in here, we can see that even better. So you can see that we have this app folder, which by itself represents the root page. So inside of the app folder, this page represents the root page. That's how I know where to find that. And if I go ahead and create a new folder and name it absolutely anything inside of the app folder, and then put another file inside of it called page.tsx, then that's going to be rendered as dashboard. And same is true for as deep as I want to go. So let's go ahead and take this concept and actually create that. So I'm going to go ahead and I'm going to create a new folder inside of my app folder here called dashboard. And just by now, if you try and go to slash dashboard on your local host, you're going to get a 404. So why is that? Well, remember, we need to do some file conventions here. In order to turn this folder into a route, we need to give it page.tsx like this, page.tsx. And here's an important thing about page.tsx. So the only thing that matters is that the file name is page, and it doesn't matter if it is .jsx or tsx, right? So it's just, it matters that it is page. And the other thing that matters is that you do a default export. For example, I'm going to call this a dashboard page like this. So just make sure that you don't accidentally do this export const without the default. So if you do this, it's not going to work. But if you keep the default export, that's going to work. Let's go ahead and return dashboard page like this. So if I go ahead to localhost 3000 slash dashboard now, there we go. I am on the dashboard page. Great. And now let's just confirm that I can create some even more nested routes. So inside of this dashboard folder, I will create a new one called settings. And inside of it, a new page.tsx. And let's go ahead and do the same thing. Settings page. And let's return a div settings page. Like this. So now I'm going to go to localhost 3000 slash dashboard slash settings. And there we go. I am on the settings page. Great. So what did we learn? Well, we learned that in order to turn a folder, which is the default routing mechanism of Next.js into an actual route segment, we need to use a file convention called page. And inside of that page, it doesn't matter what our component is named. It can be named anything like this. All that matters is that we do a default export. And if I refresh, there we go. Everything is still working. So remember, there is a file convention called page, which needs to be inside of a folder, which you want to turn into a route segment. But there are some other things you might be interested in. For example, what is the layout file? Let's go ahead and see what the official definition says. So in here, we have a layout file convention, which is a shared UI for a segment and its children. And you can see more about that right here. You can see how it works on this very example that we have here. So we have the dashboard folder, right? And if we create a layout file, then that code inside is going to be shared both in the dashboard route segment, but also in the settings route segment. So layouts are very useful. If, for example, if I go on localhost 3000, let's imagine that this is our homepage, right? And then on the slash dashboard, I want to go ahead and add a little navbar. So the perfect place to do that is, well, the layout file. Because then it's going to be shared across the settings page as well. Because right now you can see that if I go to slash dashboard slash settings, there isn't much difference between them, right? You don't even notice that this is a child of dashboard. Sometimes this is exactly what you want. But most of the time, you're going to want to give your users some kind of indication that you are in the dashboard route segment. And layouts are perfect for that. So inside of the dashboard folder here, I'm going to go ahead and create a new file, layout.tsx. And let's go ahead and learn some things about layout. So you will immediately get an error like this because we are missing a default export here. So let's go ahead and call this a dashboard layout. and let's return a div and just write layout inside. And what happened now? As you can see, we are no longer rendering the contents of our page.vsx and I am on localhost 3000 slash dashboard. If I go to slash dashboard slash settings, same thing happens. The route is obviously working, but nothing is rendering here. That is because whenever you are working with a layout file, you have to remember that this is a file convention, right? So this is a reserved file. Next.js is gonna render this in a very specific way. So here's what you have to do every time you use a layout file. You have to extract the children. So I'm gonna go ahead and just quickly extract the children here. And just for now, I'm gonna give them a type of any, right? It doesn't matter. Usually they are a type of, well, I am gonna write it now. They a type of React Like this And then what we do is we just render the children like this And what happens now if you take a look there we go we can now see our slash dashboard slash settings normally. And if I go back, we can see our dashboard page. So what did we achieve by creating this layout file? Why is this even needed? Well, let me show you. Let's go ahead and modify this a bit. So I'm gonna go ahead and give this a class name of, let's give this a flex, flex call and gap Y4. And then in here, I'm gonna create a little navbar component like this. And I'm gonna write, this is a shared navbar for dashboard segment, maybe a bit long. And let's go ahead and give this a background color of black and text of white like this. And let's take a look at it now. So we can now see that we have this mock navbar at the top, which is clearly in the dashboard page. But what happens if we go to slash settings? It is also here. So every single route that we create inside of the dashboard folder is now gonna have this little navbar. So let's try that out. So inside of the dashboard folder, I'm gonna create a new one called users, for example. And inside a new page.vsx, let's go ahead and render a users page, return a div, users page, like that. So where is this going to be located? At localhost 3000 slash dashboard slash users. And the users and the settings and the dashboard, which is inside of this page.vsx, are all going to share this layout file right here. So let's go ahead and take a look at that. So if I go to slash dashboard slash users, there we go. The same thing. This is a shared navbar for the dashboard segment. Perfect. So I hope that kind of cleared up what a layout is useful for. It is especially useful when you want to do something like, for example, this is something we're going to have. We're going to have a folder called auth, right? And inside of here, we're going to have something like a folder called login. and then we're going to have a new file page.vsx and then we're going to have a simple login page with a default export and this is going to say login page, right? And now what's cool about this layout files is that if I go to localhost 3000 slash out slash login, you can see how this one doesn't have that layout, right? Because that layout that I want is specifically for once the user logs in, right? when they have the entire sidebar and the navbar. So that's why those layout files are very useful. And I hope that kind of cleared it up. And here's the thing. If you try and look inside of your app folder, you will notice that you also have a root layout here alongside our page.vsx here. So this is a very important file that gives us crucial structure to our project, like HTML and body. So you must not remove these files. This one is very important. But other layout files, which you manually create, like this one in the dashboard, you can always remove them. So if I just go ahead and remove this, the page is not going to break. But now all of those, all of these files here will lose their shared navbar, right? So that's what layout files do. And let's just quickly go ahead and just take a look at all the other file conventions that we have here. So we just explained what the layout is. We explained what the page is. Besides that, you have the loading. loading will be triggered as a suspense around your page if you directly await something inside of page.vsx so if I went and did something like inside of the dashboard page.vsx here this is a server component by default so if I go ahead and give this an asynchronous thing and do something like const await sorry const users await get users from wherever Whoops. So just a mock function. This doesn't exist, of course. Then if I had another file inside of dashboard called loading, then that file will be shown to the user until this is loaded, right? So that's what that loading file is used for. But I do that in my other tutorials, if you're more interested in that. In this one, I'm just going to focus on the out, but I still want to give you a little bit of, well, introduction to Next.js. And we have a custom not found. So that is if you're going to be with Next.js does have a default not found, but you can limit the not found to a specific route segment, as you can clearly see here. So you can limit your own not found and customize the not found page using this file convention. Error is for catching errors. And you can go ahead and explore on your own what the other file conventions do. Great. And I just want to go ahead and do just a few more tips and tricks that you might find helpful here. So we just learned how we can share a layout using a folder like a dashboard. But here's the problem, right? What if, for example, you want... Let's go ahead and remove the dashboard folder. We can do that. Let's just focus on the out folder. So make sure you have an out folder, login and page.psx inside. So here's the thing. So I want to go ahead and create inside of this out folder a layout.psx like this. And I'm going to go ahead and do a default export of out layout. I'm going to go ahead and get the children. We already know what their type is, but this is just for demonstration. And I'm going to render the children inside like this. And then I'm going to go ahead and add a little navbar here. and I'm going to write this is alf not bar. Let's go ahead and give this a BG red and text white. And let's give it a BG red of 500 more specifically like this. Great. So now if I go ahead and I don't know, copy this login and rename this to register, right? Go inside of the register page, rename this to register page and let's render register page. we already know what's going to happen. Both login and register pages are now going to share this out navbar. So if I go to slash out slash register, we know that these two are sharing the navbar. But here's a little problem, right? You might have already noticed if you're kind of experienced in routing, right? It seems like the only way we can create a shared layout is by creating a folder like out, which simply has to be a part of our URL, right? What if I just wanna go to slash login or just slash register without the slash out first? Is it possible to do a shared layout? Well, actually it is. And there is a specific way you can do that. So here's what I recommend you do. Remove absolutely everything regarding this out folder. So just the out folder, not everything. And you should be getting a 404. And you can just go ahead and return to localhost 3000 like this. And here's what I'm going to do. So I'm going to go ahead and create a new folder. But this time, I'm going to wrap it in parentheses. And I'm going to call it out again. And then inside, I'm going to create another folder called login and another page. And very simply, I'm going to do the same thing. Login page, div, login page without out prefix like this. So can you guess what this does? Well, I think you can guess because of this text which we wrote. How do we access this page now? Well, I'm going to give you a tip. We are not writing alt anymore. So this wrapping in parentheses is a folder convention in Next.js, which tells the router that this is a specific segment, right? So this can hold its own layout, just like our previous alt folder could. but one cool thing is that this will not be visible in the URL segment. So if you go ahead and go to localhost 3000 slash login directly, there we go. So this helps us both organize our routes better, especially if we're going to have a lot of out routes like a login, register, forgot password, this, that, right? So because of that, this kind of route groups are especially useful. And here's another cool thing, of course, if you create a layout.tsx file, it will still work. So I'm going to go ahead and create out layout here. I'm going to go ahead and extract the children. And I'm going to simply write a div here, a navbar. This is navbar without out prefix. Let's give it a class name bgred500 and a text of white and let's render the children below. And there we go. So you can see how we are still sharing the layout, but look at our URL. It is just localhost 3000 slash login. So I just wanted to clear that up. There's a lot of possibilities that you can do inside of the app router, right? You can read the documentation and find so much more things that you can actually do. But I'm explaining some concepts that I will be doing in this tutorial. So I just want to make sure that you're not getting confused when it comes to doing that. Great. And there's just a couple of more things I want to show you. So I wanted to show you this. So if you go ahead and create a folder inside of the app, and let's call it components, for example. And inside, if you have a file called page.vsx, right? And if you accidentally do a default export, like components page and a div components page, you already know that this is going to become a route, right? But look at how this looks. We technically don't want this to be a route, right? This looks like it should be an inner components folder. But if I go to my localhost 3000 slash components, look at this, it renders the components page. That is definitely not something we want, right? Because if you write it like this, you're probably bringing in, you know, some habits from single page applications because you used to write it like this. So here's a little tip that you can do, which is similar like this route groups. But what's different is that it completely, it tells the router to completely forget about this, to never put this in the router. You can do that as well by adding an underscore like this. And you might get this like weird one unsaved file inside of your types If that happens for you just click on it save it and close it That it And let's go ahead and check it out now. Look at this. Now, localhost 3000 is a 404 page. That's perfect. So that's how you can omit the folder from the router if you, for any reason, inside of the app folder, want to have a file called page, but don't want it to be a part of the routing system. You can do that as well. And you're gonna see me use this convention underscore components whenever I'm creating some components which will only be used once, so not reusable. I'm gonna keep my reusable components here where the chat scene component buttons are. Great. So if you're having any trouble with this .next folder, which might happen, here's another little tip that you can do. So inside of your terminal, just shut down the app, go ahead and remove the dot next folder and just do npm run dev again. There we go. You don't have to worry about the next folder because that is just cache and it will regenerate every time you do npm run dev. Great. So I hope you kind of understand how routing works and some tips and tricks here. And I just want to go ahead and show you the difference between a client and a server component to wrap this up. So we can remove the underscore components folder and we can remove the out folder. So just like we started a globals file, a layout file, and a page file. So right now, if you create any file, be that an error or page or layout or components inside of the app folder, by default, that's going to be a server component. What that means is that if you go ahead and do a console log here, and I'm going to say, where am I logged? like that, you might be expecting to see that log inside of your inspect element. But no matter how many times I refresh, it's never logged here. Instead, you can find it inside of your terminal. That's because by default, this is a server component. There we go. Where am I logged? So if you want to turn it into a client component, what we have to do is mark it as use client at the top like that. Let's check it out now. So now if I go inside of my inspect element, there we go. We successfully converted it to a client component. So when is one going to be more useful than the other? So client components are your normal React components. So inside of these components, you can add a use effect, for example. So I'm going to add a little use effect here with an empty dependency array. And I'm going to console.log mounted like this. And if I go ahead and log this, there we go. It says mounted right here. But what happens if I remove the use client, which will turn it back into a server component? You will see that I'm going to get an error and it's very clear what it says. You're importing a component that needs use effect and that only works in client components, but none of its parents are marked by use clients. So they are server components by default. And you can, of course, click here on this link to learn even more about that. So you just figured out that client components are useful for interactivity, right? For use effects, on clicks, all of those things. But server components cannot do that kind of stuff. What they are good at is being asynchronous. So you can turn any server component into an asynchronous function. And then what you can do when we connect with the database, you can do something like const users to be await. And then imagine we have a database dot, well, something like this, right? You can actually call the database inside of the server component. And then you would probably like return it to a client component like this. Data, user, something like that. And then client component would be all nice and interactive. So you can imagine server components like an API route, like a get route, something like that. Great. I hope that kind of cleared it up. So just go ahead and turn this back into a server component. So this is the only thing you should have inside. In my app folder, I reverted everything to the original. So I only have page.vsx right here. Great. So you figured out how routing works inside of Next.js. If this is your first time doing this, I would suggest you kind of practice on your own a little bit before continuing forward. Confirm that you know how this works. And what we're going to do in the next module is we're going to go ahead and connect to our database and create our Prisma schema. And then we're going to start incorporating NextOut version 5. Great, great job. All right. So if you sped up the last part or if you skipped the last chapter completely, I just want to show you my app structure. So I didn't add any new files. I did add them during the explanation, but then we removed those files. The only thing I believe is different. inside of our page.vsx, I added the asynchronous tag here. So if I remove it, it should be exactly the same as your file because I was explaining something about server components here. So I mentioned by the end of the last chapter that now we're gonna connect to the database, but that's actually not what I wanna do immediately. First, I wanna do something else. So let's go ahead and let's go inside of our ad folder globals.css and let's go ahead and add html, body, column, root and let's go ahead and add height 100% like this. So all of our pages are at its full height. Once we've done that let's go ahead and let's create our landing page which is going to have our first auth component called the login button, which is then going to open or redirect to the auth form. So let's go ahead inside of the app folder, inside of page.csx right here. And let's go ahead and remove this and let's add a main element here. Let's give it a class name of flex min height screen. Actually, I believe we can just do age full here. Let's give it flex call like that Items center and justify center So everything inside of this main element is going to be centered right here in the middle And let me just turn off my copilot here So it doesn't confuse us All right And here's what I want to do next I want to add a specific background color to this main element So if you want to, you can do BG Sky 500 for example And that's going to give you this nice background color but I wanna use a specific gradient that I really like. So if you want to, you can ride along with me. So you can see how Tailwind can do some very specific gradients if that's what you wanna learn. So I'm gonna write BG dash, I'm gonna open square brackets. Again, this is completely optional, right? You don't have to do this. You can just use the BG Sky 500. So I'm gonna write BG and inside of the square brackets, I'm gonna write radial dash gradient I'm going to open parenthesis. I'm going to write ellipse underscore at underscore top. Then I'm going to add a comma and I'm going to write underscore var. I'm going to open parenthesis again. I'm going to write dash dash tv tw sorry dash gradient dash stops like that. And then outside of this square brackets, I'm going to write from sky 400 to blue 800. Like that. And there we go. So this is the gradient that I like. Of course, this is just for fun. You don't have to do it like this. You can simply write bg sky 500 or red, whatever you prefer. Now inside of this, let's create a div with a class name of space y6. like that. So whichever elements are inside are going to be equally spaced by a value of six, which you can see the full calculation here. Great. And now inside, I want to add an H1 element, and I want to write out inside. And I'm going to give it a class name of text 6XL, font semi-bold, text white, and drop shadow medium, like that. And there we go. We have a nice out text here in the middle. And if you want to, you can add a little emoji here. So I'm on a MacBook. So I have this little shortcut, which can open my emoji tab here. And I can add a little key lock icon right here. You don't have to do that, right? Great. And below that, I'm going to add a paragraph and I'm going to write a simple authentication service. and let's give this paragraph a class name of text white and text large like that. And there we go. We have our nice little landing page here. If you want to, you can add a text center inside of here. So all of the text is centered nice and neat like this. Great. So what I want to do now is give this out a special font called Poppins. So I want to show you how to add a custom font. You can do that by importing the font you want. For example, pop-ins from next font Google. So it uses Google fonts. And then you can do const font to be pop-ins, subsets, Latin. And let's give it a single weight of 600 because we know we are using the semi bold option. And let's go ahead and let's import the util cn from add slash lib utils. so now we can combine this existing class name with our font so let's wrap the entire class name of the h1 element inside of curly brackets like this so make sure this class name is inside of curly brackets and then add a cn wrapper around it so like this wrap the entire thing in cn and I'm going to use this existing class name as the first argument and the second argument is going to be this constant font, and then we're going to extract the class name from that font, like that. And that should give our out just a tiny bit prettier font. Great. So what I want to do next is go below this paragraph, and I want to go ahead and create a div, and I want to render a button component. We should already have the button imported from here, so make sure you have that, and let's write sign in. Let's give it a variant of secondary. Let's give it a class name wful. Actually, we don't have to give it. Let's go ahead and simply give it a size of large, like that. There we go. So right now, when you click on it, nothing will happen. So what I wanna do is,\nI want to create our first out component called login button. So let me go and close everything here. And let's go inside of the components. And not inside of the UI folder, but just inside of the components, create a new folder called out like this. So you should have the UI folder and the out folder. And inside of it, create a new file, login button.tsx, like that. Let's go ahead and mark this as use client because it's going to have some interactive elements. Let's go ahead and create an interface. Login button props. It's going to accept children, which can be a type of react.react node. It's going to accept an optional prop called mode, which can either be a model or a redirect. And let's also give it a third optional prop. Actually, the second optional prop, but third overall. And that's going to be a Boolean like that. So let's go ahead and write export const login button here. and let's extract this props. So login button props. Let's get the children, the mode and the as child like this. And what I want to do is set the default mode to be a redirect. If the user doesn't pass anything, it's going to be redirect like that. And then what we're going to do is we're going to simply return a span with children inside. And let's give this span a class name of cursor dash pointer. like that and now let's create an on click function so const on click is going to be console log login button clicks and let's give this an on click and pass in the on click function like this there we go we have our first login component sorry our first out component called login button and now I want to go back inside of my app folder page and I want to wrap this button and turn it into a component that in the future is gonna serve as the login button. Great, so it doesn't have to be a button, it can be whatever we want. So let's try this out now. If we've done this correctly, once I click on this in my inspect element here, I should see login button clicked and I have. Perfect. So now we have a little util so that we can turn any element we want, be that a button or something else, inside a login component, right? But obviously, we don't have the actual model mode finished here. So this is what I'm going to do. So inside of the login button where I'm here right now, let's write if mode is model. For now, I'm just going to write a little span here, which is simply going to say to do implement model like this. So if you go back to page and give this a mode of model, it's just going to say to do implement model. Right. Great. So we're going to come back to this later. What I want to do now is actually add this on click functionality. So in order to do that, let's go ahead and let's import use router from next navigation. Make sure you don't import it from next router. So inside of the app folder, we are using next navigation. And let's go ahead and add our router. So console router is use router like this. And instead of this console log, what we're going to do is router.push to slash out slash login. So that's going to be our login route in the future. Great. So let's go ahead and try this out now. Make sure in your page, you don't have the variant model. So just leave it as it is. And when you click, you should be redirected to a 404 page. Great. So we finished our homepage. Very simple screen here. And what we're going to do next is start building the form components. And then when we finish the register form, that's when we're going to add the database so that we can actually send those values from the form to our database and store it. Great, great job. so now let's go ahead and let's actually create this route which is currently a 404 so when we click here we get redirected to a 404 I want to fix that and I want to show an actual box so inside of our app folder we have to create that route and we know the url the url is local close 3000 slash out slash login so exactly what we've written inside of components out login button right here. So we're going to create this very route, go inside of the app folder and create a new folder called out. And then inside another folder called login. And let's go ahead and give it a page.vsx. So just like we practiced in the second module, let's go ahead and do a default export here of our login page. And let's create a div login page like this. And there we go. We should no longer be getting any errors. So when you click from your homepage to here, you should see the login page. Great. Now let's go ahead and let's add a layout to this out folder so that every single route that we have, be that login, register, new password, forgot password, all of those things will share the same layout. So create a layout.vsx file like that. And let's export default out layout. Let's go ahead and extract the children. And the type for the children is react.reactNode, like that. And if you want to, you can collapse them like this. So it's just a bit more readable. Great. And now let's go inside and render the children like this. And nothing should change for now. So what I want to do is give this div a class name with h full. flex items center and justify center and now our text is in the middle of the page and now I want to add it a background color so we can do that you can either simply write bg sky 500 like this or you can copy if you've written like I did in our home page in app page you can just copy this BG gradient and the from sky and the to blue like this. So I'm going to paste that here instead of this. And there we go. Now I have the exact same background color, both on my homepage and here on the login page like that. And we are done with the out layout, nothing more we have to do here. So let's go back inside of our login folder page.vsx right here. So in here, we're going to do a very simple thing. Instead of returning a div, we're going to return a login form component, which right now does not exist. So let's go ahead and let's create it. So we're going to create that inside of components out. So we'll be able to render the login form either in the page or if you remember, inside of our components login button, we have a model to implement. So we're going to be able to reuse the login form either in a separate route or in a model, if that's what we prefer. Great. So go inside of the out folder in the components and create a new file, loginform.tsx, like that. And let's go ahead and export const login form. So we are not exporting default here because this is just a component. It's not a page. And let's go ahead and return login form, like that. and then we can go back. So I'm going to close everything. We can go back instead of the app folder, alph, login, page.csx, and we can import the login form from components, alph, login form, and you should see that rendered right here. What I want to create next is a card. So every single one of our elements, like login, register, new password, verification, all of those things are going to be rendered inside of the identical card model. So we're going to create a file called card wrapper. In order to do that, we have to go inside of our terminal first. You can either shut down the app or open a new terminal like this and write npx chat cn ui at latest add card like that. And that's going to add a card component inside of your project. That's it. You can close this terminal, make sure you have npm run dev running and just refresh your app to make sure everything is synchronized. Great. So now let's go inside of the components out login form. And inside of here, we're going to go ahead and we're going to wrap the entire thing inside of a component called card wrapper, which also does not exist yet. So if you save, you're going to get an error. so let's go inside of the out again inside of components out create a new file card wrapper dot vsx like that and let's go ahead and mark this as use client component and let's create an interface card wrapper props let's give it a children of react dot react node let's give it a header label, which is a required string. Let's give it a back button label, which is going to be a string. Let's give it a back button href, which is also going to be a string. And let's give it a show social, which is an optional Boolean. And now let's export const card wrapper here. And let's go ahead and extract those props. So card wrapper props like that. And now we can the structure, the children, the header label, the back button label, the back button href, and the show social, like this. And now inside of here, what we're going to do is we're going to return and render a card from . . .ui card. You can see how I added this import here. So you can either do it like that. The reason it can import it from here is because we are already in the components folder. So it's this one that we're working with, card wrapper. So it goes back one file back another folder and it goes inside of the uicard or you can do add slash components uicard if you want to be consistent throughout the project both will work just fine and besides the card we're going to need a couple of more elements so we need the card we need the card content we need a card footer and the card header like that so let's go ahead and wrap this inside of a card. Let's go ahead and give this a class name of width 400 pixels and shadow medium like that. And inside render the children like that. Now let's go back into the login form here and you can import that card wrapper from dot slash card wrapper And there we go You should have a little TypeScript error but you should be able to see your login form inside of a card this time Great so let go ahead and give this card wrapper all the props it needs. So the header label is gonna be welcome back. The back button label is gonna be don't have an account, question mark. The back button href is gonna go to slash out slash register and we're gonna have a prop show social. So we show the login buttons using Google and GitHub. Like that. Great. And we no longer have any TypeScript errors and it should still be rendering just fine. Now let's go ahead back inside of this card wrapper component and let's style it a bit more. So what I want to create is a reusable header component. So let's go ahead and do the following. Inside of the out folder in the components, create a new file header.dsx like that. Let's go ahead and import pop-ins from next font Google. Let's go ahead and let's import cn from lib utils. Let's define the font using the pop-ins import. And just as we did in the landing page, we're going to give it a subset of Latin and a weight of 600. Like that. And let's create an interface header props here to accept the label, which is a string. and let's export const header component. Let's go ahead and extract the props. So header props label like this. And inside, very simply, we're going to return a div with a class name of width full flex, flex call gap y4, item center, and justify center. and inside an h1 element which is going to say alf like this and if you want to you can add a little emoji like I am doing right now great let's give this h1 element a class name which is going to be dynamic so let's use the cn library and first let's find the default class names which is going to be the text 3 excel and font semi bold and then let's pass in the font.class name here like that. And below that, we're going to add a paragraph, which is going to render our custom label, which we are going to reuse for different types of login components. Let's give this paragraph a class name of text muted foreground and text small like that. And we are done with our header component. Now we can go back inside of the card wrapper component here, and we're going to use this header label to render our new header component. So instead of rendering the children here. Above that, add a card header, which we already have imported. So just ensure that you have this. And inside of here, we're going to render our reusable header component, which you can import from dot slash header like this. Or if you want to be consistent, you can use components out header like that. And there we go, you should be seeing our nice out text right here. So let's go ahead and give this header a label, which is going to be header label like that. And there we go. We have a nice text which says welcome back. Perfect. So let's go ahead and see what else we have to do. Let's wrap this children inside of card content like that. There we go. You can see how now the text is indented and it's no longer all the way to the side of this border. Perfect. And now what I want to do is I want to create the social components and the back button components. So let's go ahead and do this. If we have a prop show social, go ahead and render the following. We're going to render a card footer, which we also have imported. So you should be using all of this here. And inside of here, we're gonna render our social, which currently is not defined. So let's go ahead and create that. So instead of the out folder, create a new file, social.tsx. Let's go ahead and mark this as use client. and let's export const social. And let's return a div with a class name or flex item center, pull width, and a gap x2 in between the elements which we're going to have inside. And in order to render the Google and GitHub icons, I recommend that you install a package called React icons. So let's go ahead and do npm install React icons like this. Let's wait a second for this to install and refresh your page. You should still be getting this error here. In order to get rid of the error, we can already go back inside of the card wrapper component and import the social from .slash social. If you want to be consistent, you can change it to components out social. The same thing we did with the header because all of these components are in the same level in the components folder out right here. So outside of the app folder. Let's go back inside of the social.vsx and let's render those buttons. So I want to go ahead and I want to import fc Google from react-icons-fc. And I want to import fa GitHub from react-icons-fa. Now inside, let's go ahead and let's render a button component from dot dot slash UI button or components UI button. Like this. And let's go ahead and just simply render the fc Google in here. and now let's give this a size of large let's give it a class name of width pool let's give it a variant of outline and on click for now can just be an empty arrow function like this and let's give this fc google a class name of h5 and width 5 like that and we can copy and paste this button one below another and give this one an FA GitHub like this. There we go. We now have our social logins. Of course, we will add the functionality later, but this is very cool because we will reuse this card wrapper and we have the props to hide or show the social buttons depending on how we need. So let's take a look at this now. There we go. We have beautiful GitHub and Google icons right here. And if you go inside of your login form here, so inside of my components out login form, and if you remove the show social, there we go. They are hidden. If you bring it back, they are shown right here. Great. So let's go ahead. Also, if you want to, you can change this to add slash components out card wrapper. So I just like to be consistent with this import alias. It doesn't really matter. Great. So now let's go ahead back inside the card wrapper component. And what I want to create now is the last component we need, which is the back button component. So let's go ahead and add another card footer here. And inside, we're going to render the back button component like this. And let's give it an href of back button href. And let's give it a label of back button label. So you should have both of these props destructured from here. great and you should be getting of course an error because our back button component does not exist yet so let's go ahead and create it inside of the components out folder back dash button dot csx like that and let's go ahead and let's mark this as use client and let's export const back button and let's return a div which uses the button component like this. And I will just change the import to use components UI button. Now let's quickly create an interface back button props to accept the back, the href, which is a string and the label, which is a string like this. And now we can destructure those props. So back button props, we can get the href and the label like this. Great. Now let's go inside of the button here. And let's go ahead and add a link component from next slash link. So make sure you add this import. And we're going to give this an href of href. And inside, we're going to render the label like that. Well, my apologies. So I just exited the back button. So we were here. So label like this. And let's go ahead and give this button a variant of link. Let's give it a class name of font normal and with full. Let's give it a size of small and let's give it an as child property so we can properly render the link component inside. Like that. Great. Now we can go back and set the card wrapper and we can import the back button component from the slash back button or components out back button. And there we go. We should now have our back button. When we click, it should be a 404 page because we don't have that href yet. Perfect. So we created a reusable component and now we are ready to set up our form. Great, great job. Now that we have our reusable card wrapper, let's go ahead and let's actually implement the fields which make the form. In order to do that, I want to go inside of my terminal here and I want to add a package from ShadCN. So let's go ahead and write npx ShadCN-UI latest add form. This is going to add a couple of elements, but also a couple of packages inside of our app. And let's also, well, let's take a look at everything it added, right? So we can see that it modified our package JSON. So when I click here, there we go, it added hook form resolvers, some Red X UI primitives for the label. It added React hook form and Zod for validation. So we have the form and the label, but we are missing one specific field here. So let's go inside of the terminal here and let's add npx chatcn-ui at latest add input like this. So this one does not add the input by itself. So we have to manually add the input. Great. So once you do that, make sure that you have npm run dev running, refresh your localhost to ensure everything is working. Now let's go ahead and let's go inside of our components out and let's go inside of the login form right here. And the first thing I want to do actually is I want to create a form schema, which I'm going to use inside of this form. And I'm going to keep all of my schemas in one place. So let's go inside of the root of our application and create a new folder called schemas like that. And inside, simply create an index.ts. So from here, we're going to do the validation on the front end, but also on the back end by easily importing it from this common folder here. So let go ahead and import everything as z from Zod which we have installed And let also well no need to import anything else Instead we can export const login schema to be z like that And let simply give it an email which is z and a type of email. And let's give it a password, which is simply going to be z.string. In the register form, we're also going to add a minimum value of six. But for login, I don't recommend you do that because remember your password standards can change, right? So you might have some users which created their account before you updated the minimum length of your password. So it's kind of a good recommendation from me to you that you don't limit the minimum length of the password on login. On register, that's fine for new users, right? But on login, make sure you don't block anyone if they have an old password which you allowed at the time. So just leave it like this. Great. So now we have our login schema. And let's go ahead and let's import inside of this login form, everything we need to create our form. So we're going to need use form from react hook form, we're going to need zod resolver from hook form slash resolvers slash zod like that. And let's also go ahead and import everything we need from s slash components UI form like that. So we are going to need the form itself, the form control. We're going to need the form field, form item, form label, and form message like that. And now let's go ahead and let's define our form here. So I'm going to write const form to be used form like this. And let's go ahead and give it a type. In order to give it a type, we have to import Zod. So let's import everything as Z from Zod. And let's also import our login schema from at slash schemas. So just make sure that you exported this constant like that. And then you can use the combination of those two. So this is going going to be z.infer, open pointy brackets again, type of login schema, like that. And now let's go ahead and give it a resolver to be Zod resolver, which we imported and pass in login schema again. And let's now give it default values. And because we added the types right here, you can see how it will auto complete the email to be empty and the password to be empty like this. Great. So we have our form hook right here. And let's go ahead and let's see if I need to mark this as use client, I believe to get rid of this errors. Yes. So make sure you mark the login form component as use client. You can see how when we don't have that, we have some errors and they're not exactly clear what's going on, right? But I assumed is because the moment you use a hook inside of something, you probably need to add, well, you definitely need to add use client at the top. So just ensure you have that and your page should be, well, working, right? And now we can use this form to actually create our elements. So let's go inside of the card wrapper and let's add the form with the capital F right here. So we imported that from this. Make sure you have all of this imported because we're going to use all of those from components UI form. And what we have to do in this component is spread this entire constant. So let's go ahead and simply spread the form. And that will give it all the props it needs, except the children, which we are going to write now. Inside this form, write a native form element here and give it an on submit to be form.handle submit. My apologies. form.handleSubmit and you can just give it an empty arrow function inside like this. So form, which comes from this constant, handleSubmit and then simply an empty arrow function. We're going to change this with an actual submit function later. And let's also go ahead and give this native form element a class name of space Y6 like that. so this is going to separate the inputs from the login button and now we're going to create a div which is going to hold all the inputs and that one is going to have spacing of four like that great so let's go ahead and let's create our email field so let's use the form field which we already have imported and form field is a self-closing tag like this so make sure you have form field imported and make sure you use it like this. Let's give it a control of form.control. Let's go ahead and give it a name. And you can see how it only gives us these two options because it knows our schema. And then let's use the render field to extract the individual field and immediately return a form item component like that. Inside of it, a form label component, which will say email. And below it, let's add a form control component. And finally, let's add the input, which we forgot to import. So let's go ahead right below here and let's import the input from add slash components UI input like this. Great. And there we go. Now what we have to do is we have to give this input controls which match our form. So we can simply spread the entire field prop and that's it. This is now a controlled component. Let's give it a placeholder of John Doe example.com and let's give it a type of email like this. And there we go. We have our first field right here which says email. Great. And you can see how we have some validation. You can see when I pressed enter here, this one has an error right here, but we are missing the actual message. So you can try and press enter inside of the field when it's empty and you should get a little error here. So what you can do is go outside of form control and add form message. I believe we have this imported as well. Form message. There we go. And you can see how now I have an error. So if you go inside and press enter, you're going to get invalid email here. And if you ever want to change those errors, you can. So for example, if you want to change the one for the invalid email, I believe you can simply go inside of here, get a message and write email is required, for example. Let's see if that one is that. And there we go. I just changed it to email is required. So if you want to, you can always modify. I believe for every additional chain, you have the message object. But if you want to modify the first one, then it doesn't have the message, as you can see, but it does have invalid type error. So in here, you can say must be a string or something like that. If you want to explore Zod more, you can play around with it. I'm mostly going to leave the error messages as they are. Great. So now what we have to do is we have to copy and paste this field and give it to our password, right? So go ahead and copy the entire form field like this. So here's where it starts and find the end of the self-closing tag. And inside of this div with space Y4 spacing, simply below it, copy and paste it. And you should now have two email fields. Let's change the bottom one to have the name of password. Let's give it a label of password. And let's go ahead and change the placeholder to be 1, 2, 3, 4, 5, 6 in the stars number, right? And type is going to be password like that. And there we go. We now have our email and password here. What I want to do next is I want to add a submit button. so let's go ahead outside of this div right here but still inside of the native form and let's add a button component so we need to import that as well I just did it automatically from dot slash UI button or components UI button however you prefer all right make sure you added this button here and I want to go ahead and write login like this and I'm going to give it a type of submit and the class name with pull like this. And there we go. Now this is our submit form and you can see how it has that something is an invalid email until we give it a proper build right here. And yeah, let's go ahead and just add, I told you that you don't add anything here, but just add a minimum value of one at least, right? and you can modify this message to say password is required. So we are not explicitly gonna instruct the user that it needs to be six characters because this is not a registration form, right? They already have a password. We cannot instruct them what their password must be because they could have created this account a long time ago when we allowed three characters or something like that. So I believe now, there we go, it says password is required because if you don't add this message, then it's going to tell you that a minimum you can see the string must contain at least one character so it's kind of a weird error like this so you can give this a password is required and I'm also going to modify the email then message to the email is required like that great so now if I try and submit an empty form there we go email is required and password is required looks much nicer. Great. So how about actually submitting the form? How do we go about doing that? Well, we have to create this empty arrow function, which we started doing right here. So go ahead and simply write const on submit or handle, sorry, on submit. Let's do it like that. And in here, we'll have access to our values. And the values are going to be a type of z.infer open point in brackets type of login schema. And if it comes along the values, and add this on submit function here instead of this empty arrow function here. So form.handle submit and then inside we'll pass in our on submit. So this wrapper will pass in the validated values right here. So let's try it out. So in here I'm going to open my inspect element here. I'm going to give it a name of example mail.com and 123456 for the password and there we go. We have an email and we have a password right here. Great. So there a couple of more components that I want to create before we go into actually submitting this And that the form error and form success components So let go inside of our components overall So this time not inside of the out folder because this isn't exactly tied to out. So just inside of components, add a form error.tsx like this. And let's go ahead and let's import rom at radix UI React icons. So in the beginning of the project, I told you when we set up ShatZN UI to choose the New York style. If you chose the New York style, then this is the package you're going to have by default for icons. Otherwise, if you chose the default style, you can try Lucid icons or Lucid React. I'm not sure what it is. But if you watch my previous videos, you're probably familiar with it. Basically, this is just to get the icon. We also have FA icons. So you can choose any icon you want. From here, I'm going to export exclamation triangle icon, meaning that something is going wrong. Exclamation triangle icon like this. I will create an interface form error props to optionally accept a message. and let's export const form error like this. Let's destructure the props, form error props like this. Let's get the message out. And if there is no message, simply return null. Otherwise, we're going to return a div with a class name of bgdestructive slash 15. padding three, rounded medium, plex, oops, where was I in the form error, plex, items center, gap x2, text small, and text destructive. And then inside, I'm going to render the exclamation triangle icon with a class name of height four and width four. And I'm simply going to render a message inside of a paragraph like this. Great. So make sure you have this little component. And now we can go back inside of the login form. And this is where I'm going to keep them. So I'm going to keep them above the button here. So if I add form error from dot dot slash form error, you can see how it automatically imported it here or components form error, if that's what you prefer, like I do. You can see by default, nothing is shown. But if I pass in a message to be something went wrong, this is where that's gonna pop up, right? Or for example, email taken, something like that, or invalid credentials. So this is where we're gonna keep those errors. And I prefer it this way rather than a toast notification, especially when it comes to, you know, notifying our user that we've sent them a confirmation email or something like that. I want that to stay here. We can use toast notifications later for different stuff. But for this one, I want it to be like this. And besides form error, I also want to have form success component. And luckily for us, it's almost exactly the same. So just copy and paste this here and rename this to form success. And we're just going to tweak the colors and the icon a little bit. So rename this instances to form success. And let's go ahead and change the icon to be check circled icon. Or, you know, if you're using React icons, just import whatever you like from here. Use an emoji. It really doesn't matter. and now let's just change the colors to not be bg destructive but instead emerald emerald 500 slash 15 and text is going to be bg emerald 500 as well so just don't misspell emerald you can hover over a class name if you have the tailwind extension to confirm that it exists great and now we should have the form success component so i'm gonna copy and paste this and call this form success from dot dot slash form success. I'm just going to change this import to use components because I prefer it this way. And instead of invalid credentials, we're going to say something like email sent or something like that. And there we go. You can see how this is going to look like. Most of the time, only one of those will be available. So either the success message or the error message like this. And just to clear this up, let's just go ahead and simply remove this to be an empty string like this. Great. So I'm going to wrap it up for now. What we're going to do next is we're going to create our first server action and get a little bit familiar with how we are going to send the data to the server. You of course don't have to use server actions for this tutorial. It's just something that I prefer. If you prefer API routes, that's perfectly fine. This project does not depend on server actions at all. So you don't have to worry about that. Great, great job. So what I want to do now is find a way to transfer these values from our client component to the server. For that, I'm going to be using server actions. That being said, if you do not prefer them, you don't have to use them. If your project uses API routes, this authentication service and everything we learned today will still work just fine. It's just a way to pass something from the client to the server. and my choice for that will be server actions which are built-in RPCs in Next.js 14 and I'm just going to show you exactly how simple they are. So let's go ahead and do the following. I'm going to hold all of my actions inside a new folder in the root of my application. So let's go ahead and create a new folder called actions like this. Now inside I'm going to create an action called login.ts and the first thing we have to do when building any action is mark it as use server. This way, our server code will never be bundled with the client code. So this is now as equivalent of an API route. So let's go ahead and export const login. Let's accept the values for now. Let's give them a type of any and let's simply do console log values like this. That's it. This is a server action, a completely valid server action. What you can do now is go back inside of your components out login form. And inside of here, what you can do is call the login from actions login. So make sure that you add this import. Where is it? There we go. Import login from actions login. Make sure that this is used client like that and simply passing the values. And that's it. This is a server action. Can you guess where this will be logged? On the server. That's right. So So prepare your terminal right here like that. And let's go ahead and enter some values. So fake email and one, two, three, four, five, six. And there we go. We have successfully passed our values to the server. Server actions can be as simple as that. I know there's a lot of examples going on about how complex they are with all the new hooks, use form status, use form state. We're not going to be doing that. That is for progressive enhancement. and while that definitely has its pros, I feel like that's a whole module that we have to go over before we can even touch out. So I'm not gonna be doing progressive enhancement in this tutorial. I'm simply gonna be using them as an extremely simple way to pass something from the client to the server, right? So that's what we're gonna be doing today. That being said, if you don't like server actions, you could have just as easily done, you know, axios.post, your API route, and simply passing the values and then dot then and dot catch, right? It doesn't really matter, but I am going to be using the server actions in this tutorial. Great. So how do we get the pending state from this server action, right? Well, there are a couple of ways we can do it. We can manually set pending, you know, let's imagine that we have a use state here and then you know dot finally here we would call it and change it to set pending false but there is an easier way which is by using the built-in use transition from a react so add use transition from react here and let's add it here to the top and we're gonna expand extract is pending and start the transition from use transition like this And then what you can do is go inside of your onSubmit function and simply wrap this login inside of a start transition. Like this. And then what you can do is go ahead and use this is pending to disable all the states you need. For example, I want to disable this input while it's pending. Like this. I also want to disable the password input while it is pending. And I'm going to do the same thing for the button. So disabled is pending. And I believe that now it will only be a very quick second. But you should see a blink happen, right? That is the disabled field. So why do it this way? Why use start transition? Well, you don't have to use start transition, but it will be very useful. if you ever do any of the next cache or revalidation or redirects here. So if you happen to do revalidate path or revalidate tag, right? Which are next.js cache functions. Usually you couldn't exactly cache the end of them by using .then or something like that. But start transition can do that. So start transition can tell you exactly when something like this has ended. So that's why I prefer using it like that. And it works just as fine if you don't use it, if you don't use those inside. So this is going to be the way I'm going to be passing things from the client to the server. Again, you know, you're not required to do this if you prefer API routes, sure thing. And what I want to do now is kind of establish how we're going to validate the fields on the server. So you can imagine this code as exactly what you would do in your API route. Again, if that's what you're preferring. So first things first, our values are not a type of any. They are a z.infer, and in order to do that, we need to import everything as z from zod. And we don't need this, and we need a login schema from schemas like that. So z.infer type of login schema.\nlike this and there we go now our login form has no errors because this is exactly what our server action is expecting right here and what i want to do now is actually validate these fields because remember client-side validation can always be bypassed it's very easy to do that so what we're going to do here is add const validated fields to be login schema dot save parse and again validated the values, but this time on the backend where no one can manipulate it. And then we're going to write if validated fields dot success, sorry, if not, so make sure you put this exclamation point here. So if we didn't get back a success field from this, in that case, I'm going to go ahead and return an error invalid fields like this. So if you were doing this in an API route, this would probably be something like return response. I'm not exactly sure. Is it a JSON or something like that? Basically, you would return back with this kind of object. And let's go ahead and give it a default of success to be email sent, something like that. So just mocking these things for now. Great. So now we know that our login schema here is validating our fields. So let's go ahead and do the following. What I want to do is find a way to use these errors and the success messages and display them on the form. And we can do that thanks to these fields, which we've set up form error and form success. And I'm really not going to complicate this. So I'm just going to add two state fields, one for error and one for success. So let's go ahead and pass in const error, set error to be state from React with the default value of an empty string. So let's go ahead and see where did I import this. There we go, use state and use transition from React. I'm going to copy and paste this and the lower one is going to be success and set success like this. And then inside of our on submit, on then, I'm going to get the data. and I'm simply going to set error to be data.error and set success is very simply going to be data.success. Like that. And let's just see if I'm doing anything wrong here. So login, yeah, is also supposed to be an asynchronous function like this. and does that fix it data oh yeah so sometimes the error can exist and sometimes it doesn't right so here's what they did so make sure that the login function is an asynchronous function I mean the server action make sure this is an asynchronous function and in here in order to fix this type script error we have to be a bit more specific with the type of our error and success, which can be a string or undefined. Like that. And we should no longer be having any TypeScript errors. And every time we hit a new submit, let's go ahead and clear all errors. And let's clear all success messages like this. Great. And I believe that we should already be seeing something. And what we have to do is now just pass the full error message to use error. and success message to use success like that. So let's for now, let's manually just throw an error first because I believe our validation will pass. So let's try it with an error first. So just return an error object. So if I try anything, mail and any password, there we go. I get an error in valid fields. Perfect. And now let's go ahead and enable this back. So make sure you bring this back. we are only going to throw an error if our backend result validation fails. Otherwise, we're going to throw this success message. So let's try that now. And there we go. Now it says email sent. Perfect. So this is our first server action. You can see how simple it is to use. And you can see how easy it is to control the errors, the success messages, and get the pending state using start the transition. And this is completely safe. This is just as you would write your own API thanks to this use server clause. So this code is never bundled with the client so you don't have to worry about any secrets being spilled here. Great. And what I want to do now is, well, create a register form. And the reason I want to do it because we can't log in into anything, right? There's nothing here I can write anymore except just throwing these errors. So let's go ahead and create our register form. so the first thing we have to do is we have to create this currently 404 route so when you click on don't have an account it should lead you to slash out slash register so let's go ahead and resolve this so it's not a 404 page so go inside of app out and you can just copy and paste the login and rename it to register like this and that should resolve this i believe if i refresh there we go now both my login right both out register and out login will show this login component but don't worry we're going to go ahead and resolve that now so go inside of register go inside of page let's go ahead and rename this to not be login page but instead instead register page like this what i want to do now is i want to go inside of my schemas so let's get inside of schemas index.cs let's copy and paste this schema here and let's create a register schema so we're going to have an email we're going to have a password but the minimum value for the password is going to be 6 because this is a register form so we can tell the new users the new instructions and instead of password is required here I'm going to write minimum minimum six characters required. And besides email and password, we're also going to have a name, which is going to be a string with a minimum value of one. And let's give it a message of name is required. Like this. There we go. So make sure you have the email, the password and the name. And now what I want to do is I want to create a register form. So in order to create a register form, we don't have to do much because we have most of our reusable components. So that's going to be inside of components, out and inside of here. So let's start by copying the login form and pasting it inside. Inside of the out folder, right? And let's simply rename it to register form.tsx like that and go inside of the new register form. So make sure that you are inside of the register form in your tab here and simply rename this to not be a login form, but instead a register form. And we are no longer going to be using the login schema. So find the import for the login schema and remove it and instead import register schema. Doing it like this will help you see all the errors you have so you know where to change it. So I know I need to change it here to use the register schema. I know I need to change it here and here in the on submit as well. And I already know that inside of my default values, I'm gonna have one more, which is a name. So I can simply add a name here as well like that. And let's go ahead and modify this card wrapper. So instead of welcome back, it's gonna be create an account. The back button label is going to be already have an account with a question mark and it's gonna lead it to out login. and I'm going to keep this show social as well because they're going to work equally on login and register components. Great. And there's honestly just one more thing we have to do here. It's copy an existing form field like an email and paste it and change this one to have a name of name and the form label of name. And the type doesn't matter and the placeholder is going to be John Doe. like that. And now what we have to do is use this new register form. So go inside of your add folder, go inside of out register page and remove this import. And instead add a register form from components out register form and let's use it here. And there we go. Now we have a login form on out register. We have a back button that leads us to the login form and this one leads us to the register. And we obviously have to modify a few more stuff. You can see here I have the button login. So let's change that. So go inside of the register form inside of components out. Let's find the login button. There it is. And let's change it to create an account or register, whatever you want. There we go. Now when I click here, I should be getting this error. So minimum six characters required, email is required, and name is required. Perfect. So I'm going to go ahead and just prepare an equivalent server action for this as well. So it's not going to be using the login action. Instead, it's going to be using the register action. So inside of actions, well, you can just copy and paste this one, right? And let's rename it register like this. Go ahead and remove the, rename the expert const to be register as well and remove the login schema and import the register schema so now you know where to change them and there you go it can stay exactly the same now go back inside of your components out register form here and go ahead and find where you import the login action and change it to be the register action from actions register and now you're gonna have this little error in the on submit. And there we go. The types should be completely fine. So if I try this now, Antonio, test and a password. There we go. I have a success message that email was sent. Perfect. So you've just learned the basics of server actions and you just saw how fast we created our register form. So that's how handy it is. This little card wrapper, which we created so we can easily change the label, the back button and where it leads. And of course, well, it's easy to copy and paste this form of fields as we need them as well. Perfect. So what we're going to do next is we're actually going to be using this actions in the register to create a user inside of our database and encrypt their password. In order to do that, of course, we're going to have to connect to the database and we start creating some Prisma models Great great job So now it time for us to transfer these values which we've successfully put on the server side now, and add them to our database. In order to do that, we first have to set up Prisma, which will be our ORM for this video. So let's go inside of Prisma and we can shut down the app. Sorry, let's go inside of the terminal and we can shut down the app. And first, let's install Prisma. So we have to install this inside of dev dependencies. So npm install dash capital D Prisma, like this. After Prisma has installed, go ahead and install npm install at Prisma slash client, like this. And after this has installed, we're going to go ahead and we're going to create our Prisma util. So let's go ahead and do the following. So make sure that you have both of this installed, the Prisma client in your package JSON and Prisma in your dev dependencies here. Now let's go inside of the lib folder and create a new file called db.ts, like this. And inside of here, let's go ahead and import Prisma client from Prisma slash client. Let's go ahead and declare. Actually, let's not do this immediately because I want to show you why we are doing this. So first, this is what we're going to do. expert constant database to be global this dot prisma or new prisma client like this and you can see that we have this little error here we're going to fix that with what i started writing here but i decided we're going to do it later so i can explain why we're doing that and now let's write an if clause if process dot environment node environment is not production in that case let's go ahead and assign global this dot prisma to be the database variable like this in one line so we have to add types for prisma in global this now so let's go ahead and do that we can fix that by adding declare global var prisma to be a type of prisma client or undefined like this and you can see that now we have no errors in global this dot prisma and you can see how we have the type for it. So why are we doing this? Well, it's because of Next.js hot reload. So as you can see, we could have easily just done this. So imagine I didn't write any of this and just wrote export const db to be new Prisma client. This is what's going to happen in production. But in development mode, we need a different thing. We need this to be working. And the reason is because of Next.js hot reload. Whenever we save a file, Next.js will run a hot reload. And what that would do is initialize a new Prisma client every time. And then you would get some warnings in your terminal that you have too many active Prisma clients. So what we do is we add a if clause. If we are not in production, in that case, we're going to store the database variable inside of global this.prisma. And then when hot reload fires on the next iteration, it will check if it has Prisma already initialized in global this, and then it's going to use that. Otherwise, if we are running it for the first time, it's going to just initialize a single Prisma client. The reason global this, the reason we store it in global this is because global is not affected by hot reload. Like that. Great. So what I want to do now is the following. I want to go ahead and I want to go inside of gitignore right here and go ahead and find dot environment dot local. And below that, just add dot environment or just add it anywhere in this file. So just add pure dot environment file here like that. Great. And what I'm going to do next is I'm going to go back inside of my terminal and I'm going to run npx Prisma in it like this. There we go. And you should get this success message kind of. And what that's going to do is it's going to create the Prisma file. And also inside of your dot environment file, you can see that it's going to fill it with a fake database URL. So you know, this is where we have to put our database URL. And we also have the Prisma folder where we have a schema Prisma setup for us like that. So make sure you have all of those files. What we have to do next is we have to obtain an actual database URL. For that, we're going to be using neon.tech, which is a completely free Postgres database. And you don't need a credit card for it. It's completely free, no need to do anything here. So this is what we're going to do. Let's go inside of neon.tech like this and go ahead and find the login button. Once you're logged in, you're going to be prompted with a similar query like this to name your project. So let's call this alphmasterclass or I'm going to call it alphutorial like that. And database name can be exactly the same. It doesn't really matter. You can choose the region closest to you and click create a project here. And in here, you're going to get your connection string, or you can go ahead and select Prisma here to see exactly what you have to put inside of schema Prisma and what to put inside of the .environment file. So let's first do the schema.prisma. So I'm going to go ahead and copy this, and I'm going to go inside of my Prisma, schema.prisma, select everything and paste it here. And there we go. So we just need a data source DB with PostgreSQL and database URL and a direct URL. And now we have to add this inside of our environment file. So you can click on the little eye icon. So it's going to show these keys or you can directly click copy here like that. Then go inside of your dot environment file and replace this database URL, which what you've just copied. And there we go. We now have a database URL and a direct URL connecting to neon.tech using Postgres SQL or Postgres, however you want to call it. Great. So now that we have this, you can go ahead and close this. And let's go ahead and add a model here. So I'm going to write model user, for example, and give it an ID. And inside of here, let's make it a type of string. Let's make it ID and a default value of CUID, like that. And I'm also going to give it the name of string, for example. And then what you can do is go inside of the terminal here. So just make sure you save this model user inside of schema.prisma. And then you can run npxprisma generate. So what this is going to do, oh, you don't have any generators defined, so nothing will be generated. My apologies. I think that I forgot a generator. Yes, because I've just copied this for Prisma. Right. So do this. Add a generator client provider to be Prisma client JS like this. So we also need this inside of our schema Prisma. My apologies, I removed that once I pasted from Neon this code. Let's try this again. So make sure you have the generator client here. And let's try again, npxprisma generate. And there we go. So now this user model has been successfully generated. And what we can do now is using this lib folder database, we can access the user model. For example, if I go inside of app layout here, so just for fun I'm gonna add a user to be 08 I'm gonna turn this into an asynchronous function I'm going to import the database from add slash lib database and then you can see the auto completion of user here so if you're getting an error here that means that you did you didn't do npx prisma generate successfully so make sure that you don't get any errors right here after you run npx prisma generate so that's what that command does so make sure you don't have any errors and that you have the autocomplete for find many or anything like that. So I'm going to revert this back to how it was and I'm going to remove this import from the database in my layout. So just as it was before. What we have to do now is we have to push our collections right here in the Neon database because if you click on the databases here, well basically you can't find anything, right? So let's go ahead and do this now. Let's go inside of our terminal again and just like we've run npxprisma generate, we are now going to run npxprisma database push like this. Perhaps this already does npxprisma generate for us. I'm not exactly sure. But what it should definitely do is synchronize your Prisma schema. If you get an error here that it could not reach this database, that can happen sometimes. So just go ahead and run it again. And if you ever see an error that your database could not be reached, just try whatever command you're doing or just restart your project again. All right. And I think that now we should be able to find this schema. So I'm going to refresh here and try and find this. I think you can click on tables and there we go. We have a user table. Of course, it's completely empty, but we have the actual collection with the ID and the name inside of it. Great. So we've successfully connected to PostgreSQL or Postgres using Prisma. Great. So what we have to do now is we have to create a proper user model inside of our schema Prisma. And we can find the exact model we need from NextAuth themselves. So our next step is to go to the NextAuth documentation page. Now, here's what you have to keep in mind. There are two out, well, two next out documentation pages. One is called out.js, which I will put the link in the description for, which is the correct documentation. And there is an old documentation for next out.js. And you can see that it's an old documentation, because here in the navbar, you're going to have a little banner, which says that we're looking at next out version four, for the new documentation, go to out.js.dev. So don't be on the one which says next out, beyond the one which says out JS. And you will also have a banner here, but it's just gonna give you, well, you're basically on the correct page, right? So what I wanna do here is I wanna kind of explore this and I wanna find my Prisma database adapter. And in there, I can find my schema. So let's see if that is perhaps in the getting started here. And I'm gonna click, let me zoom in. So getting started, I'm gonna click on database providers, adapters, sorry. and in here we have a lot of adapters and here is Prisma. So go ahead and select Prisma and here's what we have to add. We have to add this Prisma adapter. So we already have Prisma client, we already have Prisma. So let's go ahead and add the out Prisma adapter because we're going to need it. Not immediately, but later. So let's just already ensure that we have it. So go ahead and run, whoops, npm install at alph slash prisma adapter like this so just make sure you have this installed because we gonna need it later when we set up next alph properly there we go npm install alph prisma adapter and confirm in your package json that you have in your dependencies alph Prisma adapter like that Great So let see what else we have to do So of course we have the actual instructions for the setup, but I'm going to skip this for now. And I'm going to go immediately and find this create the schema, create the Prisma schema from scratch. So make sure you are on this page right here, or you can pause the video and copy from my screen if that's easier. So the first thing I want to do is I want to find the user model and I want to copy it and paste it inside of my Prisma schema. So we're not going to be using the exact model that they are. We're going to modify it a little bit, but this is a good starting point. So let's replace this user here that we have with whatever they provided us in this documentation. So you should have the ID, the name, which is optional, email, which is optional and unique, email verified, which is an optional date time image, which is an optional string and two relations, one for the account and one for the session. And the one thing you can immediately remove is the sessions. We're not going to use that. We're going to be using, we're not going to use the database session for this one. So now you want to go ahead and find the model account inside of this documentation and copy the model account like this. And what I want to do is paste it below the model user like that. And there we go. Now this error should go away. And let's take a look at this model account. So you should have the ID, the user ID, which works as a relation with the user. That's why we no longer have these errors here. You should have the type, the provider, the provider account ID, refresh token, access token, expire ZAT, token type, scope, ID token, and session state, and a unique rule for the combination of provider and provider account ID. And you can go ahead and save this file. And here's the thing. If you take a look further inside of this, you can see that we also have the session. You can see that we also have the verification token, but we're not going to be using those models. We're going to implement our own verification token because this can be kind of misleading because you might think that this can be used for credentials provider, but it's actually intended to be used with the email provider, which is the direct or magic login link, which I'm pretty sure you've probably seen somewhere. but honestly it's very easy to implement and I don't see it being used that much across the internet perhaps I could be wrong and I just don't think it makes such an interesting tutorial to teach you how to do that instead I'm teaching you how to do it with credential providers so you can actually register with your name email and password and then we're going to create our own model verification token which is going to confirm that user's email when they register Great. So we've set up that. And now what we have to do is we have to push that inside of our database. So that's what we should have next. So make sure that your model account exists and it's exactly the same as mine is. And make sure that your user exists and it only has a relation with the accounts like this. And then go back inside of your terminal here and run nbxprisma generate like this. so that's going to add it to your node modules and to your database util and then npx prisma database push so this is going to push it to neon.tech so let's see if this succeeds or not i believe everything should be just fine and yeah it's telling us that we have a unique constraint on the email and it's because we already pushed that old user so if you get this warning you can just ignore them by pressing the y button like this so just ignore it it doesn't matter if it resets the database because we don't have any records after all there we go and you should get the message your database is now in sync with your prisma schema like that and let's try it out now so i'm going to refresh this tables here on neon and there we go and i have my account right here and i have my user right here great so we are now finally ready to create our register form but I believe something here is missing. You probably noticed that we don't have a password field. That's because NextOut by default does not exactly recommend using the credentials provider but it has full support for credentials provider if that's something that you want. They have their own reasons why they don't do credential providers. I personally don't like them too much as well but I do understand that it's something that each developer needs to know how to implement. So that's why I'm doing this tutorial. So in the user model, let's go ahead and after the image let's add a password which is going to be an optional string as well so why optional well it's going to be optional because if we use OAuth providers like google and github well in that case we don't have a password right so we need to allow the adapter which we will later connect to create this user model without requiring a password so every time you add something new to your schema Prisma, like we just did with the password field, you have to go back inside of your terminal here and do npxprisma generate. And after that, you have to run npxprisma database, not psuch, but push like this, npxprisma database push. And that should add the password field to your tables collection. So if I refresh here one more time. Go inside of the user, expand it. There we go. We have password field, which is a type of text. Great. So now we are actually ready to revisit our actions right here, register. And in here, we're going to take the values which user provided and actually fill that user model with our first record. Great, great job. So in order to save the user inside of our database, we have to find a way to encrypt the password. For that, I'm going to be using a package called bcrypt. So let's go inside of our terminal here and let's do npm install bcrypt like this. And by default, it doesn't come with types. So we have to additionally install its types. So let's write npm install dash d for dev dependencies at types slash bcrypt like this. So now you should be able to import this in your project. And let's just check it out. So inside of your package JSON, in the dependencies, you should have bcrypt installed. And inside of dev dependencies, you should have types bcrypt like this. So let's go ahead and let's import bcrypt from bcrypt like this. And now we can use this to encrypt our passwords. So let me just make sure that I am running the project. So I'm going to run npm run dev here. And for now, I'm just gonna refresh the local host. Make sure you are on the register page right here. So what we have to do after we confirm that the fields are not invalid, we have to go ahead and extract the validated fields. So you can use validatedfields.data like this. And in here, you can get the exact email password and name, which we have defined in our register schema and which of course the user has to enter. So let's get the email, let's get the password and let's get the name. And the first thing we're going to do is we're going to hash the password. So const hashed password is going to be await bigcrypt.hash password and give it some salt, which is going to be 10 in my case. If you hover over the hash function here, you're going to see different ways of using it. So if you want to, you can generate salt differently, and then you can store that in the database as well. Or you can simply pass in the salt rounds like I'm doing right here in a very simple way. Great. So now that we have the hashed password, what I want to do is I want to confirm that this email is not taken. So let's go ahead and write. Well, first, let's go ahead and import our database. so import database from at slash lib database which is this little util which we created and then in here we're going to write const existing user to be await database dot user find unique where we have an email matching like that and then we can say if we have an existing user that means this email is already taken. So we're going to return an error saying email already in use like this. And if that is not true, we can continue and create our user. So I'm going to write await db.user.create. And I'm going to give it a data of name, email. And for the password field, make sure that you pass hashed password. So this is very important. Do not accidentally use the password like this. Make sure you're using the hashed password. You never want to store plain text password inside of your database. So make sure you're encrypting this first. Great. And what I'm going to simply write here is I'm going to write a to do send verification token email. So we're going to do that later. For now, I'm just going to write a success message user created like this. Great. So let's check this out now. So I'm going to refresh this inside of my Neon database. I don't have any anything inside of my user model here. So let's go ahead and give this a name of Antonio. Let's use emilexample.com and let's go ahead and give it a proper password. And once I click create an account here, I believe we should successfully connect our database and we should get the user created message right here. So inside of my Neon database here, if I refresh, I believe in my user. There we go. I have an ID. I have a name of Antonio. I have an email. I don't have anything for email verified because, well, we didn't verify the email. I don't have an image. And you can see that my password is a hash. So even if someone breaks into our database, there is not much they can do with this string right here. So our passwords are encrypted. Great. Perfect. So now what we have to confirm is that if I try and do this again, so find the email which you've used. So for me is mail at example.com. So let's try this again. New user. Let's use the same email. And let's try creating a new account. And there we go. We say email is already in use. Perfect. So what I want to do now is I want to create a little util for this existing user here by email because we going to be doing that a lot So for that I want to create a new folder in the root of my application And I going to call that data And inside of here go ahead and create a new file user So this is going to be specific user data. And let's go ahead and let's import the database from add slash lib database. And let's export const get user by email to be an asynchronous function, which accepts the email which is a string and then in here let's open a try and catch block inside of the catch here you can just return null and inside of here you can get const user to be await database.user find unique where you have a matching email property like that and simply return the user like this. And let's go ahead and copy and paste this. And I want to create another useful util here, get user by ID. And instead of email being the prop, it's going to be the ID. And we're going to be simply using the matching ID of the user. So very simple. And we're going to use that inside of our auth callbacks later, where we need more information from the database. Great. So now let's use this inside of our register function here. So instead of this, it's going to be await get user by email, and we're going to pass in the email. So let's go ahead and import get user by email using the add data user and the structure get user by email. And here's a quick little tip for you. Some people have reported that bcrypt is causing them errors in the app. So if you want to, you can use an alternative version of bcrypt. I'm going to install it and demonstrate it just in case you're having any issues. So I can install bcrypt.js like this. And just as in the previous one, you also need to install a dev dependency of bcrypt types, sorry, of add types slash bcrypt.js. And I believe they should work exactly the same. So you can import from bcrypt or bcrypt.js. And I think it should be exactly the same. So let's go ahead and try this. If I go in npm run dev here, I'm going to create a new account here just to confirm that everything is still working. So new, new example com. Let's go ahead and create an account. And there we go. User has been created. And if I refresh this, I should have two users inside of my user model here. And there we go. You can see how they have, of course, different encryption, but the encryption is still fully working. Great. So now that we have configured that, what I want to do next is I want to start implementing our actual next out setup. And then we're going to come back to this to send the verification token email. So the reason I don't want to do this immediately, even though it's very simple to do that, is because I want to demonstrate to you how we can first log in and then how we can restrict users which have not verified their email from logging in. Because it's one thing for us to disallow that, for example, in the login function here, right? So we can just simply check if the field from our Prisma schema called email verified doesn't exist. Okay, just break the function and don't allow the user. But keep in mind that this function is just a wrapper around next out. So if somehow the user finds a way to log in, we need to tell NextOut directly, never allow this user to sign in completely, right? Instead, redirect them to verify their password, to verify their email, right? Great, great job so far. So we're finally ready to add NextOut inside of our project. And for now, we're simply going to implement the login functionality the same way we just implemented the register functionality. So in order to enable login, we have to install NextAuth version 5 inside of our project. So head to the Auth.js documentation here. And in the guides section, all the way to the bottom, you're going to find an upgrade guide to version 5. So first of all, let's install the proper version of NextAuth. So if you're watching this into the future, perhaps it will not have this sign and it's just going to be NextAuth. All that matters is that the version is higher than four, meaning minimum five. So let's go ahead inside of our terminal here. Let's shut down the app and let's run npm install next dash out like this. And after you run this, go ahead and run your project again and confirm in your package JSON that you have next out beginning with the version five like this. Great. So now let's go ahead and see what new features we have. So this is the one I like the most, the universal out. Previously, when you wanted to access the currently logged in user in server component, you had to use, well, different kinds of methods, right? We had get server session, we had get session, we had without, get token, use session, all of those things. Now they have all been replaced with a universal out. So I think this is a great change. In here, we also have some breaking changes, like the import being deprecated. So the next, the middleware, JVT, adapters, none of this exists anymore. And now we finally have the configuration. So let's go ahead and do this. So in the root of our application, create a new file called auth.cs, like this. So a very simple name. What I'm going to do is copy this snippet from here, and I'm going to paste it inside, like that. What we have to do now is we have to add the get and the post inside the API for next out. And you can see that in here, we export this universal out lib, which we can then later use in server components to get the currently logged in user session or get no session at all if we are logged out. So let's go ahead and use this get and post to add them to our API routes. So in here we have to create an API folder inside of the app folder, then an out folder, then a catch all next out route, and finally route the thes inside. So let's go ahead and do that. So inside of here, I'm going to go ahead and collapse everything, go inside of the app folder, create a new file, new folder called API, then another folder called out inside, and then another one where you spread next out inside of squared brackets and then route.es. And inside, I'm simply going to copy and paste this snippet from here like this and paste it here. And what we have to do is change the import to go to the root. So at slash out like that, because our out file should be located in the root of our application. Where is it? There we go, out.es. And this is optional and I'm gonna remove it because we are not working on the edge because we are using Prisma, which by default doesn't support the edge. And then what you can do is go to localhost 3000 slash API slash out slash providers. And in here, oh, we have an error, as you can see, missing secret. I believe we can also see that inside of our terminal here. There we go. You can see that the moment I added this out file, it's saying that we have an error, missing secret. So you can see that we have this link here, which we can click on and open it like this. And in here, we have instructions on what we have to do. So we have to add our secret environment variable. So let's go ahead and do that. So I'm going to go inside of my environment file. And I'm going to add an our secret. And in here, you can write whatever you want for development purposes. So you can simply write just a secret. And then if I try and go to this route again, slash API auth providers and refresh. There we go. You can see that it noticed that we have a GitHub provider inside of our auth.ts file, right? But for production, it is recommended that you either generate your own secret using OpenSSL, but that is only available on Unix systems like Linux or Mac. So you can run this in your terminal if you want, or you can simply click here and that's going to generate a random one for you. So this is for production, right? You don't want your alt secrets to be this simple, but in development, it's completely fine. And if I refresh, it's still working. Perfect. So now that we have this done, what I want to do is I want to set up the middleware. And in here, you have a nice little table showing what has changed from version four to version five, if you want, if you're interested in that. So you can see that getServerSession with the out options had been placed with a universal out call. In the middleware, we no longer have without and middleware, we now just have out. UseSession has stayed the same for client components, but you can see that everything else uses the new out universal call. So I really like this change. So now let's set up our middleware. So inside of the root of our application here, create a new file, middleware.vs. And here is an important thing to understand about the middleware. So middleware is not next out specific. Middleware is next.js specific. So please don't misspell the name of this file. Otherwise, it's not going to work. And now I'm going to copy this snippet from here. And I'm going to paste everything inside of the middleware like this. and here's an important thing for you to understand about the middleware so i've seen a lot of misconception about this config matcher right here a lot of people don't understand exactly what it does some people think that this is where you want to put your private routes some people think that this is where you should put your public routes the truth is everything that you put inside of here will not be used to check whether it's public or private it's simply going to be used to invoke the middleware, right? So if I go ahead and add, for example, slash out slash login here, this is not going to be protected or public. This is simply going to invoke this function right here. Or if I write a long regular expression for a bunch of different things, then everything will invoke the middleware, right? So let's try this out. So if I go ahead and add a console log of request next URL dot path name here and I'm gonna give it a little string here so it's clearer to see and let's make sure that our app is running there we go it's running and if I go inside of my out so\nslash login now, there we go. You can see that it says route out slash login. But if I go on register, you can see that I don't have that console log. So the middleware was not invoked. But if I go ahead and add slash out slash register here as well, then in the terminal, when I refresh the register page, there we go. You can see that the route is slash out slash register. So that's how the middleware works, right? So it's not black and white where you have to put your public routes here or where you have to put your private routes here. It is simply a matcher for anything you want to invoke the middleware. And as you can see from the documentation in here, in NextOut, we have a regular expression to invoke on every single thing except this specific regular expressions, which we don't want to invoke. but in my experience this is not the perfect regular expression there is a better one and you've probably used it a lot of times if you followed my previous tutorials and that is the regular expression from clerk so I'm gonna paste the link in the description so you can find this page right here where you can find this matcher which I really like which is perfect for this project the reason I like this matcher so much is because well if you've used clerk then you probably know that they are really the creme de la creme of developer experience and authentication. So what I'm trying to do in this tutorial is come as close as possible to using clerk, but in next out, of course. So let's go ahead and copy this matcher from here. If you cannot find it in the documentation, you can also immediately go to my GitHub and find it there. So I'm going to replace my existing matcher with that matcher. And now let's go ahead and see what's going to be logged inside of our terminal. So I'm going to go ahead and refresh the register page. And as you can see, the register page is logged. If I go to the login page, now the login page is logged. If I go inside of slash, then the slash is locked. If I go inside of slash API alt providers, you can see that API alt providers is locked. So what did we achieve here? We achieved that every single route except specific next static files and next images are going to invoke the middleware. And this is the perfect use case for us. The reason I want it like this is because I rather want to manually, I want to invoke the middleware everywhere. So both on authorized routes, both on routes used to log in, both on private routes and also on public routes. And then in the middleware, I'm going to decide what I want to do with those routes, right? So you can see how we can easily get the is logged in status using request.out. And we can turn that into a Boolean by adding two exclamation points here. So I'm going to add a new console log is logged in to be is logged in like this. So let's go ahead and go inside of our terminal now and let's refresh the landing page. And there we go. You can see that the route is slash and is logged in is false. If I go to out, you can see that the route is slash out slash login and is logged in is false. So I'm going to use the combination of the path name and the logged in status to decide what to do with the route that the currently logged in user is on. And this is another structure that I plan to use for this app. I want my entire app to be protected by default. The reason I want that, because I feel that most applications work like that, right? So you are most likely going to have fewer public routes than private routes. So it doesn't make sense that we have to write every single private route. Instead, let's consider the entire application to be fully protected and in need to be authorized to access it. And then we're going to separate just a couple of routes, like a landing page or the documentation, something like that, to be able to be accessed for non-authorized users. So that's what we achieved with this middleware here. And well, you just saw how easy it is to get some tokens from here. All right. So I hope that kind of cleared up what the middleware is used for. So it's not used for explicitly private routes or public routes. It can be used for anything you want to invoke the middleware. If you wanted to, you can, of course, manually write every single route that we have inside. But it's better to use a regular expression for this, specifically because of this app folder API auth. And in here, we have a catch all, meaning that we don't exactly know which future routes or sub routes could be inside. So by using a regular expression, we made sure that we will invoke the middleware every single time any of those routes is being touched. What I want to do now is I want to go back to the upgrade guide here and I want to scroll a bit down and in here you're going to find something called edge compatibility. So as you know we are using Prisma which by default does not work on the edge which means that we are not going to be able to use a lot of callbacks and events inside of the out.ts files, which we would usually be able to if our database supported the edge. But thankfully, there is a solution for that. And that is to separate out config and then use the config in the middleware. So the middleware is the problematic part because middleware works on the edge. So we cannot use Prisma here. So what we have to do is we have to create a file out.config.cs. So let's go ahead and do that. So I'm going to create on the same level as out, create a new file out.config.cs like this. And let's go ahead and copy and paste this snippet here like this. So we get the provider, we get the type next out config, and we export default an object with the providers, which satisfies the next out config. What we have to do next is go back inside of out.cs file. And now we can safely add the Prisma adapter, which does not work on the edge. So that's why we have to separate those two files. So we're going to use this file instead to trigger the middleware and not this one, which will use the Prisma adapter. So let's go ahead and change what we need. We no longer need the GitHub import and we no longer need the providers. Instead, we're going to import out config from dot slash out config or if you want to be consistent you can use the add sign and let's simply spread the out config like this there we go but we still have to add our prisma adapter so let's go ahead and add the prisma adapter from out prisma adapter make sure that you have this package installed so if you don't you can simply go into the terminal and run npm install out prisma adapter like this but i believe that if i go ahead and install this we already have this i think we did it in the previous module so you can of course always confirm inside of your package dot json uh it should be first out the prisma adapter like this so we have the prisma adapter and we also need our database util from dot slash lib database or at slash lib database So let's go ahead and add the adapter to be Prisma adapter and pass in the database. And we also have to change the session to use a strategy, JVT. We cannot use the database session strategy. That's why I didn't add the session model inside of our Prisma schema. Because with Prisma, we cannot use the database session because it doesn't work on the edge. We have to use JVT strategy here. and now that we modified this we also have to modify our middleware here so it doesn't use this out where we clearly use the non-edge supported prisma adapter so instead we have to use the out config file to extract the out middleware from it and we can do that very simply by following the instructions here so inside of the middleware we have to import out config from dot slash out config and we have to import next out from next out and then we can destructure the out from next out and out config let's go ahead and do that we no longer need this import instead we need the out config from dot slash out config or at out config and we need next out from next out and then what we can do is write const next out and pass in the out config and then inside simply get the out and there we go everything should now work exactly as it worked before so if i go ahead and open my terminal here and if i do let me just close this one so make sure you have npm run dev running. If I refresh, there we go. Route is slash out slash login and is logged in is currently false because we are not logged in. Perfect. So everything seems to still be working. But what we can do now is we can of course, do some callbacks. I'm going to talk about callbacks later when we actually start, you know, implementing them. But callbacks are extremely useful when you want something to trigger specifically on some next out actions, like sign in, authorize, sign out, redirect, things like that, right? But we're gonna explore that a bit later. What I wanna do now is I wanna go ahead and I wanna attempt to use this auth.ts file, specifically this auth constant export to see if I can get the currently logged in session. Obviously, we are not logged in, so I think I already know what that's gonna look like, but let's just try for fun. So inside of the app folder, I'm going to create a route group inside of parentheses called protected. And then inside, I'm going to create a new folder called settings. So that's going to be our protected route in the future. For now, let's just give it a page.vsx here. And let's go ahead and export the settings page. And let's write a div settings page. So you can find this by going to localhost 3000 slash settings directly. No need to go for the protected. I just want to have an organizational folder where I'm going to keep all of my protected routes so I know visually that they should be protected. So now go to localhost 3000 slash settings here and you should just see the text which says settings page like this. So now what I want to do is I want to turn this into an asynchronous server component and I want to import out from at slash out And then what we can do is get the session by using await out. And to render it here, I'm simply gonna use json.stringify inside of curly brackets. So json.stringify and pass in the session like that. And there we go. You can see that the current session is null like this, exactly as it should be. Perfect. So let's go ahead and do the following. If the user is logged out, I don't want them to be able to access this settings route. So here's what I wanna do. I wanna go ahead and I wanna create a new file in the root of my application called routes.cs. So on the same level as out, middleware, completely outside of any folder that we have so far. And inside of here, let's go ahead and do the following. Let's export const public routes. So in here, we're going to write all the routes, which we will allow logged out users to visit. So obviously, that's going to be the landing page for now, like this. If you want to, you can write a little JS document here. Let me just find how we write that. Like this. So you can write an array of routes that are accessible to the public. like that, these routes do not require authentication. And you can give it a type of string. So we're just practicing JS document. And now let's go ahead and do this. Let's export const out routes. So these are the routes which will be used for authentication. So for now, that is slash out slash login and slash out slash register. So let's copy and paste the JS document here. If you want to, you obviously don't have to. So this is an array of routes that are used for authentication. And let's go ahead and write, these routes will redirect login users to slash settings, like this. And let's go ahead now and let's write another one here, export const API out prefix. That's simply gonna be a string slash API slash out. So that's gonna represent this file, which we created here, API out. So inside of our middleware, it's important, we're not gonna add that manually to public routes, but it's gonna be a special case so that we never block this API route. It's important that this is always allowed to logged in or logged out users. They need to be able to access slash API slash out. So we can add a JS document for that as well to explain it briefly. So this is very simply going to be the prefix for API authentication routes like that. And I'm going to write in here routes that start with this prefix are used for API authentication purposes. And the type is just a string without an array like that. and to make our code a bit better, let's also add export const default login redirect. So this is gonna be the place where we're gonna redirect whenever the user is logged in unless specified differently. So let's add a little JS doc for this, the default redirect the path after logging in. And the type for that is a simple string like that. There we go. So now we have defined our first routes here And now what we can do is we can go back inside of our middleware.ps here and let's remove everything inside. And instead, let's go ahead and let's import everything we need from our routes. So import from at slash routes. We need the default login redirect. We need API out prefix. We need out routes. And we need public routes like this. And then in here, in the middleware, I'm going to go ahead and destructure the next URL so it's easier to access. I'm going to check if we are currently logged in by using a Boolean of request.auth. And then I'm going to define if we are on out route. So actually, first let's do is API out route. So const is API out route. So next URL.pathname.startsWithAPIoutPrefix. Like this. Let me try and expand this a bit more. And you can see that because of our JS doc, we can clearly see what this means. The prefix for API authentication routes. These are routes that start with this prefix and they are used for API authentication purposes. So we always want to allow these routes. That's why I want to have them in a special constant. So whenever the middleware hits any of those routes, which are the following routes, for example, slash API slash out providers, there is no reason for us to protect this. Obviously, next out needs it to work properly. Right. So make sure that you don't accidentally protect those routes. So always confirm that your API out prefix is correct right here. Great. Now let's go ahead and check if we are on a public route. So const is public route. that's going to be next that's going to be sorry public routes that includes next url.path name so if the next url.path name is any of the following so you can see the public routes is an array of routes which are accessible to the public these routes do not require authentication so this is how we're going to know if the user is trying to access a route which is completely public and we are always going to allow that. And lastly, let's add const is out route. So that's going to be out routes dot includes next URL dot path name like this. So if it is out route, that is an array of routes which are used for authentication. These routes will redirect logged in users to slash settings like that. So basically, if the user is already logged in and they try to access the login screen, we're not gonna allow that. We're gonna redirect them back to the settings page, but we're gonna do the opposite if the user is logged out. If they are logged out and they try to access the settings page, we're gonna redirect them to the auth login page. So you can see how powerful the middleware can be when you allow it to be invoked on every single route that you can imagine. So now let's write this logic. So first thing we obviously have to allow is if is API out route. We don't even have to check if we are logged in or not in here. Simply return null, meaning do not do any action regarding this. And you can go ahead and try this again by going into localhost 3000 slash API out providers. And this should still work just fine. Now what I want to do is check if we are currently on an out route. So let's go ahead and write if we are on an out route, is out route. And then by default, we're always going to allow people to visit the out route, right? But before we do that, let's check if we are logged in. So if is logged in, in that case, what we're going to do is return response redirect new URL. And in here, we're going to use the default login redirect. So the reason I want to put that in a constant is so you can easily change it later. If you change your mind and don't want to redirect user to the settings, if you want the dashboard to be the main route, you can easily do that here. And we're carefully going to use that in all the correct places. And whenever you use the redirect in the middleware and the new URL constructor, you also have to pass next URL as the last argument here. So inside of the new URL constructor, make sure that you pass the next URL. So it doesn't matter if you manually wrote, you know, slash settings like this, you will still need to pass next URL. And the reason you need to do that is so it creates an absolute URL, because this is not an absolute URL. But when you combine it with a second parameter, which is next URL inside of this, then that will create it into localhost 3000 slash settings, which is what we need. So you can use that. And I'm going to bring this back to use the default login redirect here, like this. So just by adding this, I believe nothing should be changed yet. So if I go back to my homepage here and if I click sign in, there we go. I can still visit the out route. Why can I visit it? Because I am not logged in. So I'm not getting redirected to the settings page because I'm genuinely not logged in and I just want to log in first. And then also the order of this if clauses matters. So make sure that you do it in this exact order. So first allow every single API route and then go ahead and check the out routes. So it matters because while the out routes are technically public routes, remember we did not include them here, right? So we have to check them first before we check the public routes manually. Otherwise you're going to be left in an infinite redirect loop. So when you added this, make sure you've confirmed that you can go to localhost 3000 slash API slash out slash providers. Then when you added this, make sure that you can visit your out page right here. This is how you're going to help yourself if you have any errors. If you can see the exact thing that I'm seeing, everything is going fine. And now let's do the following. If we are not logged in and if we are not on a public route, in that case, we are going to return response redirect new URL slash out slash login and pass in next URL as the second argument. And then outside of any if clauses by default, we're going to allow every other route. So this is how we're going to use our middleware, as you can see. We're going to invoke it on every single client and API route. But on specific routes, which we define inside of our file here, like outloginregister or a slash page, in that case, we're going to go ahead and do some different behavior, right? So you can see that we return null here, which basically means allow this. Don't do anything if this happens. So if the user is not on a public route we just going to fall back to this meaning that there nothing we have to do here right It completely fine But otherwise it going to start checking for logged in. If it's not logged in, and it's not on a public route, we will redirect the user to the login page. Perfect. So let's try this out. Now, here's what should happen. You should see you should be able to see out login normally, you should be able to see out register normally, You should be able to go to slash API slash out providers completely fine. But here is a route that you should not be able to visit. localhost 3000 slash settings. If I go here, I am immediately redirected back to the login page. So make sure that inside of your routes, you didn't write the settings page in the public routes. But here's what happens if I add it to the public routes. So if I add settings in my public routes now, I can go ahead and visit the settings page. There we go. I can now visit the settings page. So I believe this is quite a useful middleware which we've created. Just remove the settings from the public routes. And we have quite easy controls outside of the middleware itself because I feel like it's too cluttered to keep it all together inside of here. I believe it's complex enough to have this. Great. So I really, really like this middleware. And I think we've pretty much mastered the middleware now, right? So I believe you know what it's time for. It's time for us to go back inside of auth.ts right here and to actually add, well, not here, my apologies, the config and to actually remove the GitHub provider for now and instead add the credentials provider, which will finally allow us to call this login function. And once we log in, we will be immediately redirected to the settings page. And once we log out from the settings page, we're going to get redirected back to here. First things first, let's remove the GitHub provider. We don't need it like this. Instead, let's go ahead and let's import the credentials from next slash out slash providers slash credentials like this. And let's move this to the top right here. And now I want to import the login schema from dot slash schemas. So we already use the login schema inside of our login form and inside of our login server action here, right? But I'm going to use it one more time in the credentials provider. Because remember, in next out, just because we have this server action for login doesn't mean that every user will always use that. There are definitely some users which can bypass our server action and not use this login screen at all, right? They can manually send information to the app API out if they want to. So that's why we also have to do the login schema check here in the providers. So in the providers, go ahead and add the credentials. Open an object inside of here and let's write an asynchronous function, authorize, like that. and it's going to give us the credentials in the props. So inside of here, I want to go ahead and validate the fields again. So const validated fields are going to be login schema.saveparse credentials like this. And then if validated fields are a success, in that case, let's go ahead and let's destructure the email and the password from validated fields.data. like that. And then what I want to do is I want to check if that the email which was passed in the credentials provider is actually connected to any, you know, user in my database. So I know that I can, you know, check the password. So const user is going to be await get user by email from data user. I'm going to change this to the at sign and pass in the email. And then I'm going to check if there is no user or if there is a user but there is no user.password. In that case, I'm just going to break this function. So how can it happen that the user has no password? Well, it can happen if they logged in using Google or GitHub. So if they create an account using Google and GitHub and then they come around and try to use the credentials provider, we are not going to allow that because they don't have a password. And in order for credential provider to work, we need to compare the hashed password inside of our database with the password that the user just passed. So if this passes, that means, okay, this user registered using their name, email, and password. So what I have to do next is check if the passwords match like this. So let's write await. And we have to import bcrypt here so let's import bcrypt from bcrypt.js or bcrypt. I found in my development process that bcrypt.js has less errors because I actually had an error when I used bcrypt but you can try it out if you want to but I believe that I also use bcrypt.js inside of my register action so just confirm that you use it in the same place so in the register I use bcrypt.js and in the out config i use bcrypt.js and just confirm that in your package json you have bcrypt installed i mean bcrypt.js installed and also the types for bcrypt.js in dev dependencies all right so now that we have that what we can do is await bcrypt.compare and we're going to compare first the password which the user just entered, and then the hash from our database using user.password, because remember, that is a hash. So this way, we are confirming that they entered the correct password without us knowing what the actual password is, because we were just comparing the hash. We have no idea what is the actual password of the user. Perfect. And if passwords match, return the user like that. and then go outside of this if function here and return null by default. There we go. So what we have to do now is we have to go inside of the out.cs and the same way we can export sign in, we can also export, sorry, the same way we can export out, we can export sign in and sign out. And this can be used in server components or server actions. That cool. Yes. So now let's go ahead and do the following. Let's go inside of our actions and let's go inside of login.ts. So this is the place where we have to call that login function. So first thing I want to do is remove this return here. And instead, let's use the validated fields.data And let's destructure the email and the password like that And then let's go ahead and let's attempt to log in So I'm gonna go ahead and import sign in using at slash out So make sure that you added an export for that here And then I'm gonna wrap that inside of a try and catch block So I'm just gonna to do this and all we have to do in the try block is wait sign in give it the type of sign in which in our case is credentials here and go ahead and give it an email and a password and let's go ahead and write redirect to to be default login redirect from out routes from at slash routes like this So this will be the settings page like this. So later, what we're gonna do is we're gonna have a callback URL or that. So that's why I'm manually defining this here. So we don't have this yet, but later when we implement the callback functionality, we're gonna add that here. So that's why we need this field. Usually, I believe this is still gonna work if you just don't add a redirect to. But if you have a redirect to and the callback URL is null, then it will not redirect so it's just gonna be weird right so that's why I'm adding redirect to default login redirect and it's explicit so I like it it's like telling me all right when you log in this is where you will be redirected to so I don't have to think you know okay I have to think about my middleware now and I have to think okay so once they get logged in this is what will fire and then that will redirect me to here, right? Technically, I just know that because we just wrote it, but in here, it's more explicit. So I like it this way. And then in here, we have to check if error is the instance of out error. Is it out error? Out error. I'm just not sure where we can import this. So I think that we can import out error from next out. There we go. So import out error from next out. So if the error is an instance of out error, we can open a switch case on the error dot type here. And if the case is, let me just see, is it the case? Yeah, without the column. So if the case is, let's see, why is it not auto-completing? There we go. So you can see we have a bunch of different cases that you can cover, but I'm just going to focus on the credentials sign-in here. So if this is the case, I'm going to return an object with an error, invalid credentials, like this. let's give it a default case the return an error of something went wrong like that and what's important when using sign in inside of server actions is that inside of the catch function at the end of this if clause you also throw the error back otherwise it will not redirect you so make sure you add throw error they do this also in the official next js course so i'm not sure if this is a bug or a behavior, but yeah, you have to throw an error. Otherwise, it will not redirect you to the login redirect, which in our case is the settings page. So first, I want to test the invalid credentials one out. So I believe that this is technically already working, but let's just try it out So I going to write a random email here and a random password And now I believe I should get an error invalid credentials There we go It says invalid credentials So if you remember your email and your password you can enter it I believe I have an email, newmail.com or newexample.com. And my password was 123456. And I think, there we go. You can see that this redirects me to the settings page. So it means that I'm officially logged in. And you can see my session here. Because remember, in the settings page, if I go inside of app, protected settings, in here, you can see that we evade out and JSON stringify the session, meaning that I am logged in. And now, if I go and try and go to the landing page, I can still see this. Because this is a public route. But if I click sign in, you can see that I'm redirected to the settings page. That's because of that part inside of our middleware file. So inside of, where is it? Middleware right here. If it is the out route and if we are logged in, we redirect the user back to the settings like that. So we handled so many cases with our middleware. I really, really like how we did this. What we have to do now is we have to add a logout button so that we can actually clear our cache and stuff, right? So inside, there's going to be very simple. So make sure that inside of your out.vs, you added an export for sign out. And then inside of your app folder protected settings page.vsx, go ahead and add a form here and add a little button, which will just say sign out. Give it a type of submit and give this an action to be an arrow function. Make sure it's an asynchronous arrow function. Add a new server here and simply call await sign out from at slash out. As simple as this. So this is how you can use the sign out function in server components. Yes, you can also use that inside of client components, but not by importing from here. So this is exclusively for server components, server actions and stuff and basically all side server things. But don't worry, later I'm going to show you how you can also sign out and sign in using completely client components. So no need for server actions at all. Great. So I believe that if I click sign out here, I should be redirected to the out page. And there we go. And if I try and go manually to slash settings again, I'm redirected back to the settings page. Great. We officially implemented login. Let's go ahead and create a new account here. So I'm going to call this test, test at mail.com. One, two, three, four, five, six. Create an account. Great. Let's go ahead and here. Let's use test at mail.com. One, two, three, four, five, six, seven. That should give me invalid credentials. and if I remove one, that should log me back in. There we go. You can see my exact information here. Perfect. So what I want us to learn next is how to extend this session object with a couple of more stuff, right? Because name and email is not enough. We also need the ID. We also need the role. We need a bunch of more things inside of our session. And thankfully, NextOut has callbacks which can do that. So that's what we're gonna learn in the next module. This was a very long chapter, but I hope it cleared up some doubts that you had in the middleware and how all of that stuff works. So great, great job and see you in the next chapter. So what I want to do now is find a way so that we can extend this session of the currently logged in user. So let's go ahead and just confirm one more time that this is working. So I will sign out. I will refresh. I will try to go to slash settings manually. I'm redirected back here. And if I go ahead and add new example.com, one, two, three, four, five, six, I think that's my login and password. I might be doing something wrong. No, this is the correct one. Great. So we can go ahead and explore the following items. So inside of our auth.cs file here, we don't have a lot of things set up besides the adapter and the session. But in here, we can do something called callbacks. So above the adapter, go ahead and add the callbacks like this. It's going to be an object and add a comma at the end. And in here, we can define different types of callbacks. So we can visit the AuthJS documentation to learn a bit more about callbacks. So this is located in the guides basics callbacks right here if you want to read some yourself. So as you can see here, callbacks are asynchronous functions, which we can use to control what happens when a specific action is performed. And here are a couple of them. So we have the sign in callback, which can be used to decide whether we are going to allow the user to sign in or not. So even if they successfully quit an account, we can still completely block them from ever signing up inside of our account. And this function is way more powerful than doing that logic in, for example, our actions login, right? So in here, I can technically do the same thing. If email is some user, I can block them from signing in, right? But if you write that inside of a callback, then it doesn't matter what method someone is using to log in, whether they use an API endpoint or our server action, NextOut is never going to allow them to log in if we write that inside of our callback here. So that's one example. Next, we have the redirect. So we are not going to mess around with the redirect. It works fine from default. And I believe you can read more about the redirect callback right here. So it's called anytime the user is redirected to a callback URL, for example, on sign in or on sign out. By default, only URLs on the same URL on the site are allowed. And we can use the redirect callback if we ever want to customize that behavior. So this is how it looks on default, right? So if we ever want to modify it, this is where we can do that. In our case, this works perfectly fine. And I believe in most applications as well. So we're not going to play around with the redirect callback too much. And now we have the two important callbacks, JVT and the session callback. So the session callback is actually what returns our session, which if you remember inside of our app folder, when we created protected settings page, we use that right here. So that's the session that comes from this callback session. This return session is what we get right here. And as I've just mentioned, we have to find a way to extend this session because this is not enough information to us for us to work with. But before we can extend the session, we have to extend the JVT, which returns our token because the session uses the token to actually generate the session. So let's go ahead and explore that a bit. So I'm going to go ahead inside of my auth.ts file. And first thing I'm going to do is I'm going to try and modify my JVT callback. So for that, you can write asynchronous JVT and go ahead and extract the token from the props here. And in order to get rid of the error, you always have to return the token at the end like this. And then what I want to do is console log the token like that. And you can wrap it inside of an object if you want to find it easier in the terminal log. So I'm going to go ahead and prepare my terminal here and make sure that you are logged in, right? And you can see how when I refresh here, this is my token. So the name is new. The email is new at example.com. My picture is null. And this is interesting. This is my sub, which is actually my user ID. So this is exactly what I can find inside of my database. So you can either go to NeonDB and look at your tables, or here's another way you can look at your database if you're using Prisma. You can run npxprisma studio, and that's going to launch it at localhost 55555. So in here, if I go inside of the user model here, I'm going to go ahead and see a couple of users that I've created. And there we go. Take a look at this ID, clq081. And if I take a look at inside of my other terminal here, there we go. CLQ 081 right here. So this is a matching ID of our user. Great. So we already have the ID inside of here. And here are some other information inside. But this is not enough. This is not everything that we need. But token is not the only thing you can extend from this JVT session right here. If you hover over here, you're going to see everything that you can extend. You can extend token, the user account, profile trigger, is new user and session. So you can go ahead and play around, for example, let's go ahead and log the user. Let's see what that's going to look like. So if I go inside of my terminal here, yeah, and I don't know exactly how the user is filled. I believe that this might only be not undefined the moment you log in, right? So I don't think that this user field is too reliable to use. And I think the same thing is true for the profile. For example, I don't think we even have profile for this one. There we go. You can see how it's undefined, right? But if I bring it back to the token, you can see the token is much more reliable when we have a logged in user. So for that case, we're going to use the sub from the token to actually load our user from the database inside of this callback. And then we're going to pass more information to the final session, right? So first, we have to go ahead and use this token. specifically we have to use token.sub and pass that to the session callback. So for now let's leave this as it is right. Actually you can leave the console log so that you can follow the flow of information going on. So this is the callback that we have. Now let's go ahead and add another one. So asynchronous session which can extract the user. Besides the user it can also extract sorry not the user token and session that's those are the props it has and the same as in the token you always have to return the session for this to work and inside of here we can console log the token but i'm gonna write it as session token so we know the difference all right this is the session token so let's go ahead and look at our terminal here and there we go you can see that now i have the session token which is identical to the token from our callback below\nSo here's what we can do now. If inside of this token, I decide to do this, token.customField is equal test like this. And if I go inside of my terminal, there we go. You can see that that is now passed inside of sessionToken, right? So it's both available in the original token. This is the token callback, but it's also available in the sessionToken. So this is my customField test. And then what I can do, so let's do another thing here. So I'm gonna write sessionToken to be token. And I'm going to write session, well, to just be session, right? So now we can keep track of both of those. There we go. So now I have the session that user is name, email, image, null, right? Here's what I'm going to do. I'm going to go ahead and I'm going to write session.user. And let's write custom field again to be, well, we can actually transfer it from here, right? So we can now use this token, token.custom field like this. And let's just go ahead and write if session.user just so we don't have the error accidentally like this. And now I believe if I refresh here, there we go. On the settings page, you can see how now I have name, email, image, and here is my custom field fully working inside of my server component. So I think this already gives you an idea of how we can transfer the ID from the token inside of this user session, right? So if you want to, of course, you can always write something you want here. So it doesn't matter. You don't even have to use the token to extend the session. If you want to, you can use it completely like this. So you can see how now the custom field is anything. But here's what I want to do. What we want to achieve is we want to get the ID for the current user, which we've just established that inside of the token or session token, is sub. So let's go ahead and do this. I'm going to remove this console log, I'm going to remove this token. So I'm just going to return the token in the JVT callback. And then inside of here, what I'm going to do is I'm going to write if we have token.sub, and if we have session.user, in that case session.user.id is going to be token.sub like this so as easy as that and let's take a look now there we go we officially have the id inside of our session so now every single place where we use this session be that a server component or a client component we can always have access to the ID of our user. But this was quite easy, right? Because we already had the ID inside of token here. But what about a completely custom field, right? What if we want to add a new field inside of our schema, for example? Well, we actually do need some new fields. So let's go ahead and do that. So I'm going to go inside of schema, schema.prisma right here, go inside of the user here. and after the password, let's go ahead and let's add a role for our user and let's make this a type of user role, which we don't have. We're going to create it in a second and let's give it a default value of user like that. So in here above, I'm going to write an user role and I'm going to give it a type of admin or a type of user like this. There we go. So now we have a enum of user roles which are available for a user model. So make sure that you save this file, give it a default value of user. And now what we have to do, well, first I recommend that you shut down your app completely and then run npxprisma generate. So this will add it inside of our node modules. And I think it's best that we clear up our entire database. A, because I wanna teach you how to do that. And B, because I don't wanna have any old fields which don't have the user role. So before we do that, let's actually do this. Let's do npm run dev and let's just sign out. Since we're gonna remove some users, I think it's better that we don't mess with the cookies. So let's go ahead and sign out. Of course, I believe this isn't a problem for NextOut. I'm pretty sure they can handle deleted users, but just for development, I don't want you to have any problems while you're doing this. So make sure that you are signed out, make sure that you updated your schema Prisma. and now let's run npxprisma migrate reset. So what this is going to do is going to reset the entire database. So it's gonna give you a question to confirm that so all data will be lost. Of course, only do this in development. Don't do this in production. And now that the database reset was successful, we have to run npxprisma database push. So every time that you reset your database, you have to run npxprisma database push. And after this has been done, let's go ahead and do npm run dev like this and let's go ahead and refresh the login page here and if you want to you can also either keep open the neon database or npx prisma studio so one of the two just so you have an overview of your application so my users are completely empty as you can see here so if i go back here i will not be able to log in because i don't have any account. So I'm going to create a test account here with the password 123456. I'm going to create this account. And this should add a new user inside of my database. There we go. We have a new user with an encrypted password. And there we go. We have a default role for our user. And you can see how it has an enum so that I can change it to only one of the two here. And now if I go ahead and log in with this user. So test at example.com 123456, you're going to see that I'm logged in with this user right here. Great. So what my goal is now is to extend the session so that I can actually have access to this role user right here. So how can I do that? Well, we can do that quite easily. We have to go back inside of our out file right here, and we have to focus on the token session. So we first have to pass this to the token. The reason I want to pass that to the token, because we can get access to the token inside of our middleware from the request right here. And then it's going to be useful for us to know whether someone is an admin or not inside of the middleware. Because then we can write something like is admin route. And then we're going to write the same logic. If is admin route, and if you are not an admin, redirect the user back. So we can create role-based access, right? a role-based access control using the middleware and the token extension so here's what i want to do i want to go ahead and i want to import get user by id from data user so just make sure that you have a get user by id it's very simple so it's very similar to our get user by email of course you can use the get user by email as well but keep in mind that that's going to be a very expensive query because ID is a primary key. So obviously the query is going to be much faster for that. So now inside of this token here, what I want to do is I want to go ahead and fetch my user. So I'm going to write the following. First, if I don't have token.sub, that means that I'm logged out. So I'm just going to return the token, right? No need to do anything here. Then I'm going to go ahead and write const existing user to be await get user by ID to be token.sub. Then I'm going to write if there is no existing user, I'm going to go ahead and return the token again. And finally, we can go ahead and assign the role to the token. So token.role is going to be existing user.role like this. And let's go ahead and console log the token inside of our session now. So I'm going to write session token to be token here. And if I go inside of my terminal here, inside of my original npm run dev, there we go. You can see that my session token now has a role of user. Great. So what I can do now is the following. I can do if token.role and session.user, session.user.role is going to be token.role. And for now, just ignore this TypeScript error. But let's take a look at our app now. If I extend this, there we go. After my ID, you can see that I can find a row of user inside of my session. So that's how we can extend the session inside of NextOut. It's actually not that complicated. You just have to know the flow. So first it starts with the token, right? In the token, we already have the ID, which is stored in the subfield. And then what we have to do if we want to get more information is get the user from our database. Apparently, you can also use the extracts from user and profile. But honestly, they are always undefined for me. So I'm not exactly sure how we are supposed to use them. If I learn, of course, I'll make a video about that. But for now, I completely rely on something like this, right? And this is also why we had to separate our auth and our auth config. Because in these callbacks, we're using Prisma, which is not working on the edge. So if we had callbacks, which were defined in auth config, then that will be going through the middleware. And then it will break the app because it's not supported on the edge. But I believe that inside of auth config, we can freely use Prisma inside of providers like credentials, because this doesn't run on the edge. This is simply run once the user tries to sign in. So I think that's why this is working completely define here. Great. So now that we know how to extend the role, let's go ahead and explore how to modify the TypeScript or the types for the session and for the token. So in order to add the TypeScript to this user inside of our session, there actually is a guide for it. So let me go ahead and expand the screen so you can find that. So it is in getting started TypeScript session right here. And you can scroll down here to the adapters. And in here, you're going to have module augmentation right here. And they're going to teach you how to extend the existing session user and add a specific field that you want. But here's the thing. This actually does not work for me, but we're going to try it out. And then I'm going to show you a solution that I use, which works for me. But still, I'm going to give it another shot. Maybe I missed something. So they are doing this inside of out dot CS. So we're going to try that as well, we're going to write our declaration here at the top. And we're going to try to fix this error of our role not existing in this user field because by default it doesn exist here So let go ahead and do the following So let add this import from next out importing the next out and the type default session Let's go ahead and try that out. So we already have this actually. So we can just add a comma and import and default session like that. And then we have to go ahead and declare a module out of the core like this. So let's go ahead and declare this module. So I'm gonna do that here. I'm gonna close this. Then we have to create an interface session. We have to get the user. And then we have to extend it before we write anything inside using the default session and specifically get the user like this. And then this is where we would add our role, for example, to be admin or guest or sorry, user, right? But as you can see, it's clearly not working for me. So even if I add it as a string or something, it's not working, right? So it simply does not exist on type user. No matter how many times I declare this module, this is not working for me. So even if I reload my window, there we go. It's still not working. So at first I thought, okay, maybe it's because I'm declaring this inside of this file. So let's try the alternative thing. Let's try this. I'm going to close this and I'm going to create a new file in the root of my application. Next-out.d.ts. Maybe I have to declare it here, for example. So I'm going to copy this here. And I'm going to paste it inside of next out DTS right here. And in here, let's import next out. And let's import type default session from next out. And I thought, okay, maybe that will work. So if I remove it from here, and if I remove this import now, but as you can clearly see, it's still not working for me. So I don't know if this is maybe a part of the migration process or maybe they missed something, but this is simply in no way working for me. So this is what I'm going to do next. I'm going to do the following. Instead of extending auth core, I'm going to be extending next auth. So this is what I'm going to do now. First, I'm going to write our extended user. So export type extended user is going to be default session user. And in here, I'm going to go ahead and write role to be admin or user like this. And then inside of here, I'm going to write session user to simply be extended user like this. And I'm going to change this declare module to go directly to max-out like this. So now looks like it's still not working. But if I go ahead and reload this, I think that maybe then it will work. And OK, as you can see, now it can recognize it. But now we have a problem that the token.roll doesn't match what we just defined inside of here to be admin or user, right? So what you can do is you can kind of explore even further how you can modify the token. But I really didn't manage to do that, especially in the next out version 5. I could do it in the old versions, but it's no longer working for new versions. so what you can simply do is as admin or a user for example and there we go now you can see that it's working and you can see that user.role is the correct one obviously it's not a really clean solution right but I think it works good enough but if you want to we can try and extend the token as well so they obviously have instructions for how to extend the token but I believe it's still not going to work, but let's try it out. So I'm going to go and remove this as admin user, right? So let's keep this in an error and let's attempt to resolve this. So we have to import JVT from outscore JVT and we have to declare the module outscore JVT like this. And in here, I should just go ahead and add a role to be optional and add it to be as admin or as user like this. So now if I refresh this, for example, so I'm pressing command shift and P to open this little command here and I do reload window. It's the same thing as shutting down your Visual Studio code and bringing it back up, but it's faster. As you can see, this is not working for me. Token.role is just an empty object for me. So I don't really know. Perhaps we can try maybe extending next out slash JVT. Maybe that will be better. And maybe we can import JVT from next out JVT. Does that have the JVT alias? It does. Token.roll. No. If I reload my window, I think it's still not working. Yeah, you can see that no matter what I do, this used to work in the previous versions, but now it's no longer working. But, you know, at least we know how to extend the session. Of course, this might change in the future. They might fix this. So that's why I want to show you this guide. They have clearly defined instructions on how to do this, right? But at least, you know, these are just types, right? I know it sounds weird, just types. But yeah, you can technically, if you exactly know what you're getting here, you can write stuff like as, you know, you can actually import user role from Prisma client directly. So user role from Prisma client like this, and that's the equivalent of admin or user, right? So you can use that here, but I actually don't recommend importing that here because I think you're going to lose your auto import for the user role. So you can try it like user role from Prisma client. And I believe that if I like remove this here, maybe I'm wrong. Maybe it doesn't work. Maybe I just made it up. So I'm just going to write as string for now. I'm going to refresh my window. And then I think if I try to import user role, oh, it's working. Okay. Then maybe you can add it there as well. Yeah, do that. Replace the admin and user with user role from Prisma Client. My apologies. It looks like in my development process, I got some bugs doing that, but it looks like that was just in my session. So we can now import user role from Prisma Client in the auth.ts file, and we can use the token role to be expected as user role. And because we extended the session user with the extended user, which has a role property of user role, there are absolutely no errors inside of our application here. And now we can freely, well, nothing has changed here, right? We were just working with the types now. So it's not really important from the functionality of the project, but it does make our development experience much better now. Perfect. And if I go ahead and go inside of my, inside of my app folder, protected settings, page.vsx right here. Let me just try and do session.user. And I think that you can see how it gives you the types of user and their role. So I can safely have an autocomplete for the role here and an autocomplete for the ID here, right? So that's what's important for our development experience. That's why we care about TypeScript working, because by extending this, it's one thing to just have it accessible and visible here, but it's another thing to have type safety so that we know that we are working with that information. Also, one more thing, in previous versions, user.id was not defined. Now it seems to always exist. So usually you had to do the same thing in the extended user. You also had to add an id to be a type of string, but now it looks like you don't have to do that anymore. but if you for any reason have an error it's just as easy as adding it here so id.string right and whatever other fields in the future you might have right so if you can add a complete custom field here and if you go ahead and write session.user. there we go you have the custom field autocomplete and you can assign anything you want here for example if i go ahead and visit my code now, you should see the custom field to be anything here. There we go. So I hope you kind of learned how we can manipulate callbacks in JVT and session now. So just remove that assignment of a custom field and go back instead of next out and remove the custom field from the extended user. We were just doing that to practice. Great. And here's another thing I quickly want to show you while we are already here. So I'm going to go ahead and sign out now. And if I go inside of my Prisma Studio, you will notice that I have a field email verified to be null. So here's what I'm going to do. I'm going to go ahead and I'm going to attempt to block myself from signing in. So let's go to the top here above the session. And let's write asynchronous, sign in. And in here, we can the structure of the user. And by default, let's return true. So we allow the users to sign in. And in here, let's go ahead and see what this user is made of. So I'm going to write const database. Actually, let's call it existing user to be await, get user by ID to be user.id like this. and then I'm going to write if there is no existing user or if there is no existing user dot email verified in that case return false. So this way, if the logged in user is not inside of our database for any reason, if that might happen, or if we don't have verified email, we are not going to allow the user to log in. So let's try that out. So by looking in my database, if I try to log in with this user, which doesn't have their email verified, it should not be allowed to log in. So if I try test at example.com, one, two, one, two, three, four, five, six, right here, I should not be able to sign in. Let's take a look. And there we go. Something went wrong. Perfect. So we just finished that. Of course, I'm going to remove this for now. We're not going to need it. We're going to come back to this sign in. I just feel like it's easier for us to work with when we are allowed to sign in so we can fully see our active session and everything regarding that. Great, great. So I think we did a good job going over all the important aspects of callbacks. I hope you learned what they do and what they are used for. And what we going to do next is we going to go ahead and enable GitHub and Google sign in And then we going to go ahead and create some email verification Great great job So now let's go ahead and let's set up our OAuth providers, Google and GitHub. So if you're logged in, go ahead and log out so you can see the login screen right here. And the first thing I want you to go through is to localhost 3000 slash API slash out slash providers. And right now, the only thing that you should see is the credential providers, because that's the only thing that we have inside of our auth config. Right here, we have the providers. And the only thing we use is credentials, as you can clearly see right here. So what we're going to do now is we're going to go ahead and import GitHub from next out slash providers slash GitHub. And we're going to go ahead and use this and add it just above the providers here. So once I save this and refresh this page, there we go. Now I have my GitHub provider here. And we can do the same thing for Google. So let's go ahead and copy and paste this one. Let's import from Google and let's rename this to Google. And then we can just as easily add Google at the top here. And then when you refresh this page again, there we go. Now we have Google, GitHub, and credentials. Great. So what we have to do now is we have to obtain some environment keys. So first, let's do them for GitHub. And let's define them inside of these providers here. So open an object and define the client ID to be process.environment.github.client.id. And then client secret to be process.environment.github.client.secret, like that. And then copy this first variable and immediately add it to your .environment file. like this, GitHub client ID. And then do the same thing for GitHub client secret. So just paste it here. Now let's go ahead and obtain those. So you have to go to GitHub and go inside of your profile right here on your settings. So you can click on the sidebar here and find the settings here like this. Then scroll all the way down and go to developer settings. And in here, go inside of OAuth apps. Go ahead and click create new OAuth app and give your application a name. So this is going to be our tutorial for me. And then let's give our homepage URL. So that for me is HTTP localhost 3000 like this without the slash at the end. So just leave it at 3000 to be the last thing. And now we have to find our authorization callback URL. So we can easily find that inside of this API auth providers. We have the callback URL written right here. localhost 3000 slash API auth callback GitHub. So let's go ahead and copy this. And let's go ahead and paste it here and just remove the annotations. Make sure you have no annotations at the beginning or at the end of your application. And go ahead and click register application like this. And there we go, you can now obtain your client ID. So let's go ahead and assign the client ID like this. And now we have to get our GitHub client secret. So let's go ahead and click on generate a new client secret. And there we go. You should now be seeing your GitHub client secret here. So copy this and let's paste it here. There we go. GitHub client secret and GitHub client ID. Perfect. So now let's do the same thing, but for Google. So go back inside of auth config, expand this object right here and assign the client ID to be process.environment.google.clientID and client secret to be process.environment.google underscore client underscore secret as well. Like that. Great. And now we have to obtain both of those. So let's add them to our environment files. So Google client ID and Google client secret. So in order to obtain those, go ahead and Google Google API console and click on the link which goes console.cloud.google.com. And that will open up a console similar to this. So in here, first step is to create a new project. So click on this navbar at the top and click new project. Let's give this a name of auth tutorial. And let's click create right here. And now just wait a second for this to be created. Once it's been created, go ahead and click select project. and ensure that in your navbar, your new project is selected. And now go ahead and click the search and search for APIs and services. So this one, APIs and services like this and click on this one. So it opens the actual page for that. And in here, first, let's define OAuth consent screen like this. And go ahead and select the external user type. So anyone with a Google account can test out the authentication. Give your app a name, so out tutorial. And go ahead and select your user support email, which should autocomplete from your current account. You can skip the app logo and app domain. And you can also skip authorized domain. So we're going to add this after we deploy, because right now we cannot add localhost here. and go ahead and add a contact developer information email address here and click save and continue and once you've done that scroll down and just click save and continue so no need to add any special scopes here and no need to add any test users just save and continue and there we go you're going to see your entire thing right here and now let's go and click inside of credentials here and let's go ahead and click create credentials and let's go ahead and create OAuth client ID. So inside of here, select the application type to be a web application. You can leave the name as it is. And now we have to add authorized JavaScript origins and authorized redirect URIs. So let's go ahead and write HTTP localhost 3000 for this one. And for the authorized redirect URLs, we have to use the one defined in here, callback URL. So just copy this one. Go back to this and paste it here. And of course, remove the annotations like that. And make sure you don't add a slash at the end here or a slash at the end here. So make sure it ends with Google like that. And go ahead and click create. And there we go. You now have the client ID, so we can copy this. So Google client ID. And we also have the client secret. So copy the client secret as well. And let's paste it here. There we go. So we have everything we need now. So what I want to do now is go ahead inside of our app folder, sorry, components out social right here. And now we have to implement the sign in functionality from here as well. So you already know that there is a certain way we can sign in by using the out.ts from where we export the sign in function. But we can only use this in server components or in server actions, like we do here in the login. We import sign in and then we use it here and we specify credentials. So we can technically create a new server action, which we'll simply call await sign in. And then in here it would be Google, right? We can do that. But I want to show you a different way of doing it. Just in case you were wondering, you can do it still completely inside of client components without server actions as you could before. So for that, let's go inside of components, out social right here. And the first thing I want to do is create this common on click function here. So let's go ahead and write const on click to accept a provider, which will either be Google or GitHub. And in here, let's go ahead and let's import sign in from next dash out slash react. So that's what you have to import if you want to use it purely in a client component. So go ahead and select the passing the provider param. And then instead of having a redirect URL, redirect to, as we have if we import it from out, which is used for server, right? In here, we have a callback URL. So let's define the default login redirect from at slash routes to lead to there. And now let's go ahead and use this on click here first for the Google button. so on click and passing google as the provider and in here pass in github as the provider and now let's go ahead and test out both so here i have my prisma studio open you can run it by running npx prisma studio i only have one user in my database and now let's go ahead back to our login page and let's go ahead and click on github here and there we go you can see that it's asking me to authorize this website. And once I click authorize, I should be redirected and I should be logged in on my settings page. And there we go. And you can see that now I have some new fields. For example, my image is automatically filled with avatars from GitHub. I also have the email which is connected to my GitHub account. And I still have the role user and I still have my ID here and if I check my Prisma Studio and refresh my users I have a new user here like this and you can see that I also have a link with an account because this is an OAuth sign in so I have a proper relation with my account where more information is stored like what is the provider what is the access token the token type the scope more things if you're interested in that great so now let's go ahead and try this out with Google provider. But here's the thing. If you try and log in with the same GitHub, sorry, with the same email that you just used for GitHub, you're going to get an error. So if possible, try and choose a different email just to test this out. So something you don't have in your database. So make sure you try with a different email, right? So I'm And there we go. So I'm logged in with different email here. And there we go. Now I have an image from Google, right? And I also have the role user, and I also have the ID, perfect. And if I go ahead and refresh my Prisma Studio here there we go Now I have three users So it official Our OAuth providers are now working But here what I want to do now Email verification is only going to be needed for credential users because Google and GitHub already do email verification on their own, right? Google in itself needs to have two-factor authentication to confirm that the user exists and phone number verification. And GitHub simply uses a verification email link. So there's no point in us doing that as well. That's the point of OAuth providers so that they offer users a seamless sign-in and they offer you security that it's not a spam email. So here's what I wanna do. Go ahead and select these two users in your database which are logged in using GitHub or Google and go ahead and delete those two records from your database. And that is automatically going to delete the accounts as well. So if I go into account here, you will see that I have no rows once I remove those two users. And here's what we can do now. So we can use, we can go inside of out.ts here. And besides callbacks, there is also something called events in NextAuth. So let's take a look at what the documentation says about events. So events are asynchronous functions that do not return a response. They're useful for audit logs or reporting or handling any other side effects, which is exactly what we need right now. So, for example, we have an event sign in. So when a user signs in, if you add that to the event object inside of out, you can do whatever you want here. Right. You can also check if it's a new user. So if you want to add a different field or something, you can do that. And that's going to be asynchronous and it's going to be useful for audit logs, for example, if you want to add some logs here or if you want to do some side effect. We are specifically interested in a link account. Why are we interested in this one? Well, if this event is ever triggered, that means that the user just used an OAuth provider to create or log in inside of their account. Also, you probably noticed that we don't need a special register for OAuth. So it's very simple. If it exists, it's going to log in. Otherwise, it's going to create a new account. So we don't need a... This will work equally. We reuse this component here, right? It's exactly the same. We don't need a special login for using credentials. So where was I here? So we're going to use a link account so that whenever someone creates an account using Google or GitHub, we're going to automatically populate this field called email verified. Because again, there's no need for us to verify an email coming from an OAuth provider, right? I mean, except if you chose a really, you know, shady OAuth provider, of course. But we are working with Google and GitHub here so we can trust them completely. So let's go ahead and let's do that. So I want to go inside of out.ts here, and let's go ahead and let's add events. And in here, I'm going to use the link account right here. And inside of link account, we can go ahead and extract the user. And let's also mark this as an asynchronous function. And in here, let's go ahead and simply do await database.user.update where we have a matching ID of user ID and the data we are going to update is the email verified field to be new date. So it's not a Boolean. It is a date field. So we know where an email, when an email was verified. So in the future, if we have some new rules for email verification, this is going to be very useful because we can simply query and find all users which have not verified their email in a long time. So it's better than a Boolean, which is true or false. That's it. That's all we have to do. And let me tell you what I was talking about. In the model user, email verified is a date time, which is optional. Great. So confirm that you only have one user in your database, which should be the credential providers, you know, email name and password here with the email verified null. So now if I go ahead and use GitHub, for example, to log in, what should happen is a new account should be created. And besides that, if I go inside of my Prisma Studio here and refresh this event link account, there we go, has immediately verified the email. We know that everything is fine with this account. No need to verify anything further here. Perfect. So that works just as we expected. But now we have to talk about this little error that we can get. So this is my current email, which I use for GitHub, right? So what happens if I go into my Google account and use the exact same email to log in? Let's try it out. Well, as you can see, I'm redirected to this weird page which we've never seen before. And you can see that we have an error to confirm your identity sign in with the same account you used originally. And in my URL, I have an error here. So how do we, first of all, how do we not show this page? So this is great. This comes out to generate it from next out. But I wanna use my own pages. I don't want to use this pages. It's great that they exist out of the box for sure. But how about we use our own? Well, we can do that as well. What we have to do is we have to go inside of out.ts here. And let's go ahead and do the following. Above events, add pages. And in here, first, let's define our sign-in route. So for us, that is slash out slash login. So now next out is always going to redirect to this route when something goes wrong. And let's also add an error route to be slash out slash error. So if something else goes wrong, regardless login or just something breaks, I never want the users to see this page. I want them to see my nicely designed page instead. So you obviously know that we don't have this page. So we have to go ahead and create it. So it's going to be quite simple. Let's go ahead and go inside of app, alph, create a new folder called error. And inside a new file, page.tsx. Let's go ahead and import the alph error page or error page. It really doesn't matter. and actually not a div. What I wanna return is an error card, which we don't have yet, but we're gonna create it in a second here. So let's go ahead and create the error card by going inside of components out, create a new file, error-card.psx. Let's go ahead and let's import the header from dot slash header or components out header. Let's import the back button from dot slash back button or components out back button. And let's finally import everything we need from components UI card. So that's the card, card footer, and card header like this. And let's go ahead and export const error card here. And let's return a card. Whoops. let's return a card with a class name of width 600 sorry 400 pixels shadow medium and let's add a card header here with a header component and let's give it a label of oops something went wrong and a card footer with a back button component which will simply have a label back to login and href of out login like this. Great. And now let's go inside of the out error page and let's import the error card from components out error card. So now if you visit localhost 3000 slash out slash error like this, uh oh looks like it automatically redirects us to here and i believe that is because we have to add that inside of our routes.ts here so we have to make that an out route so let's go ahead or even better let's make it a public route actually no let's make it an out route i think that makes more sense out there because it's only going to happen for logged out users yeah so let's make this inside of out routes here. So now if you try and go there again, out error, you should see, oops, something went wrong like this. Perfect. And I just remembered that I could have probably used inside of my components out, where is it? Error card. I think I could have used the card wrapper. So let me just try it out. Hard wrapper. Yeah, I think I could have used that. So I can just give it a header label of oops, something went wrong like that. I can give it a back button href to outlogin and a back button label of back to login like that. And in here, I can add a exclamation triangle icon from Radix UI icons and maybe wrap that in a div. with a class name wFoolItemCenter and a flex maybe justifyCenter there we go and give it a little class name of textDestructive like that alright, that looks fine and yeah, now we can remove all the imports besides cardwrapper and I'm just going to use components out like that. So just a little bit of styling. Obviously, it doesn't actually matter for the tutorial itself. And we can go back to the login. Great, but let's go ahead and try this again. So I'm gonna go ahead and use the Google login and I'm gonna select the same email that I already have inside of my database here. So what probably happened is that you didn't get redirected to the error page. Instead, you came back to the login page. Well, that's an improvement from what previously happened, which just redirected us to that weird page which we've never seen before. But here's the thing. If you take a look at our URL,\nnow have the error inside of our URL. So we can use that to display the error here instead. So the reason it didn't redirect us to that error page is because that's used for something else, right? But we have that covered as well. But let's go ahead and read from the URL now inside of the login form. And let's go ahead and show the error inside of our little form error box at the bottom. Let's go back inside of our form. So components out, sorry, login form right here. Oh, and we have some errors here. Okay. Well, we'll solve that in a second. First, let's go ahead and let's import use search params from next slash navigation. Like this. Let's go ahead and let's get the search params. So const search params. are use search params. And then let's get the URL error to be search params dot get error. And now let's go ahead and compare it. So if it is our current error in the URL, so I'm going to copy it from here, which is OAuth account not linked, or you can just simply see what I write here. So O out account not linked, the capitalization also matters. So if that's the case, we're going to write email, or we can write, please log in with, or you can just say email already in use with different provider. All right, something useful, otherwise an empty URL error. and then you can go ahead and use this URL error down here in the form error so make sure that the error goes first and then pipe pipe URL error and there we go email already in use with different provider so I'm going to go ahead and refresh this and clean the error from my login so I'm going to try and log in again and see if it appears and there we go you can see how the error is being thrown now. And you can see that if I go ahead and continue something here, and now I have a new error, you can see how it replaces that error. So that's also something we want. We don't want that error to always be shown there. Great. So now let's go ahead and see why we are having these little errors here. So I believe we can just easily fix that by adding exclamation, question marks here to the data like this. Success does not exist on property error. All right, let's go ahead and check that out. Why doesn't it exist? All right, if it doesn't exist, we probably didn't get around to doing that yet. Yeah, so it doesn't exist yet. We don't throw success anywhere. So for now, we can comment this out and I will add to do add when we add email, sorry, 2FA. Yeah, success is going to exist later when we add two-factor authentication. So in the login function, we're going to throw a success, meaning the two-factor code has been sent, right? So that's what we're going to use this for. But for now, no need for this. Great. So I don't know when we got that error. Sorry if I didn't catch it earlier. I hope it didn't cause you any problems. But yeah, you can just add a little question mark here. Great. So we just implemented OAuth inside of our application. So just to wrap up this module, you might be asking yourself, well, should we really get an error for this? Like I'm pretty sure I've seen some pages which are able to combine different OAuth providers. Like it's called automatic linking, right? Why don't we have that? Is it good to throw an error here? Well, you have an answer for that question inside of frequently asked questions. So in the concepts, frequently asked questions here, there is one called when I sign in with another account with the same email address, why are accounts not linked automatically? And in here you have an explanation of why they do that. So they are obviously thinking about that. And there obviously is a certain risk between doing that, especially with the amount of providers that they support. I believe that between Google and GitHub There might be no issue, right? They're obviously very reliable providers But not the same can be said for all of them, I guess I believe there actually is a little key Which you can add to manually link an account I think it's called something like dangerous linking And then you just enable that But I would recommend you don't do that Obviously, if they disabled it, they know something we don't Great. So you just wrapped that up. Great, great job. So what we're going to do next is we're going to go back to our register here and we're going to implement the email verification for credential users because we technically wrapped up email verification for OAuth users. All right. So now let's go ahead and let's create the verification process for credential registration. So the first thing we have to do is we have to go inside of our Prisma schema and we have to create a model which will be used to verify our credentials, right? So let's go below the account here and let's create a new model called verification token like this. Let's go ahead and give it an ID, which is a type of string. It is the default ID and the default value of CUID. and let's give it an email of string and let's give it a token of string and let's make sure that the token is unique and let's give it an expires of date time. So we're going to make sure that the verification email can expire, right? We don't want anyone else to abuse that. And let's add a unique rule for the combination of email and token. So only one unique token per specific email. Great. So now we have the verification token here and there's no need to create a relation with the user. It can live on its own like this. And let's go ahead and push that where we need to push it. So what I recommend you do is that you shut down your app and run npxprisma generate and then let's do npxprisma database push like this. So now you should have a new collection in your Neon database. And I believe you can also check that immediately here in NBX Prisma Studio. So if I run this right here, and if I go inside of here, you should see a new collection verification token as I have right here. So everything is fine. Great. So now let's go ahead and let's create a couple of functions, which we are going to use to create this. So the first thing I want to do is I want to create my data lib for the verification token. So let's create a verification token here. .vs. So the same way we did like with user. And let's import the database from add slash lib database. And let's write export const get verification token by email to be an asynchronous function which accepts the token, which is a type of string. And inside, we are simply going to open a try and catch block. In the catch, we're going to return null. Otherwise, let's get the verification token by using await database.verificationToken.findUnique. Let me see if I can expand this. There we go. find unique where we have a matching token like this and then return the verification token and my apologies this one should be email right so this is get verification token by email so we are not using the token to search we are using the email to search so where email like this and in this case it's not going to be find unique it's going to be find first like this so email email like that great and let's go ahead and copy and paste this and let's name this one get verification token by token and let's accept the token prop and then here we can use find unique and we can use the token query there we go so make sure you have get verification token by the token field and get verification token by email. In here, we use find first and we query by email. So make sure you have both of those. Now that we have this, let's go ahead and create a lib, which is going to be used to generate these tokens and make sure that if an existing token exists, it gets removed. So let's get inside of lib and create a new file called tokens.ts. So In here, I'm going to keep all kinds of tokens which we are going to generate. But the first one is going to be export const generate verification token, which is going to be an asynchronous function which accepts the email. And then inside, let's go ahead and we have to generate the actual token. And for that, I'm going to use the UUID. So I ensure that it is always unique. And for that, we have to install UUID like that. And let's see if we need the types for that. So if I import V4 as UUID from UUID, there we go. I need the types for that. So I need to run this command right here. So npm install dash dash save dash dev or just dash capital D types slash UUID. So we need that as well. And that should get rid of this type script error. There we go. So import v4 from here. And now we can get our token by simply using, so let's import it as UUID v4, like this. UUID version 4. Great. Now let's go ahead and write const expires to be new date. New date again, dot get time. Sorry, plus 3,600 times 1,000. Like that. So basically we're going to expire the token in one hour, right? This is going to calculate the number of milliseconds in one hour. And this gets the current time. So we add one hour from now and we wrap all of that in a new date So we have that And now what we have to do is check if we have an existing token already sent for this email In that case let go ahead and do const existing token to be await get verification token by email because that's the only thing that we have at this point inside of this prop. So we're going to use the email one make sure that you have it in here, make sure that you use find first and the email query here. And if we have existing token in that case, we're going to go ahead and remove it from our database. So let's go ahead and import our database from add slash lib slash database. So in here, let's write await database.verificationToken.delete. And where is going to be ID of existing token.id. Like that. And now we can generate a new verification token. So const verification token is going to be await database.verificationToken.create. Like that. And data is going to be email, token, and expires. There we go. And now you can just go ahead and return the verification token like this. Perfect. So now we have to find a place where we're going to run this function, generate verification token. The first place we have to send it, we have to use it, is inside of the registration itself, right? So let's get inside of actions, register right here. And we already have a little to do here. Great. So let's go ahead and let's import generate verification token from at slash lib tokens right here. And then we're going to go here at the bottom once we create the user. And let's just do const verification token. So verification token is going to be await generate verification token. and we're gonna pass in the email that the user is trying to create an account with like that. And then we can change this message to be confirmation email sent. So obviously we're still not sending the email, but for now we're just gonna have the verification token here. So let's go ahead and make sure that our app is running and you can also prepare NPS Prisma Studio in another terminal so that you can see when it gets added. So let's go ahead and check it out. So I'm going to refresh this right here. I'm going to pair my Prisma Studio here. And right now I have zero verification tokens here. So nothing in my Prisma database. So I'm going to create a new, actually, this is my email, right? So this is verification test. It really doesn't matter what's the password. and all this is sent now is tell me that the confirmation email has been sent and inside of my Prisma Studio if I refresh this I should have a new verification token and there we go you can see that we have the exact email which this verification token is used for and we have the unique token using UUID and we have the date when it expires perfect so what we have to do now is actually send an email, but there is one place where I wanna generate this token before we do that, and that is in the login, right? So this user should not be allowed to log in at the moment, right? If I use that email, which I have right here, and if I try and log in here, it's going to work, right? Well, first thing we have to do is we have to not allow the user to sign in if they still haven't set up their email verification. And otherwise, if they try, we need to send them a message like, hey, we sent you an email again here on the login form. So if they go away from the register form, they need a way to send that email again. And they can do that very simply by trying to log in. And we're just gonna send them an email again. So let's go ahead. and we're going to do the exact same thing here. So let's go inside of actions, login in here and let's go ahead and import generate verification token from add slash lib tokens right here. And now what I want to do in here, when we get the email and the password, let's go ahead and let's try and get an existing user from our database. So I'm going to use await get user by email. So make sure that you import get user by email by data user. We already have that and it's very simple. We also have the user ID. So in here, we're going to attempt to fetch the user that the current user is trying to log into with the email. And in here already, we can check if there is no user or if there is no user dot email. Sorry, existing user. Or if there is an existing user dot email. or if there is no existing user.password, meaning that they shouldn't be logging in with their credentials. Instead, they should log in using OAuth. In that case, we can return an error, invalid credentials. Or you can return something a bit more specific like invalid, I don't know what will tell the user. So it's either gonna be, user does not exist or yeah, let's do that. Email does not exist. Let's do that. All right. And now let's go ahead and check if the user exists, but it doesn't have its email verified. So if exclamation point right here, user doesn't have the email verified, existing user, my apologies, existing user does not have the email verified. In that case, we're gonna generate a new verification token right here using await, generate the verification token and passing the user dot email, existing user dot email. So like this. And then we're going to return success confirmation email sent. Right. So we're going to break the function here and we are not even going to attempt to sign in. But remember, we are not done yet because users can still use the API to log in inside of our app. So we have to protect them inside of the sign in callback. So we have to do that as well. But we're going to do that in a moment. I just want to test out that the new verification token is being generated if I'm trying to login with the user that doesn't have their email verified, meaning that they just signed up for their account or they waited longer than an hour. So the email that we sent them during the registration is no longer valid. So they need a new email. So one of those. So let's go ahead and try that out. But just before we try it out, I think we have to revisit our components out outlogin form because in here, I believe I commented out. Yeah. So I wrote 2FA. Yeah. Yeah. It's actually already. So make sure that you add inside of the on submit function, inside of out components outlogin form, just bring back set success data question mark dot success like this. Let's just confirm that we actually show the success message we do. All right. So let's try it again. So this time I'm going to go ahead and log in again as this user. And I just want you to kind of pay attention to the current token, right? Kind of try and remember like the last few characters, because the only thing we're going to notice is that this one, this token will be updated and the ID is going to change. So try to, you know, keep an eye on that. So I'm going to try login now and I should be blocked from logging in and I should get the message confirmation email sent so if I refresh this there we go you can see how the token changed and the id changed meaning that we successfully created a new expiration well we could have just looked at the expiration yeah we just created a new expiration for the token great so we're almost there we just have to do the same thing now inside of our auth.ts because you know you already saw that sometimes auth can redirect to that special page which it generates by itself so from there we don't protect anything right so that's why whatever you do in your login or register functions you also have to do an equivalent inside of callbacks because what you're doing the register and login actions or api routes It doesn't matter. You're doing that, well, yeah, for security, but also for user experience. But for total security, you also need to do it inside of NextOut. So you need to have equivalent things inside of NextOut. I could have easily done this, you know, inside of registration as well, but it's easier to do it in this way. Like I like to know that my out is taking care of this. So when it comes to out, you never want to be worried that you're missing out on something. And this is, you know, the best way to do it right here. So let's go ahead and we have to create a callback called the sign in. So I'm going to do this at the top here. So asynchronous function, sign in like this. And inside, we're going to structure the user and the account. By default, let's return true, meaning we are allowing you to sign in. But here's what we're going to do. We're going to check and we're going to allow OAuth without email verification. So the way we can do that is simply by checking if account?provider is not credentials. Make sure you don't misspell this. So credentials. In that case, simply return true. That's it. So if it's anything other than credentials, I'm going to allow this without email verification. Of course, when you start adding more providers in your code, you might have to adjust this. I'm not sure what kind of providers exist in NextOut. So if you might want to be more specific for example you can do the opposite thing You can do if account is credentials And in that case you know open the whole logic to check whether the email is verified or not. But I'm doing it this way. It's simpler for me, right? And inside of here, I'm going to attempt to get the existing user using await get user by ID using user.id inside of here. and I'm going to simply check if not, so put an exclamation point, existing user question mark dot email verified. In that case, return false. So very simple. If the user has not verified their email, I'm going to block them from logging in. That's it for now. And I'm going to add a to-do comment here, add to FA check. So we're going to have that later. Great. And if you want to, you can add a comment here, prevent sign in without email verification. Right. So as I said before, you know, if you add more providers in your code, you have to check if this is still okay because I don't know what kind of providers exist. So right now I'm only requiring email verification on the credential provider, right? And I'm not requiring Google or GitHub. So if you're using this in production and you add some new provider, make a decision if you still want only the credentials to be checked for email verification. If not, you can go ahead and modify this logic to do the opposite. So you can do if account provider is equal to credentials. In that case, you can go ahead and do this thing, for example, something like that. But as I said, I know what is the structure of my app. I know which providers I use. So I want to allow everything to simply be able to log in, except if you're using the credentials, then I'm going to do a check on you to see if you have your email verified. So this is what should happen now. So go inside of your actions login right here. And for now, I'm going to comment this out, right? So I'm not going to generate a new token. So imagine that I didn't write this code where we block the user from our login function. So this is what's going to confirm to us. Now it should throw an error instead of this because we just modified that inside. There we go. You can see how it threw an error. That's exactly what we want. So even without this code in the login function, we have a fallback inside of out, which will never allow that kind of user to log in if they don't have their email verification. So that's why it's important to match the logic which you write inside of your, you know, be that a server action or an API route, match that inside of callbacks as much as you can, it's gonna highly improve the security of your application. Great. So now that we have all those systems in place, and of course, make sure that in the login, you actually bring this back, which if you commented it out with me. And what we have to do now is we have to set up our mail provider. So for that, I'm going to be using Resend, because it's extremely simple to set up, It has a very generous free tier and it's just, it will take us five minutes to do this for this tutorial. I'm going to give you some alternatives as well as if you want to for any reason, but most of them are more expensive to start with and, you know, require a credit card or something. But Resend doesn't require anything. You can just start using it and you can finish the entire tutorial just by using Resend. All right. So let's go ahead and let's do that. So head to resend.com or use the email in the description and go ahead and create an account. If this is your first time, you're going to get prompted to create a name. So I'm going to call this out tutorial and create a team. And there we go. Now let's go through this onboarding. So first, let's add an API key like that. Great. And what's great is you can already test whether it's working or not. So you should have, it should have the email you logged in with right here. You can see I created a new email just to test this out. And just go ahead and click send email. And if I check my inbox here, very quickly, it should appear. There we go. Can I hide this sidebar? I can't. Okay, there we go. Congrats on sending your first email from onboarding. Great. So now let's go ahead and let's go inside the docs right here. And let's use the Next.js quick start. And first they tell us to create an API key. So we already have that. So I'm keeping that right here. Don't close this tab yet. And now let's do an npm install recent. So I'm going to copy this. Let's go inside of our terminal here. I will close the Prisma Studio. and I'm going to run npm install resend like that. Let's see what is the next step. So we don't need any email templates here. I just want to see if, okay, so they use resend API key. So that's what I was looking for, the name for what to save this in. So let's go ahead inside of our environment variables after this has installed. And let's simply add the resend API key and copy the API key from your onboarding right here. there we go. Great. So now that we have the Resend API key, I just kind of have to explain one thing about Resend. So you can go ahead and find, you know, the free tier. And if you're interested in that, I'm not sure where it is. Is it settings here? There we go. Yeah. So you have 3000 free emails a month, 103 emails a day in the free tier right here. Right. But right now, you can only send emails to yourself, right? So to this email, which you can find in emails, you cannot find, you cannot send it to anyone else until you add your domain. So that's what you have to do. You have to add a DNS record to your domain. And no, you cannot use Vercel or Heroku or something like that. No, you need to own a domain. So good thing for tutorial. this is more than enough that you can send yourself emails. But if you want to share this with your friends or something and allow these emails to be sent to everyone, the easiest one is to simply add a domain. I'm going to show you how to do this exactly in the end of the tutorial after we deploy because this is for production only. You don't need to do this for development. Great. So just make sure that you have your API key here, right? If you cannot see it anymore, I don't know if you can see it. Yeah, let me just see edit API key. Yeah, if you accidentally lose your key, you can always click create a new API key, right? And just give it full access, of course. And then you can copy it again and just paste it inside of recent API key. Great, so what I wanna create now is my mail library. So let's go ahead and let's go inside of lib. And in here, create a file mail.ps like this. let's import resend from resend and let's define resend to be new resend which uses the process.environment.resend underscore api underscore key and confirm that it matches here so resend api key copy it from here paste it here make sure there is no typos and now let's export const send verification email here to be an asynchronous function, which has the email, which is a string and token, which is a string. Right, or I'm just going to collapse this like that. So we have the email and the token. And then inside of here, let's generate the reset link. Sorry, the confirm link is going to be HTTP localhost 3000 slash out slash new dash verification. And then we're going to add a question mark and we're going to add a param for the token. So let me just zoom out so you can see this in one line. And we're going to add that token at the end. So this is the important part. Well, everything is, but make sure it goes to slash out slash new dash verification. And make sure that you add a question mark and the param or a query token and then stringify the token which we are passing from the props here. So this is the confirmation link which we are going to email to the user and then we're going to create this new verification page and we're going to use it to detect whether the token has expired, whether it exists and if everything is okay we are simply going to change the user's email verified status in the database to a new date and of course we're going to have to change this later to something dynamic so it works in production. Otherwise, you know, your users are going to get local host emails, which won't make sense. But for now, this is good enough. And now what we have to do is send the email. And now we're going to see how incredibly simple that is with resend. So just resend.emails.send from, and in here, choose onboarding at recent.dev like this to email subject is going to be confirm your email and we're going to have HTML open backticks and you can just write HTML here so write a paragraph like this and I'm going to write click open an anchor tag. I forgot how to write anchor tags. Okay. In here, I'm going to write here and let's give this anchor tag an href to the confirm link. And in here, you can simply write to confirm email. There we go. That's it. That's all the code we need. So make sure that you have a little paragraph here, right, which starts here and ends here, then open an anchor tag, which has an href, open annotations, and add a confirmation link here. And now let's go ahead and use this send verification email inside of actions, register here, whoa, how did they get there? There we go, we have this to do right here. So let's go ahead and import that from our mail. So send verification email from at slash lib slash mail like this And let go ahead and remove the to do and use await send verification email And we expect two arguments email and then a token So first let pass in the email And then actually we can do this. We can use it from the verification token to ensure that it's the one that's in the database. So let's use verification token.email and verification token.actualtoken. Now let's try that out. So I'm going to go ahead and I'm going to create a new account here. So I'm going to call this verification, verification at mail. Actually, yeah, we need to use the actual, you need to use your email. So make sure that you use the email where you can send the email address to. So for me, that's this one, tutorial mailing. It should be written right here below, or you can click on emails. So make sure you do it with this email because no other emails will work until you add a domain, right? So I'm just gonna go ahead and copy this, copy right here. If you have an account already, just remove them from your database. So there we go, add an email that you can find here in Resend. Let's go ahead and create an account. And now I should be actually sending an email. So let's see if that's working. Confirmation email sent. And there we go, confirm your email. And if I click here, it should redirect me back to login because I'm not logged in and we didn't add this new route, which is the out new verification token. We did not add that to our routes, but it is officially working. Great. So now what I want to do is also send the verification email in the login page, sorry, in the login functionality. So let's go ahead and do that as well. So I'm going to go inside of actions, login here. And now let's go ahead and import the same thing. So I'm going to import send verification email from s slash lib mail. And in here, let's await send verification email. First argument is the email. And the second argument is the token itself like that. so now uh if i go ahead and log in using verification no sorry using the my email here which is tutorial mailing all right so i created an account right but i didn't verify my profile so if i try to log in now i should be blocked from logging in and instead a new email should be sent so let's try that out and there we go you can see how now i have two emails this is the old one and This is the new one a minute ago. And I can go ahead and click here again. Perfect. So we can officially send emails in our app. Great. So I'm going to end the module here. And the next one, we're going to create that new verification screen where we are actually going to verify the email. For now, if you want to, if you have a domain and you know how to add an DNS record, you can do that right now. If not, wait till the end of the tutorial when we get to deployment. And I'm going to show you how to do that. Great, great job. All right, so now what we have to do is we have to create the actual confirmation page so that the user can click on this link from their email and that that's gonna load and confirm the email here. But just before we go there, I feel like I didn't do a good job explaining inside of out.es here, we have this asynchronous callback sign in. And it seems like I kind of magically knew look at this provider here and put the credentials. So what I want to do is I want to console log the user and the account here so we can actually see what's going on inside and how I knew that I have to look for credentials here, right? So we use the sign in callback to first allow any OAuth logins, which is Google and GitHub to log in freely. But otherwise, if it's not allowed, so if it is credentials, in that case, we search for the user. And first we check if they have their email verified. And then later, we're also going to do two-factor authentication for credential users, because there's no need to do two-factor for Google and GitHub users, because Google and GitHub has their own two-factor authentication. So make sure that you add or you don't have to, you can just look at the video. So I added the console log in the sign-in callback to see what we get from the user and the account here. So I'm going to go ahead and attempt to log in using GitHub here. Let me just refresh this page to confirm everything is working. So I will try to log in with GitHub and I'm going to see inside of my terminal if I have any useful information from that callback here. And there we go. You can see that I have logged my user. There you can see my user information here. And you can also see the account information. And in here, you can see that the provider is GitHub, like this, right? So I could have also used the type. Maybe that's even better, right? But as I said, you know, there are many providers and not all of them are exactly, you know, reliable to not have their email verification. So now that we checked how this looks for OAuth providers, let me sign out and let me try and log in with another user. So I'm going to go ahead and open my NPX Prisma Studio just to see what users I have inside of my account here. Do I have any user which is already registered? There we go. I have this test user which has their email verified null and it should not have any account relation. So I know that this is email username and password registration. and I think I know what is the, I think I know what's the password for this one. So I'm also gonna go inside of actions login and for now, I'm just gonna comment out this code checking for email verification just because I want this to fail, right? I want this to prevent us and I wanna see this console log here. So I'm gonna prepare my terminal here. You don't have to do this, of course. I just wanna go ahead and show you how this looks. So if I log in using this account, there we go so I have an error here but if I scroll up there we go so we have my user here with the email with the email verified null and I have an account and you can see the type is credentials and provider is credentials as well so you could have used the type or the provider inside of here so we can now remove this console log so we could have also done user account.type is not credentials that would also work so that's where I got the provider value from if in case you were wondering about that. Great. So let me just revert this. So I want to make sure that this is now being sent here. Let me just go back inside of my code here. So I will just uncomment this out. So if I try this again, this should send an email. Well, it will not be able to send an email here because we have not set up a domain. Great. And one more thing I want to explain is the recent here. So I told you that you can only send emails to this email, which you logged in with, right? That is true until you add a domain. Once you add a domain, you can send emails to anyone. And you can also change where the emails come from. Because if you take a look at our mail service now, you can see that I have to specify that it's coming from onboarding at resend.dev. But once you add your domain, which we're going to do later when we do deployment, you will be able to modify this. So it sends from, you know, for example, code with antonia.com. It will be something like onboarding at code with antonia.com, right? So no, you're not stuck with this email. Don't worry. Great. So now what I want to do is I want to go inside of routes.cs. and what's happening right now is that if user tries to click on their confirmation email right here they get redirected back to the login page that's because they're trying to access a page called slash out slash new verification so you might be thinking all right we have to add that to the out routes and that could work yes but I also want to add this to public routes because remember, user will be able to change their email from their settings page. And if they are able to change their email from the settings page, that means that they are logged in. So the best way to the best place to put this route is inside of the public routes. So both logged in and logged out users can access this. So let's write slash out slash new dash verification like this. And make sure inside of your mail.ts, inside of lib folder mail.ts that you use that exact route. So slash out slash new verification. So it should match this exactly. And now if I try and go inside of my mail here and open this page, I should get a 404, which is a step towards while not being redirected. So now we have to actually build this page. So let's go ahead inside of the app folder out. Let's create a new folder, new verification. And inside, create a page.tsx. And let's call it new verification page. And a div new verification. So once I save this, there we go. We no longer have an error. And if I try again from mail here, there we go. You can see how it redirects me to new verification. And inside of my URL, I have a matching token, which I can find in my database. Great. So now what we have to do is we have to build the new verification form. So new verification form like this. If I save, of course, I'm going to get an error because that does not exist. So let's go inside of components out and create a new file, new verification dash form dot TSX. Let's mark this as use client and let's export const new verification form and let's return a div new verification form. Then we can go back to page.tsx and we can import that from components out new verification form. And there we go. We no longer have any errors. And now we are free to develop directly in the new verification form. So first things first, let's go ahead and let's import part wrapper from dot slash card wrapper or components out card wrapper. And let's go ahead and then replace this div with the card wrapper. And let's go ahead and give it a header label of\nconfirming your verification. And let's give it a back button, href of slash out slash login. And let's give it a back button label of back to login like this. Great. And now what I want to do inside is add a div here with a class name of flex items center with full and justify center like this. And inside, what we are going to do is we're going to add a little loader. So for that, we have to install a package called React Spinners. So I'm going to shut down my Prisma Studio and do npm install React Spinners like this. So make sure you add this package. And then we can import any loader you like. So you can Google React Spinners to see what kind of spinners they all have, but I really like the one called BitLoader here. So in here, you can now render the BitLoader like this. And there we go. You can see how this is going to look like. So when a user clicks from their email here, it's going to start loading and automatically verifying this URL link using the token, which we have inside of our URL. Just a quick tip. If you don't have the token inside of your URL, confirm inside of your libmail in send verification email that you have a token as the parameter that you properly assign it to the URL, and then find where you use the send verification email. So we use it inside of login and register. So in here, ensure that your first parameter is the email, the second parameter is the token, and the same thing in the login function. The first parameter is the email, and the second parameter is the token. Great. So let's go back the new verification form, which we are working in. It is located in components out new verification form here. What I want to do now is I want to import use search params from next navigation. And let's go ahead and add in here. So search params from use search params. Of course, make sure that you've marked this as use client so you can use hooks inside. And then let's get the token using search params dot get token. So how do I know that I need to get the token exactly? Because of my URL. So you can see that this token value is stored inside of a token query right here. So that's how I can get my token. Great, so what we have to do now is we have to write an on submit function. And we're actually gonna wrap this inside of a use callback because we're gonna put it inside of a use effect. So let's go ahead and import use callback from React. And let's replace this on submit to use callback. Add an empty dependency array. And in here for now, we can simply console log the token and add a token to the dependency array. Now let's import useEffect from React. And let's call useEffect here. and inside of use effect we are simply going to call on submit once like that and now if you go ahead and open your console log you can see the token which is stored inside of my url being logged here and you're probably noticing that it's being logged two times you don't have to worry about that that is only because of i believe it's called react strict mode which in development calls every every use effect twice. So in reality, in production, this is only going to be called once, right? And I believe we might even get some bug here, because in this on submit, we're going to check if the token has expired. But once we confirm it the first time, it's going to be expired. So then when it triggers another time, we're going to get a little error here. But that's only going to happen in development, not in production. Because in development, React calls use effect twice. So I just want to bring that to your attention. So if you're seeing it twice, that's completely fine. I'd have that as well. Great. So now what we have to do is we have to actually create a functionality to call the verification. So we're going to call that new verification action. So let's go inside of actions and create a new file, new verification.ts. let's go ahead and mark this as use server at the top here and let's import the database from s slash lib database let's import get user by email from data user and let's import get verification token uh not by token because sorry get verification token by token like this from data verification token. And I'm just noticing now that I have a little misspell here. So let me go ahead and quickly resolve that. So you can check if you have that as well. So verification is spelled V-E-R-I-F-I. And I think inside of my data folder, I misspelled it V-E-R-F-I. So I have to rename this to verification by adding a little I here. I just don't like typos in my code. And now I have to look throughout my project and see where I am misspelling it. So let's see. I believe this is just some cache. This should be completely fine. So when you rename your file, you have to go through all of your other files to see if they are still using it properly. So I'm going to search for get verification token here. All right, so I use it inside of verification token data. All right, I use it inside of tokens here. There we go. You can see how I have an error here because I renamed my import. So go inside of lib tokens. And in here, I have to rename this to be verification. There we go. So we resolve that. Let me refresh this page. All right. I think that's the only place where we have it. Get verification token is inside of tokens. And here where we are working in the new action. So actions, new verification. I have to rename this as well to verification token. There we go. So no more errors inside of my code. Great. So inside of here, let's export const new verification. Let's make it an asynchronous function, which simply accepts the token, which is a string. Let's get the existing token using await, get verification token by token and pass in the token. if there is no existing token in the database we are going to return an error saying token does not exist meaning there is no way we can verify your email and now let's check if token has expired by using new date existing token dot expires like that is smaller than new date as of now and if it has expired in that case let's go ahead and return an error token has expired like this. And then let's go ahead and find the user we are supposed to validate using this token. So await get user by email is going to be existing token dot email. If there is no existing user in our database connected to that email, that could mean that the user changed their email in the meantime somehow. So let's just return an error. User does not exist. Or maybe more specifically, email does not exist in our database. And if all of those tests above have passed, we can finally do await database dot user dot update. And in here, let's use where ID is matching to existing user dot ID. And the data we are going to update is that email verified is new date. But also one more thing that we are going to update is the email to be existing token dot email. So why am I doing this as well? Why do we need this update? Well, during the registration process, it is not needed. But we are going to reuse this new verification server action for whenever user wants to modify their email, right? So when we create the settings page and the user adds a new email, we are not going to immediately update their email in the database. We are simply going to create a token with that new email and send an email to that email. And then when they confirm it, we are going to update the email value inside of their database. So that's why we need to do this as well. So in registration process, this is gonna be exactly the same as it was before. So that's why maybe this isn't making sense to you. But keep in mind that we are going to reuse that for when user wants to change their email, right? So that's why we have to update the email here as well. Great. And then finally, we can go ahead and remove the verification token. So verification token.delete where ID is existing token.id. As simple as that. And let's return success email verified. there we go. So we have our server action finished. Now we can go back inside of the new verification form inside of our components out new verification form right here. And let's go ahead and let's import that action. So import new verification from actions new verification here. And let's go ahead and call the new verification here on the on submit. So I'm going to do a new verification pass in the token, which we have inside of here like that. And of course, we can go ahead and check here immediately if we have the token or not. So if we don't have the token, we can break the function immediately like this. Well, that's actually not what we're going to do. What we're going to do is we're going to throw an error, right? So let's do that. So let's import useState here. And let's go ahead and let's define error to be setError from useState. And let's give it a value of string or undefined. And let's do the same thing for a success message. So setSuccess, like that. So in here, if we don't have a token and we try to submit, we're simply going to do setError missingToken. and we're going to break the function then like that and then instead of new verification let's do dot then and let's get the data and in here let's set success to be data dot success and let's set error to be data dot error like that and let's also add one dot catch in case something breaks and let manually do set error to be something went wrong Like this And what we have to do now is actually render the errors So let's import our form error from dot dot slash form error or components. I think it's just components like that. And let's do the same thing for form success from form success. Great. And now let's go ahead and render those. so here at the bottom below the beat loader let's add form success to have a message of success and form error to have a message of error and there we go you can see how it says the token has expired right so you should get this message if you use the old verification token you can see I sent this 17 hours ago. So obviously it has expired by now. But you can see how I'm still showing the loading indicator, even though I got an error. And here's another thing you can test. Inside of your URL, remove the token completely. And now you're going to get an error missing token, right? So choose one error, at least for you to have here. And let's go ahead and modify this now. So we are only going to show the bit loader if there is no success. and if there is no error. So if it's a completely empty slate, there we go. So you can see how now the loader no longer shows. But if I refresh, it will only show during the actual server action call that we are doing right here, new verification. So on the use effect, the moment the component mounts, we call on submit, which then in return calls the new verification if it has a token inside of search params here. Great. And now what I want to do is I want to also modify this form errors and form success. But let's go ahead and try and create an actual new token. So for that, I'm going to go ahead and go inside of my Prisma Studio. And this is the email I can send my emails to. So confirm inside of your resend that you're using the email where you can send emails to. And let me just close everything I don't need here. So go inside of your app. and try to log in again, right? And this should send you a new email, or you can clear a database and create a new account with this email. And there we go, confirmation email sent. So now I should be getting a new email. There we go. So when I click here, let's see if that's going to work. Email verified. And then we have an error. Token does not exist, right? So that's the thing that I was telling you about. That's because our use effect fires twice, right? So on the second iteration, it's looking at an already confirmed token, which we removed from the database. But still, I kind of want to cover this case just in case, you know, it can happen in production, even though I don't think this can happen in production because it won't fire twice. But we're going to do our best to cover that as well. And here's a fun thing. Let me just check if I'm now running my Prisma Studio. I think I shut it down. So let me open it again, npx Prisma Studio. So in here, if I look at my user and let's find it by this email, so tutorial mailing and look at this, we have officially verified our email, which means that if I try and log in again, I will be allowed to log in. Let's see. And there we go. I am officially confirmed and I can log in inside of my account. Perfect. So let's just try one more time to resolve that little bug that we have. so I'm gonna do the following I'm gonna go inside of my Prisma Studio and let me just remove all the other users so I just want to focus on the one where I can send my emails to this one and what I'm gonna do let me just refresh this again there we go so what I'm gonna do is I'm gonna remove my email verified property like this leave it at null and click save change or simply remove it completely and just register again, if that's easier. And now if I go ahead and try and log in again, I will be sent a new verification email. But before we click on it and verify our email again, let's try and protect our little app here from that happening again. So the first thing I'm gonna do is go inside of the on submit here. And what I'm gonna do is if we already have a success message or if we already have an error message, simply break the function, right? So then we also have to add those to the dependency array. So let's add success and error here. And let's go ahead inside of here. And let's only show the error if we never had success in the first place, like this. And I think that this should improve the behavior a bit. So I cannot log in, but I got a new email here. There we go, confirm your email. and let's see if this will be stuck at email verified. Oh, so it's still showing token does not exist. But as I said, yeah, I think there's kind of no way around this at the moment. You can play around if you want to. But as I'm saying, this only happens because you're calling use effect twice, right? And I believe that it won't even listen to this logic because it's in some special react state where it's being called twice with the exact same values. I don't think it even considers this new values being changed. So I think that's why this is happening. But as I said, in production, we're going to test it, of course, later. I think this will not happen at all. And you're just going to see a message saying email verified. So if I try this again, what was my email? Tutorial mailing, Gmail. and if I log in again, I should be able to log in. There we go. Perfect. So we successfully verified our email. If I refresh here, I'm going to get email verified to the new date. Perfect. So let's just recap our entire flow. When the user registers using the register server action, we generate a new verification token using the email they used to register. then we send the email to that we send the verification token to that email inside of the verification inside of the email we are using a route called out new verification which we added to the public routes then inside of our app out we have that new verification page which renders the new verification form located in components out verification form inside of here we use the search params to search for the token, which was appended inside of our mail function, send verification email. So the second argument was token. And in here we append that token. So we check if we can find that token in the URL. If on the on submit, which fires automatically in the use effect, the moment the component loads is missing, we return an error, token is missing. Otherwise, we call the new verification server action, or you know, in your case, this can be an API route as well. It really doesn't matter. Instead of new verification, we send that token from the URL. We search the token using the function get verification token by token, which we defined in here. So we use the find unique where token matches the token. If we cannot find the token in our database, we return an error. Token does not exist. Second thing we do is we check if an hour has passed from the creation of the token. If an hour has passed, we return token has expired. Otherwise, if it has not expired, we check for the user, which this token was intended for using the email. So get user by email. If the user doesn't exist, which you know, it can happen, there are edge cases. In that case, we throw an error, the email for this token, which you're trying to verify does not exist. There's nothing we can do here. Otherwise, if we can find the user, we update the user using that user's ID, we change the email verified to be the new date, meaning today I verified this. And we also modify the email to be whatever was stored in the verification token. Because remember, this exact logic is going to be later reused when we add the settings page to change to a new email. So that's why we also have to update email in here and not in the settings, because we don't want to immediately change the user's email. Otherwise, they could use anyone's email for that, right? That's not good. We have to ensure that they can verify the email that they're trying to change. And last thing we do is we remove the old verification token. No need to store that inside of our database anymore. And we return a success message, email verified. And all of that is reflected inside of this dot then. We also have an additional catch in case something goes wrong, which is out of our control here. And we have a little loader here. We have a success and we have an error. And yeah, we have that little bug, which as I say, I'm pretty confident it only happens in development because of React strict mode. You can see that it's completely ignoring the fact that we already set a success message there. But yeah, I tried to fix it, but you can see that it's not working. We're going to test that again in production as well. Great, great job. What we're going to do next is a functionality to reset our password. So we currently don't have that little question here. So we're going to add a little question, forgot your password. And then we're going to do a very similar logic. We're going to send user an email and we're going to allow them to add a new password if they have the correct token inside of their URL. So let's go ahead and create the forgot password functionality. So for that, I want to go back inside of my components out login form right here. And in here, we have to find our password input. So this one right here, which has the name password and the input, which has the type password. And outside of form control here, we're going to add a button component, which we already have imported inside of components UI button. And now inside of here, let's go ahead and add a link component, which we can import from next slash link. So make sure that you add this import. I'm going to move it up here with the other imports like that. And now let's go ahead and give this link an href of slash out slash reset. And then let's go ahead and write forgot password here. And let's go ahead and give this button a size of small. Let's go ahead and give it a variant of link. And let's give it as child option, so it properly uses the link inside. And let also give it a class name of px0 so it is aligned with the input here And let also give it font normal So it's not bold like that. And now when the user tries to click here, nothing will happen because they are immediately redirected back to the login page. So what we have to do is we have to add slash out slash reset inside of our routes. So let's find the routes.ts here. And we're going to add them to the out routes. So go ahead and add slash out slash reset to the out routes array. And now if I click for the password, I should be redirected. Let me go ahead and try this again. There we go. So refresh your page and you should be redirected to a 404 page. And your URL should be slash out slash reset. So now let's go ahead and let's create that page. So I'm going to go inside of my app folder out and let's create a new folder called reset. And inside a page.dsx with a default export of reset page and a div reset page. And there we go. We now no longer have that error. And we can click on forgot password here, which redirects us to here. Perfect. So now, obviously, instead of this div, we're going to create a reset form like that. and we can reuse a lot of elements from our login form. So I wanna copy that one. So let me close everything and we will remember that we have that error inside of our reset page. Let's go inside of components out and let's copy the login form in the out and let's rename it to reset form like this. Make sure that you are inside of a reset form. So confirm right here that your reset form is open. First things first, let's go ahead and rename this to reset form like this. And then let's go ahead and let's change the card wrapper here to ask Sass forgot your password. The back button label, let's change that to go back to login. And the back button href is gonna go back to login like this. and now we can go back inside of the app function sorry app folder out reset and we can import the reset form from components out reset form just make sure that you have renamed the export constant to reset form here and it should look exactly the same as the login functionality so now we're going to go ahead and modify it so let's go ahead and let's start by hiding socials right so I'm going to remove the show social prop. And there we go. That's going to remove the socials because that's not something we want to show here. Now what I want to do is I want to remove this use search params. So we're no longer going to need that error. So remove the use search params. And now we have an error here. So remove this URL error constant. Now we have to go down and find the form error and remove the usage of URL error like that. And now we can also remove the imports we no longer need, which is use search params. So let's go ahead and remove that as well. Great. And now the only thing we have to have for our inputs is the email. So the user will enter the email for the account they forgot the password for. So we can find the actual password form field and completely remove that. So find the end of the self-closing tag like this and simply remove it. So you should only have the email field inside like this. Great. Let's change the login button to write send a reset email here like this. There we go. So this is already looking a lot better. Perfect. So what I want to go now is I want to go back inside of my schemas. So let's go inside of the schemas folder, index.ts right here. And let's copy and paste an existing schema, like login schema here. And let's just add an email and call this reset schema. So just an email with a message email is required, like that. Now let's go back inside of reset form, inside of components out the reset form. And let's go ahead and we can remove this link, it seems. We no longer need that. and let's import reset schema from schemas here. So now we can fix all the errors where we are using the login schema. So this is gonna be a reset schema. This is gonna be a reset schema and this is gonna be a reset schema. And as you can see, we have an error for the password field in the default values because we don't need it for this one right here. And obviously we have some issues with our on submit function because it's calling the login server action. We're not gonna use that server action. And we can call the other one. Perfect. So for now, what we can do is log, comment this out. And instead, let's console log our values here. And let's try this out. So I'm going to open inspect element. And I'm going to send this to tutorialmailing at gmail.com. So we're going to have to use that email from resend, right? Because we're going to be sending some emails now. And there we go. When I click submit, inside of my own submit, I have the proper values of my email here and nothing else. Perfect. And let's try back to login. Forgot password. There we go. Both are working. Perfect. So what we have to do now is we have to, well, you can bring this back and we have to create an equivalent action to actually reset the password. So let's go inside of our actions and let's create a new action reset. dot ts. Remember to mark it as use server. And let's go ahead and import reset schema from our schemas. Let's go ahead and let's import get user by email from data user. And let's go ahead and import everything as z from zod so we can do server side validation from here as well. great and now export const reset function to accept the let's make it asynchronous value which accepts the values which are type of z dot infer type of reset schema and then inside let's validate our fields so validated fields are reset schema save parse and pass in the values which will come from the props. If we don't get a success message, so if exclamation point validated fields dot success, return an error, invalid email. So server side validation in case they bypass the front end side validation. And then we can safely extract the email from validated fields dot data. and now let's find the existing user using a way to get user by email which we imported and pass in the email so make sure that you import the get user by email from data user and if there is no existing user let's go ahead and return an error which is very simply going to say email not found like that. Otherwise, we can return a success message, reset email sent. Obviously, it's not here yet. So we're going to add a little to do generate token and send email. There we go. So now we have our reset function. So we can go back inside of components out and find the reset form here. Let's find where we import the login action. And this time, let's use the reset action. So So import reset from actions reset. And let's use the reset and send the values. And there we go. No more errors inside of our component. So let's try this out by adding a random email which doesn't exist inside of our database. And that should throw an error that the user, well, the email is not found. So we cannot reset a password for this one. But if I try and use my existing password here, I should get a success message. reset email sent like this. Great. So what we have to do now is we have to visit our schema Prisma and we have to create our password reset token. So let's go ahead and do that. So I'm going to go inside of Prisma schema.prisma and let's add a model password reset token. is going to have an ID, which is a type of string at ID and the default value of CUID. Let's give it an email of string. Let's give it a token of string and a unique value. And let's go ahead and give it an expires of date time and a unique combination of email and token like that. So very similar to our verification token. It has an ID, it has an email token, expires and unique. Yeah. And don't reuse the verification token, right? If you're thinking, oh, we could have just reused it. It's simply safer to keep tokens separate, right? Security wise, it's better that you have an exact model you can look up and know this is for password reset. This is for email verification, even if they're exactly the same. So what we have to do now is we have to create utils to fetch these from the database. But before we can do that, we actually have to shut down our application. So let's shut down Prisma Studio. Let's go ahead and let's shut down npm run dev. Let's do npx Prisma generate. So we add those new fields to node modules and npx Prisma database push. So we add them to our neon database right here. once that is finished we can go ahead and run npm run dev again refresh your local host to ensure nothing breaks and now let's close the prisma schema let's go inside of the data folder and create a new file password reset token.ds let's go ahead and import the database from at slash lib database. Let's export const get password reset token by token to be an asynchronous function which accepts the token which is a string. Inside of here open a try and catch block. In the catch we're simply going to return null and in here let's get the password token to be await database reset token which we just created find unique and where matches the token like that If you getting an error for db token it can mean two things. You didn't run npxprisma generate, or in your schema, you didn't name the model correctly, or if you did both, then go ahead and shut down Visual Studio Code and open it up again, or use command shift P or control shift P and type in reload window right here. And that is the equivalent of shutting down your Visual Studio code and starting it again. And in here, let's just return the password reset token. Let's call this properly password reset token like this. Great. And now let's go ahead and copy and paste this. And this one will be named get password reset token by email. And it's going to accept the email prop. And it's going to use find first to get the matching email and everything else can stay exactly the same. Great. So now what we have to do is we have to go back inside of our tokens lib. So let's go inside of lib tokens. So we have generate verification token. Now let's go ahead and first let's import get password reset token by email from data password reset token, which we just created. And let's go ahead and write the, whoops, and write the function for this. So export const generate password reset token, to be an asynchronous function, which accepts the email, which will be passed from this page right here using our server action. and then let's go ahead and generate the token using uuid v4 so the same thing we are doing above let's you can copy and paste the expires from the one above so it will expire in one hour and let's get the existing token using await get password reset token by email and passing the email from the prop like that if we have the existing token in that case let's await database password reset token dot delete. And let's delete the one which has a matching ID of the existing token dot ID, which we just found using the email that was sent, meaning that the user already has a password reset token, but they are requesting a new one. And finally, let's create a password reset token here using await database password reset token dot create. Let's give it a data or email token and expires. So we have all the necessary information and simply return password reset token like that. Perfect. So now we have the ability to fetch the password reset token and the ability to generate a password reset token, which is carefully going to look if we already have an existing one. So we remove it on time. And the last thing we have to do is we have to revisit our mail library. So in the lib, let's go to mail here, and it's going to be very similar. So, well, let's write it again just to ensure our knowledge here. So export const send password reset email is going to be an asynchronous function, which accepts the email, which is a type of string, and a token, which is a type of string. and in here let's write the reset link to the http localhost 3000 slash out slash new password so this is a new route which we're gonna have to create later of course and the same thing we're passing the token and stringify the token like that and then await resend emails sent like this from onboarding at recent.dev to the email subject. The subject is going to be, subject is going to be reset your password and HTML. We can copy from below. So click here and instead of confirm your email, it's going to be to reset your password. So click here and instead of confirm link, it's going to be reset link. And instead of the confirm email, it's going to be to reset password. Like that. There we go. So we have everything we need. Make sure you don't have any typos. Make sure that this exactly matches what you've been sending before. Later, when we do deployment, we can change this to go from whatever website or domain you want in production. And make sure that when you're testing this, you are sending this to that email which you have in resent. Great. So now we can finally go back inside of our reset server action. So actions reset here. And let's go ahead and let's import. Send password reset email from libmail and generate password reset token from lib tokens. And in here we have a little to do which we wrote. So let's go ahead and write const password reset token is await generate await generate password reset token using the email which was entered in the form and then await send reset send password reset email. and in here let's use password reset token dot email as the first argument and password reset token dot token as the second argument and whenever you do this always double check that the first parameter is actually an email and the second one is a token so you don't have any unexpected bugs and I believe this should already be working so make sure that you enter the email which you can news from recent right here. Let me copy this and go inside of this button, which we have right here for got password, make sure that your app is running. Let's send this right here. And let's click resend, send reset email. And that should actually be sending me an email now. So let's just wait a second. There we go. Reset email sent. And let's look at my inbox here. There we go. reset your password. Obviously, if I click here, it's just going to bring me back to login because we have to add that page. And here's one more thing that we can check. So I think my Prisma is not running. So let's go ahead and run our NPX Prisma Studio. And you can now see that we also have a password reset token, which was generated and which was sent to that email. So this is the token that is going to be inside of our URL being sent to this email. Perfect. So we just finished the first step, which was sending the email for a new password. The next step will be to create this page, which we defined inside of libmail called out new password. And in here is going to be a very simple form, which is going to, well, take in the new password that the user wants, and also use this token to confirm that they have permissions to change their password. Great, great job. So the first thing we have to do is we have to enable this link, click here to reset password. Right now it redirects us back to login. So let's go ahead inside of our routes.ts file right here. And inside of our routes, we have to add that new link. So let's add slash out slash new dash password like that. So that's only going to be available for logged out users because logged in users will have the settings to change their password. So it's different from this new verification where it can be used both by logged in and logged out users. That's why this is in public route. So it can be accessed if the user is already logged in. Great. So just confirm that this slash out new password is exactly the same as you're sending inside of lib mail right here. So send password reset email should use slash out slash new password and then append a token query here. So make sure that this exactly matches what you just wrote inside of this new out routes. And then if you click inside of your mail here, it should lead you to a 404 page, but it should not redirect you. Great. Now let's go ahead and let's go inside of the app folder out and let's create a new folder, new password. Then inside, create a new file, page.tsx, and export default new password page, and a div new password. And there we go. We now have a new password page. Perfect. So obviously, as always, we're now going to go ahead and change this to use the new password form. and now let's go ahead and let's copy the existing, which one should we use? How about we copy inside of components out? Let's copy the reset form. I believe that one is the simplest. So copy the reset form, paste it inside of out, rename the copy to new password form. Make sure that you are inside new password form so you can close the other one. So confirm you're inside new password form. And in here, go ahead and rename this to new password form like that. And then you can go back inside of app auth new password where you have this error. And let's import new password form from components new password form. And it will look exactly the same as the one we just created for the resetting of the password. So now what we have to do is we have to go ahead and modify it. So first thing that I want to do is go inside of my schemas right here in the index. Let's copy this schema and let's rename it to new password schema. And let's change this to be a password. And let's go ahead and give this a value of minimum of six. and then the message will be minimum of six characters required or what did we write before minimum six characters required all right exactly that so just the password field for the new password schema like that now let's go back into the new password form and let's import instead of reset schema new password schema so we're gonna get errors wherever we are using the old one so in here, in here, and in here. Let's replace that with new password schema. And now we have an error for the default values. So that should be the password, of course.\nAnd inside of the on submit, yes, we have an error. We're going to fix that later. So now what I want to do is I want to modify the card wrapper here. So the header label instead is going to be enter a new password. And this can stay the same back to login. And we don't need to show socials here either. Inside of the form field, let's change the name for this one to be password. Let's give this a form label to be password as well. and let's go ahead and change the type to be password and the placeholder let's just give it five stars actually six stars like that and let's change the submit button to say a reset password like this there we go what we have to do now is we have to fetch the token from our url so inside of your URL, you should have the token if you clicked here from the reset password. So your token should be appended just like it was for the verification. If it's not, go ahead and double check inside of your mail that you are sending send password reset email with the token appended and confirm in the reset action when you await send password reset token that you do a proper order of email and token. If you want to, you can also add a console log of the password reset token to see if something is going wrong inside of it and then debug from there. Great. So let's go ahead and let's import use search params from next navigation like that. And let's go ahead and add it here. So const search params is use search params like this. And then we can extract the token to be search params dot get token. And we already know why it's token is because it's inside of our URL. So just double check that you didn't misspell that inside of your URL, it should be token like this. Great. So now we have our token here. And now what we have to do is we have to create a new server action to handle this. So let's go inside of actions, new one, new password.ts, like that. Mark it as use server. And let's go ahead and export const new password to be an asynchronous function, which accepts the values, which are a type of import everything as z from zod. So we need that. And let's import new password schema from schemas. So then we can write values is a type of z.infer type of new password schema. And let me just not misspell type of. And we're also going to pass the token from the URL, which can be stringed or null like that. And immediately, if we don't have a token, let's go ahead and return an error, missing token like this. Great. Now we can use this new password in place of our new password form. So instead of components out, we have a new new password form here. And instead of using a reset action, let's use new password import and let's import new password. And then we can replace the onSubmit function to use the new password. And besides sending values, as you can see, it also accepts the token. So add a comma here and pass in the token, which we extract right here from the search params like this. Perfect. And we're going to fix this in a second. So now I want to go ahead back inside of the new password here. And let's go ahead and let's validate our fields. So const validated fields is going to be new password schema that save parse, pass in the values. If we didn't get a success from validated fields. So just make sure you put an exclamation point here. Let's go ahead and return error invalid fields. Like that. And then we can extract the password from validated fields.data. And now let's do some token validation here. So const existing token is going to be await get password reset token by token. So make sure that you import the get password reset token from data password reset token. And then we can use our prop token, which we pass in as the second argument here. And we can see if we can find that token inside of our database. So if there is no existing token, we're going to return an error, which is simply saying invalid token. like this. And now let's go ahead and check if the token has expired. That's going to be a wait, get, sorry, no, that's going to be new date, right? Existing token.expired is less than the current new date. So if it has expired, we're going to return an error. Token has expired like that. and it's not dot expired it's dot expires like this and let's now go ahead and check the existing user which matches the password we were trying to reset for so await get user by email from data user so make sure you import get user by email from data user we're going to use uh we're going use existing token dot email like this. If there is no existing user, let's go ahead and return an error saying user. Actually, email does not exist. So for whatever email we sent this token to reset the password, no longer matches what we have in our database. Great. And now what we can do is we can finally hash the new password and update the user. So for that, we're going to need to import bcrypt. So you can use either bcrypt or you can bcrypt or you can use bcrypt.js. I'm going to use js because I found to have less problems with that. So let's go ahead and let's hash the password in here. So const hashed password is going to be await bcrypt.hash password, which we pass from our fields, and the salt is going to be 10. And then await database. So make sure you import database from lib database.user.update where id is existing user.id and data is simply going to update the password to be the new hashed password and then await database.password reset token dot delete where ID matches the existing token dot ID. And finally, we can return a success saying password updated. There we go. And because we added this success here, we no longer have that error here for data dot success. So if I'm correct, this should be working just fine, but I believe my token might have expired already. So here's what I'm going to do first. First, let's clear our URL. So let's just try what happens if I do new password without any token at all. So technically, we could already, you know, redirect the user from here, but I want to see explicitly what happens if we try to reset a password. There we go, missing token. So we are not allowing this to reset any password. Great. And now let's go ahead and let's use the email from Resend to send a new email here. So let's copy this. This one, tutorial mailing, send reset email. And this should send me a new email any second. Let's go ahead and check my inbox. There we go. Reset your password. And when I click here, it asking me for a new password. So my previous password was 123456. So this time I'm going to try 654321. Let me just confirm that I entered that correctly. Six, five, four, three, two, one. I will click reset password. And that should just tell me that I have successfully resetted my password. There we go. So if I go back to login now, and if I try this with my old password, one, two, three, four, five, six, like this, it should tell me invalid credentials. Perfect. But if I try six, five, four, three, two, one, it should allow me to log in because I already have this email verified. regardless if you have the email verified or not, right? You will not even get that message otherwise. Perfect. So we successfully implemented the forgot password functionality inside of our application. And I just want to confirm directly in my Prisma Studio that this is happening. So I'm gonna close everything besides my user here. And let's go ahead. I just wanna show the password. I don't want anything else like this. So I just want to take a look at the hashed password here. You can see how it looks now. So what I'm going to do, let me just, I have too many tabs open. So what I'm going to do now is click forgot password. Again, I'm going to send another image, another message to this. So send the reset email. And there we go. So this one took a bit longer for me, but still it successfully sent the reset email. And there we go. I have a new password here. So let me click this link. And now we're going to focus on the password hash right here and see if we can notice it changed. So now my password is 654321. So I'm going to change this to, I don't know, a new password like that and click reset password. And once this succeeds, let's go ahead and refresh our password hash. I don't know if we're going to be able to notice any change here. I'm not sure. There we go. you can see how it clearly changed to a new hash. Perfect. So it's officially updated in the database. And if I try this again with my old password, 654321, I should get invalid credentials. But if I try new password, I should be logged in inside of my application. There we go. We successfully implemented the reset password functionality. The last thing that we have to do regarding this login screen right here is two-factor authentication. Great, great job. All right, so to implement two-factor authentication, let's go ahead and let visit our Prisma schema right here and let find the user model here And let go ahead and add a new field called is two factor enabled That going to be a type of Boolean with the default value of false. So this is going to be used to decide whether to send the user the two factor token when they try to log in. And another field we're going to create is a relation with two factor confirmation model, which we are going to create. So two factor confirmation is going to be a two factor confirmation model relation, and it's going to be optional. So put a little question mark at the end. And now we have to create this model. But before we do that, let's create the two factor token, which will be sent to the user's email. So this one will be exactly the same as our password reset token and our verification token, I just want to keep them in separate models. So let's call this one two factor token. It's going to have an ID of string and a default value of CUID. Email, which is a string token, which is a unique string and expires, which is a date time and a unique combination of email and token. And now let's create the two factor confirmation model. So model two factor confirmation is going to have an ID of string ID and a default of CUID. and it's going to have a relation with the user ID. So that's going to be a string and a user is going to be a type of user, relation, fields, user ID, references ID and let's add on delete to cascade. So if the user model gets deleted, we're going to go ahead and also delete this two-factor confirmation if it exists. And let's also go ahead and add at unique. user ID like that. There we go. So make sure that you add the user relation here with the user, which we have not here, right here. So we have two factor confirmation to this model. Yeah. And make sure you add the unique. Otherwise you can see that we are getting a one-to-one relation must be unique field or something else like that. So unique user ID, or I believe you can also add unique here. I think it's the same thing. Yeah, but I'm going to use this method here. Great. So make sure you add this. And now let's go ahead and let's reset our database. So everything is clear. So shut down the app. Let's run npxprisma generate. So we add those new models inside of our node modules. And then let's do npxprisma migrate reset to remove everything inside of our database here. And let's confirm this like that. And then let's go ahead and run npxprisma database push like this. So this will push the new models inside of Neon database. And after you've done that, you can do npm run dev. And if you want to, you can also do command shift p and reload your Visual Studio Code window, so there's no any cache. Then refresh your localhost here, and it should still work just fine. And let's also prepare the Prisma Studio in another terminal, so mpx Prisma Studio. That's going to open it in localhost 5555, and let me just enable all the fields to be visible. And there we go. You can see how I now have is two-factor enabled and two-factor confirmation here. I'm going to go ahead and create a new account using my tutorial name. Mailing at gmail.com. So use the mail, which can send the email tutorial mailing gmail.com. One, two, three, four, five, six. I'm going to create an account. So we're just testing whether the rest of the rest of our application is working fine. There we go. So confirmation email was sent. there we go, confirm your email, I'm going to click here, and that's going to confirm my verification, there we go, this is the little bug that we have, but only in development, and now I believe if I go ahead and use tutorialmailing at gmail.com here, 1, 2, 3, 4, 5, 6, and click log in, I should be able to log in just fine, great, and now let's go ahead inside of my Prisma Studio and let's refresh this user. And by default, I should now have a value of is two-factor enabled false. And I can only change it to true. And we have the potential relation with two-factor confirmation, which right now you can see does not exist. Great. So now what we have to do is we have to create the tokens, the data, and the email for two-factor token right here. So let's go ahead and do that. So let's go and close everything. Let's go inside of data and create a new file to factor token.ds. Let's import the database from s slash lib database and let's export const get to factor token by token to be an asynchronous function which accepts the token which is a string. Let's open a try and catch block in the cache we're going to return null and in here let's get the two-factor token by using await database two-factor two-factor token not confirmation but find unique where we have a matching token and return to factor token like that and then let's copy and paste this let's rename this one to be get two factor token by email. And in here, let's get the asynchronous email and let's use find first and get the matching email like this. There we go. Great. Now let's go ahead and also create a data for two factor confirmation. So two factor confirmation.ts. This one is going to be a little bit different, a little bit simpler. So it's only going to have one method called get to factor confirmation by user ID. So it's going to be an asynchronous function, which accepts the user ID, which is a type of string. Let's open a try and catch block here and return null in the catch. and in here let's get two-factor confirmation by using await database.two-factor confirmation find unique. Let me see if I can expand this even further. Okay, basically in one line like this find unique where we have a matching user ID. So no need to use the email or the token here because in our schema we have an actual relation with the user. So we know this confirmation is tied to the user. So we can use the user ID directly. And let's return two-factor confirmation like that. That's all we need for two-factor confirmation here. Now let's go ahead and let's go and create a lib to generate a two-factor token. So for that, we're going to need a package called crypto. And I believe it already exists in the node ecosystem. So you won't need to install anything. At least I don't. So I can just do import crypto from crypto. You can see how it auto completes and I didn't need to install any package. So if you take a look at my package JSON, I don't have crypto anywhere installed, right? So you should just have this in by default. And let me just import crypto actually. Great. And now let's go ahead and let's also import get to factor token by email. So make sure you add that, which we just created a second ago. And let's go, well, we can do it at the top actually here. So export const generate two factor token is going to be an asynchronous function, which again accepts the email to where we send the actual token. And in here, let's go ahead and generate the token. But this one is going to be a little bit different because I want it to be a six digit number. So we're going to use crypto.randominteger. And in here, we're going to write 100,000. And here's a little tip for you. When writing numbers in JavaScript, if you're having a hard time, you know, seeing how many zeros they are, you can add underscore like that. And this is still going to be the exact same thing if we wrote 100,000 directly. So this is the same thing, but it's easier to see what it is. So 100,000. So this is going to be the start of our range. And the end of our range is going to be 1 million. Like that. To string. And let's write the expires. So it's going to be new date. New date. Get time. Plus 3600 times 8000. So it's going to expire in an hour. let's see if we can find an existing token by using await get to factor token by email and let's use the email which is passed in the props here if we have an existing token in that case await database.tofactor token so not confirmation but token be careful dot delete where we have an id which matches the existing token.id. And then we can finally create a new two-factor token. So const two-factor token is going to be await database two-factor token.create data. And inside, we can safely pass the email for the user to send the token, which is going to be a six-digit code and expires for when that code expires like that. And let's return to factor token. So if you want to, you know, you can modify the expires for two factor token. I think it's fine for the password reset token and the verification to last an hour. But if you want to, you can use some different math here to make it maybe 15 minutes rather than an hour if this is too long for you, right? But for the tutorial, I'm just gonna use an hour. But let me add a little to do here. later change to 15 minutes just so you get reminded that you know if you actually plan on doing stuff like this in production it would be smart to make this expire a bit sooner. Great and now let's go ahead and let's create a mail util for this. So inside of lib let's go to mail here and let's go ahead and export const send two factor email two factor token email and let's make it an asynchronous function, which accepts the email which is a string and a token which is a string like that And then in here let add const confirm link to be HTTP localhost 3000 slash out Actually, no, no, we don't have the confirmation link here, right? My apologies. It's just a token. So the email is just going to be, this is your confirmation code and a six digit number. So let's just do await, resend, emails sent from onboarding at resend.dev to email subject to FA code and HTML, open backticks, write a little paragraph here. and it's going to be your 2FA code and stringify the token which we sent from the props. That's it. Great. So what we have to do now is we have to manually enable two-factor authentication for one of the users which we have in our database. So make sure you have Prisma Studio running. Go inside of your user model here. Find the user which you have. make sure it's registered using credentials. So make sure that you verify the email for it and everything and change the value of is to factor enabled from false to true and click save one change here and wait for a second to this update. And there we go. Now it should be true. And you can, of course, refresh just to confirm that you have the newest value. There we go. Is to factor enabled is now true. So as you already know, I can freely log in with this email. As you can see, I'm already logged in. So I just signed out. But I should not be able to log in that easily if I have two factor enabled. So it's one way to prevent that in login.ts, which we are going to do. But the first place I want to prevent it is in out.ts in the callback sign in, which will be added to do add two-factor authentication check. So let's go ahead and do that. So I'm going to add if existing user dot is two-factor enabled. In that case, what we have to do is we, well, we can just easily return false first. So let's check if that is working. So I'm going to use tutorial mailing at gmail.com, one, two, three, four, five, six. And because I changed that to true in my database, there we go. Something went wrong. So I should not be allowed to log in if I have two-factor enable. But, you know, this isn't exactly useful because once they confirm their token, how exactly are we going to allow them to log in? We're going to use that first by importing get two-factor confirmation. So import get two-factor confirmation by user ID like that. and I'm going to use the add sign. So import it from data, two-factor confirmation. So don't accidentally use two-factor tokens here. In here, we need the confirmation. So what we're going to do here is for now remove the return false. And instead, let's go ahead and see if we have the confirmation. So const two-factor confirmation is going to be await, get two-factor confirmation by user ID and pass in existing user.id. like this. Great. And let me just expand this so we can see this. Basically, it's all in one line like that. It's a long line. And now if we don't have two-factor confirmation, then return false. Otherwise, I'm going to add a comment, delete two-factor confirmation for next sign in. Obviously, this is my choice of doing this, right? So every time the user logs in, if they have two-factor authentication, I'm gonna go ahead and delete the two-factor confirmation for them. So the next time they log in, they have to do the same thing over again. If you want to, you can of course modify that by going inside of schema Prisma here. And for example, you can add expires field here as well to be a date time. So I'm not gonna do this in this tutorial, but I'm just giving you some tips. You can do that if you want to and, you know, make it work exactly the same as it would with our other tokens. So when you go ahead and generate the two factor confirmation, where is that? Oh, we don't generate it yet. So we're going to do that later. In there, you can add the expires in maybe, I don't know, two days or something like that. But I think it's safer for two factor to be very strict. So that's how I'm going to do it. So if the user successfully logs in and they have two-factor confirmation, in that case, let's do await database.two-factor-confirmation.delete, where the ID is two-factor-confirmation.id, as simple as that. And then we can go ahead and, well, we don't have to do anything because we have returned true at the end. So if it skips this, that means that the other one is true. So just by adding this code, I should still not be allowed to log in because I shouldn't have the two-factor confirmation. So I'm going to add a console log, two-factor confirmation, and I'm going to log that inside of an object so it's easier to see. So let's check it out. If I go inside of my terminal here, let's go ahead and click log in again. And I should see undefined for that value. let's scroll above the error here or null there we go so you can see the object here let me just scroll up again there we go so it's right here two-factor confirmation is null and because of that we do an if if there is no two-factor confirmation we prevent the user from logging in perfect that is exactly what we want I'm not allowed to sign e and I shouldn't be so what we have to do now is we have to improve the behavior which happens when user clicks login here they should not get an error because they have two-factor authentication instead this should change for to be a code input which they will receive from their email so let's go ahead and do that now so what we have to do is we have to go inside of actions login right here so let's import generate two-factor token. So generate two-factor token from lib tokens, or we can reuse this one like that. And let me collapse them. There we go. So make sure you have generate verification token and generate two-factor token. And besides that, I also have mail here. Great. So we are also going to import send two-factor token email like that. So make sure you have send two-factor token email and generate two-factor token here. And in here, what we're going to do is after we check whether the user has been verified or not, but before we try to log in here, let's add if user, sorry, existing user dot is two-factor enabled. And if we have user dot email, sorry, existing user dot email. In that case, let's do const two factor token is going to be await generate two factor token from existing user email. And then await send two factor token email. And let's use two factor token dot email as the first argument and two factor token, which is the six digit code as the second argument. And let's return a special object to factor to be true like that. So it's going to break the function and it's going to give our front end a specific value so that we know we have to change the display right here. So let's go ahead and try that out now. So I'm going to go ahead and log in again. And this time I shouldn't get an error. Instead, it should just stay empty like this. but I should get a new email. There we go. Your 2FA code. And here we have a six digit number. Perfect. So now what we have to do is once the front end receives this value two factor to be true, we have to modify this inputs so that we can enter that code. So for that, what we have to do is we have to revisit our schemas. So let's go inside of schemas index.ps right here. Let's find the login schema and let's add a third field, which is going to be code. And this one is going to be optional. So Z dot optional here. And let's add Z dot string inside. So very simple like that. Great. And we can also add, well, actually, no, let's just leave it like this. And now that we have added that, make sure you did that to login schema. We can go back inside of components out login form right here. And in here, let's go ahead and let's add a new state here. So above the error, let's go ahead and let's add show to factor and set show to factor. And your state by default is going to be false like that. And then let's go ahead and modify our on submit function a bit. so inside of here I'm going to do the following if I have data question mark error I'm going to reset the form so we have some better user experience here and I'm going to set the error to data.error then if I have data success I can also reset the form and I can set success to data.success. And last one here is gonna be if data has two-factor. In that case, we are not going to reset the form because we need the credentials that user just wrote. And then set show two-factor is simply going to be true like that. And we can also add a little dot catch here. So in case anything else goes wrong, Let's set error to be something went wrong. Like this. Great. And now what I want to do is I want to display a different form field if we need to show two factor. So let go ahead and change this and let go ahead and wrap this form field and the password form field So the entire thing like that And let go ahead and wrap it inside of a fragment So go here because there's two items. So they need to have a parent like that. And let's just go ahead and indent those like this. So two of these elements should be inside of a fragment. and then we're gonna go ahead and give this a conditional. If not show to factor, then go ahead and render those and you can wrap them in parentheses if you like. I'm gonna do that as well. There we go. So we are going to render the form field password and the form field email. Where is it? Right here. Only if we didn't get the to do factor from the backend and now let's do the opposite one. So if we do have show to factor, in that case, let's just go ahead and copy and paste an existing form field like email here. So copy this one. And let's paste it inside. And let's go ahead and give it a name of code. And let's give this two factor code label. Let's change the placeholder to be one, two, three, four, five, six, and let's change the type. And we can just remove it. And let's also give it Oh, it already has disabled is pending. Great. So let's take a look at it now. So if I use tutorial mailing at gmail.com now and try to log in, I should receive two factor true. And this should change the input for me to enter my two factor code. There we go. And I should be getting a new email here with my new two-factor code. So now we have to find a way to enter two-factor code here so that next time we click login, we actually verify the code. And I also want to change this label here. So I'm going to go ahead and find where I wrote login. I'm going to change this if show two-factor. In that case, it's going to be confirmed. Otherwise, it's going to be login. So this should now say confirm. Perfect. But if we just enter the code and confirm, it's not going to work. So what we have to do is we have to go back inside of our login action. So inside of actions login right here. And let's find where we destructure the fields. So validated fields.data here at the start, besides destructuring the email and password, we can also now destructure the code, which might or might not exist, right? So here's what we're going to do. Inside of this if clause, where we check for two-factor token, and we send the two-factor token email, we're going to do the following. So in here, I'm going to add an if clause. If I have the code to do verify code, else we're going to send the two-factor code like that. So that's how we're going to differentiate if the user just pressed on login or if the user logged in again, but this time provided us with the two factor code. So we can do that check inside of here. So let's go ahead and do that. The first thing we have to do is we have to import get two factor token by email using data two factor token like that. All right. And let's go ahead inside of here in this to do. And let's go ahead and see const two-factor token that we have saved in our database for this user using await get two-factor token by email using the existing user.email. So we know what's the code. So what we're gonna do is if there is no two-factor token, we're going to return an error invalid code and then if two-factor token dot token is not same to what the user just wrote in the code field we're also going to return an error saying invalid code and then what we can do is check if the code has expired so has expired is going to be new date two-factor token dot expires is less than new date from now. So if the token has expired, regardless if it's correct or not, we're going to return error code expired. Great. And then finally, we can remove the two-factor token and we can create the two-factor confirmation so that user can finally log in so let's go ahead and write away database.two factor token.delete where id matches two factor token.id and let's go ahead and import db so i don't have that so import db from s slash lib database so first we're gonna go ahead and delete this one. And then let's go ahead and see if we have an existing confirmation. So const existing confirmation is going to be await database two factor confirmation dot find unique. Actually, you know what we can use? We can use get two factor confirmation by user ID directly, right? We have that util. So go ahead and import get two factor confirmation by user ID from data two-factor confirmation. So in here, let me zoom out. We're going to pass in the existing user.id or I can separate that like this. There we go. So get two-factor confirmation by user ID. If we already have an existing confirmation, we're going to go ahead and remove it from here as well. So get two-factor confirmation.delete where ID matches existing confirmation.id. Like that. And once we've done that, we are finally ready to go ahead and do await database two-factor confirmation.create where, sorry, data user ID to be existing user.id. and we're not going to return anything because what we want is to create the two-factor confirmation. This else is going to be skipped and then what we're going to do is try and log in and because we're going to create the two-factor confirmation just before we try to log in, the next time the user goes ahead and triggers inside of auth.ts the sign-in callback, guess what? We're going to be able to find two-factor confirmation and this is going to be skipped and we're going to delete the two-factor confirmation and we're going to return true so let's go ahead and test that out so I'm going to go ahead and go back to my login page here I'm going to repair my emails I'm going to use tutorial mailing gmail.com I'm going to log in here this will send my two-factor token on my email so let's just wait There we go. There we go, 2FA code. I'm going to copy this code. I'm going to paste it here and I'm going to click confirm this time with a code. And let's see if that's going to allow me to log in now. Let's wait a second. And there we go. We are officially logged in and we confirmed our two-factor authentication. And inside of my Prisma Studio, I should not be having any two-factor confirmations. so you can see that this no longer exists so if I log out and log in I'm going to require that again and let's go ahead and look at two-factor token they don't exist either like that so if I go ahead and try this again so tutorial mailing at gmail.com 123456 and if I go ahead and log in I'm going to be prompted with two-factor again and if I refresh on the two-factor token here We now have a two-factor token and we can see what is our secret six-digit number here. We still don't have any two-factor confirmation, but I do have a new code here. So let me copy this. Let's paste it here. Let's confirm it like that. And what this is going to do is remove my two-factor token and it's also going to create and then immediately remove my two-factor confirmation. So we actually didn't even see, but this definitely existed for a second before it was immediately removed by the login callback. And there we go. The old two-factor token has been deleted. And let's try one more thing. If I go back inside of my user here and change is two-factor enabled back to false and click save changes here. And if I sign out now, and if I try this again, tutorial mailing at gmail.com, one, two, three, four, five, six, this should allow me to log in freely without having to enter. two-factor authentication. There we go. So I recommend that you keep it like this for now, just so you can easily log in and log out. Perfect. So we just wrapped up everything we needed for the out screen here. What we're going to do next is we're going to create the actual inside screen where we're going to have an example of a server component and how we can fetch the currently logged in user, a client component, and we're also going to have the settings page where we can change the new email and the new password. And don't forget, we also have to come back inside of our login button here and enable so that we can change the mode to a dialogue instead of redirecting to this screen. Great, great job. So just before we create the inner page, let's go ahead inside of our libtokens.ts here. And let's go ahead and do this to-do, which says change to 15 minutes. But I don't want to do 15 minutes. I actually want to do five minutes to improve the security even more. So we just have to change to five times 60 times 8,000, which will turn into milliseconds representing five minutes. Great. So now what I want you to do is I want you to go ahead and log in using your credentials like this. So let me just log in. And I don't have my two-factor enabled, so I can just freely log in. And we already know how we can get the current session inside of app protected settings page by using await out. But what if we want this to be a client component? How will that work exactly?\nLet's go ahead and remove asynchronous from here. And let's change this to simply be an empty object. And then let's go ahead and mark this as use client. And we also have to remove this action from our form. We can no longer use that. And we can also remove this imports like that. There we go. So now we have, well, a pretty much unusable settings page here. So what we have to do is we have to add use session from next out react. So that's how you can use the session in client components. But if you click save here, you're going to get an error that use session must be wrapped in a session provider. So we can do that inside of our app layout right here. But keep in mind, you don't have to do it in the layout file. So if you only want one part of your application to be wrapped inside of that provider, you can do that here. You can create a layout in the protected and we are going to do that later. We're going to create a layout here. So perhaps you can wrap it there. But, you know, it really doesn't matter for this tutorial. So now let's go ahead and let's import the session provider from next out react. And let's also import out from at slash out. And then in here, let's turn this into an asynchronous function. Let's get the session using await out. And then let's wrap our entire HTML inside of a session provider. And let's go ahead and pass the session to be session like this. And then once this refreshes, there we go. You can see that we have the exact same thing we had before, but this time using a completely different method. So our settings page is a client component which uses use session. But one thing that is still not working is the sign out button. So I'm going to show you two ways that you can do this. The simplest way in a client component is to also import sign out from next slash out react. And then you can add const on click here. Sign out. And then simply, well, we can remove this form now and give this button an on click, on click like that. So if I try and click sign out now, there we go. I am signed out. and I'm going to show you another method that you can use, which is the combination of a client component and server actions. So you can do this if you want to. You can go inside of your actions and create a new file, logout.ts, mark this as use server, import, sign out, not from next out react, but from slash out and export const logout. And in here, await sign out. So we have to turn this into an asynchronous component. So why would you want to do this? Well, this is if you want to do some server stuff before you log out the user, right? So some server stuff, right? And, you know, that's what's cool about server actions, because this leave completely separated from the JavaScript bundle. So you can do some stuff like, I don't know, clearing some information about the user, removing the user, whatever you want to do before you log out the user. So that's another way you can do that. And then what you can easily do here is simply import that logout from the actions logout. And then you no longer need this like that. So you can try that out, sign out, and it's still working great. So now what I want to do is I want to bring your attention to this use session hook right here. And you can see that in order for us to access the currently logged in user, we have to go to dot data and then dot user, right? So that can get pretty annoying. So every single time we need the user, we would have to write session dot data, question mark dot user. Every single time we would have to do this just to only get this information which we need from the user, which is the role, the ID, the image, and the email and the name. So what I want to do is a reusable component, sorry, a reusable hook called use current user. So let's go ahead and do that. In the root of your application, create a new folder called hooks. And then inside, create a new file, use current user.ts. In here, import the use session from next out to react and simply export const use current user. And let's go ahead and get this session using use session. And we are simply going to return session.data user. So we don't have to write that every single time. And I'll go back inside of app protected settings page. And instead of using session, change this to be the user and use current user from hooks, use current user. And you can remove this import from here and then simply stringify the user. There we go. So we just created a reusable hook, which we can use in every single client component to quickly get access to the currently logged in user with their ID, their role, whatever we want. Perfect. So now what I want to do is I want to go ahead and create, we'll style this a little bit better, right? So I want to go inside of this app here, settings, and let's go ahead and, well, for now, remove this stringifying JSON. Let's just leave the sign out button and let's create a class name here to be BG White Paddington and Rounded Excel, for example. So it's not really visible now, but it is going to be visible when we create a layout inside of this protected folder. So go ahead and create a layout.tsx right here. Let's go ahead and export the protected layout. Let's get the children. Let's just not misspell children. And inside of here, I'm going to add a div and simply render those children. Now the errors should go away. And let's create an interface protected layout props to get the children, which are type of react.react node. So we can simply assign that here, protected layout props. There we go. So now I want to go ahead and change this layout a bit. So give this a class name of hfull with full, flex, flex, whole, and gap y10. And let's also give it items center and justify center. and then you can go ahead and give it any background color which you like for example this one or I'm just simply going to copy the one that I have inside of my out layout here so it's that nice gradient right here I'm gonna copy that and replace it here of course that's not important for this tutorial great so now we have that what I want to build now is the navbar component so I'm simply going to prepare it here above the children and write navbar like this. And obviously it's not defined. So we're going to get an error. So let's go inside the protected folder and create a new folder underscore components. And inside of here, a new file navbar.tsx. So the reason I'm putting it here and the reason I'm using this convention, well, the reason I'm using this convention is because I'm writing this inside of the app folder, meaning that anything inside of a folder can easily become a route if I accidentally name a file page. So this is a good convention for you to use as I explained in my second chapter of this tutorial where we went over all different kinds of folders that exist in Next.js. And I'm only going to use this navbar inside of this folder. So that's why I'm not putting it here in the components. It's not exactly reusable. So in here, let's go ahead and let's mark this as use client. And let's export pons navbar. And let's return a div navbar component. Then you can go back to your protected layout here and import the navbar component from dot slash components navbar. And there we go. You should no longer be getting any errors. Instead, you should clearly see the navbar component. Perfect. So now let's go inside of this navbar component and let's go ahead and create some items inside. So first of all, I'm going to give this a class name of BG Secondary. Flex, justify between. Items center, adding four, rounded extra large, width of 600 pixels and shadow SM. So I'm not going to make this inner content responsive. That's not the point of this tutorial, right? This is just something to demonstrate how we can use our current session inside of different types of components. And this specific component is simply going to be used to navigate the different items and to have a little user button from where we are able to log out. So inside of this navbar component, create a div with a class name, flex and gap x2. And then here, outside of that div, render a paragraph user button. So this is where our user button will be, which is going to be a reusable component, which we're going to create very soon. So inside of here, let's go ahead and let's import button from components UI button and let's import link from next slash link. But I believe that is a default export like that. And let's also import use path name from next navigation so that we know where we are. So let's get the path name here, use path name. And now we're always going to know on which page we are. So if I go ahead and add a button here, and let's go ahead and give it an as child prop. Inside, let's add a link component and an href to go to slash settings where we currently are and a text settings. There we go. We now have a button which will lead us to the settings page, which we are currently on. And now what I want to do is dynamically change the variant of this button just to simply indicate whether we are on this page or not. So I'm going to write variant is going to check if the path name is slash settings. In that case, it's going to be default. Otherwise, it's going to be outlined like that. So it's all in one line like this. And you can see that nothing changes because we are on that page. So now I'm going to copy this button here. And I'm going to put it as the first button. And this one is going to check if we are on a server component example. And let's change this to slash server and change this to server as well. And there we go, you can see how now we have a server navigation here. If you click, of course, you're going to get a 404 because it does not exist So let copy and paste this button as well And let change this new one to go to slash client and check for the client component So this is where we going to put our server component example This is where we going to put our client component example And the last one is going to be our admin. So let's change this to slash admin, admin and admin like that. So very simple. We're not going to complicate it any further than this. And now let's create a reusable user button component. But before we do that one, I want to create a reusable logout button. So let's go ahead and go inside of components out. And just as we have the login button, it would make sense that we have a logout button. So we can reuse that and wrap it around anything that we want. So let's mark this as use client. And let's put an interface logout button props to have optional children and export const logout button. Let's assign those. So logout button props and let's simply extract the children. And then in here, I'm simply going to call on click and I'm going to import that logout from my actions, which we've created in the settings page right here when I demonstrated you an alternative way of logging out. So you can use this method or you can import sign out from next out react and then called sign out here. It's exactly the same, right? But this actions method allows you to do something on the server side before you log out the user. So I'm simply gonna use that just to show you different ways of doing things. And in here, we are simply gonna return a span which will render the children and on click is going to be on click and let's give it a class name of cursor pointer. Great. So now I want to create a reusable user button component, which will use this logout button around one of the fields when we click on our image. So let's go inside of our terminal here and let's go ahead and run the following. So let me just zoom out. Let's add npx, chatsy and UI latest add drop down menu. So we're going to need this to open up the menu when we click on the user button, right? So we'll wait for this to install. And besides this, we're also going to need an avatar component. So make sure that you have the drop down menu and the avatar inside of your project. And now let's go ahead and let's go inside of components out and create a new file user button dot dsx right here so inside of this component here let's mark it as use client and let's go ahead and let's import everything we need from add components ui drop down menu so in here we need the drop down menu itself we need the drop down menu content We need the drop-down menu item, and we need the drop-down menu trigger. So we can open it by clicking on something. And let's also go ahead and prepare all the imports we need from components UI avatar. So basically, those two new components which we've just added. So avatar, avatar image, and avatar fallback. Like that. Great. And now let's go ahead and export const user button here. and let's go ahead and return a div user button. And now I want to add it inside of our reusable, well, not reusable navbar components inside of protected components navbar. Instead of rendering a text user button, let's import the user button component from components out user button and it's going to be a self-closing tag. So import the user button from here, go back to the user button And now we can clearly see what we are developing right here. Great. So let's change this to be a drop down menu. And now let's add a drop down menu trigger. And inside of here, we're going to go ahead and add an avatar component. So let's add an avatar component. Let's add an avatar image component, which is for now simply going to have an empty source. And let's add an avatar pullback component. Like that. And inside you can write, you know, anything you want if the image doesn't load. Or you can put any icon. For example, we can import FA user from React icons FA. So if I render FA user here, there we go. You can see how we have a little user icon. So let's give this out our fallback, a class name of VG Sky 500. And let's change this to class name text white. There we go. So it matches our background. If you want to, you can also put that gradient that we reused a couple of times in our project. Great. So now, in order to fill this other image source, since this is a client component, we can use our reusable hook. So const user is going to be used current user. And there we go. We now have access to the user. So we can write user question mark dot image or undefined like that. And you can see that nothing has changed because obviously I don't have any image inside of this user, but we are going to test out GitHub and Google login. So you can see how the image is automatically going to be shown here. So now go outside of the drop down menu trigger and let's create the drop down menu content, which is very simply going to use our reusable logout button, which we just created. So I'm going to change this input to go to components out because I like to be consistent and to indicate that this is a set of reusable components, which we store in this out folder here. Great. And inside of this logout button, we are very simply going to use the drop down menu item and write logout like that. So make sure that you have the drop down menu item here. And now when you click on this, there we go, you have the option to log out. And let's go ahead then just align this content a bit better. So let's give it a class name of width 40 and align off end. And you can also add a little icon here. So you can use react icons, you can use Redix icons, you can use Lucid, whatever you want. So I'm going to use Radix icons, Radix UI react icons, because I have that installed. If you have Lucid, Lucid icons, you can use Lucid react, I believe it doesn't matter. And let's go ahead and simply add an exit icon here. And let's just style it a bit like that. And there we go. You can see how now we have a logout button, which is working. And if I try and log in with my social right now, so I pressed on GitHub here, that will log me back in. And there we go. You can see how I have my image loaded right here. Perfect. So we just wrapped up the beginning of our inside application here. What we're going to do next is we're going to create an example of a server component. So how to use all of these hooks and stuff in servers, how to use that in client, how to handle admin stuff. And lastly, we're going to modify this settings page right here. Great, great job. So now let's go ahead and let's create our server example. So in this example, I'm going to show you how to fetch the current user using a server component and how to fill some information. So let's go inside of the app folder protected and let's create a new folder called server. And inside of the server, create a new file page.tsx. Let's go ahead and export the server page. And let's write server page inside. And now when you click inside of your navbar here, you should be redirected to the server without 404 just confirm that your URL is slash server. Make sure that this folder name is server. Make sure in your navbar that you are redirecting to slash server in the href here and that your path name is matching. Great. So we already know how to fetch the session, right? So we have to mark this as an asynchronous function and then we do session and then we do await out from at slash out. and then inside of here, what we can do is json.stringify and paste that session inside. So we already know how to do that. We can see the information here, but we have the same problem. If we wanna get just the user, we have to go to data and then we have to fetch the user. Is it session.user or this, right? So you can see that we still need an additional step here. So let's go ahead and improve that by creating a reusable lib for server components. Just as we created a reusable lib for client components, let's go ahead and create a reusable lib called out.ts inside of the lib folder. And in here, let's go ahead and let's import out from add slash out. And let's export const current user to be an asynchronous function, which will make this an arrow function, which will get the session using await out and simply return session dot user like that. And then we can go back to our server page and in here, so it's located in app protected server page. And in here, instead of using this, we can use user to be await current user from lib out. And we can remove this import and then we can use the user inside like this. And there we go. And now we get just the user like that. So you can see the difference is that when we're using this out, we have session question mark dot user. But when we use use session from next out react, we use session dot data dot user. So in case this changes in the future, you know, you never know. You can try it yourself. Maybe it will change to user, right? But for now, for me, it's dot data and then dot user like that. Great. So now we have this. And what we can do now is we can create a reusable component called user info. So you can put this pretty much everywhere. You can create it inside of this components. You can create it inside of this components. I don't know. I'm just going to put it inside of out, even though it's not exactly reusable for out. So perhaps simply put it inside of components. It really doesn't matter. So user info dot TSX. And this is going to be an agnostic component. So we are not going to define whether this is a client or server component. We're not going to care. It's going to become either server or client depending on the parent. So if the parent page is a server component and if you import a component which doesn't have anything defined that component will also be a server component But if you import it inside of a page which has used client at the top then the component will automatically become a client component right So for now, make sure that the server page doesn't have the used client. And we're not going to care about what this is. And let's go ahead and create an interface user info props. and the optional user which it can accept is going to be our extended user from add slash next out so if you remember we have this next out dts and in here we export a type extended user where we assign the role right so then we can use that as the expected type here so we don't have to manually write in a user and extend the role manually. We can just reuse this. All right. So it's going to accept the user and an optional, actually a required label, which is a string. And let's export const user info here. And let's go ahead and destructure those props. So user info props will accept the user and the label. And let's go ahead and return a card from dot slash UI card, which we already have installed or components UI card however you want it. Let's render this card and let's render the card header from components UI card as well. So make sure you add this import and inside we're going to add a paragraph with a label and let's give this a class name of text to Excel font semi bold and text center. And now let's go back to our app protected server page right here. And instead of rendering this, let's render user info from components user info, as I've added this export here. And let's go ahead and pass in the user to the user. And let's pass in the label to be, for example, server component like that. And if you want to, you can add a little emoji here. Let me just open this up. For example, we can use a laptop or something like that. So server component and there we go. Now we have a label that this is a server component. Great. So let's go back inside of the user info and let's actually develop this. So I want to give this card a class name of width 600 pixels and a shadow of medium just it looks a tiny bit better. So it matches our navbar here. And now we're simply going to render a bunch of information for the user. So you can see that we can easily access all of that. So below the card header, add the card content, which you can import from here. So yeah, I mean, you're practically done with everything that we have to learn about out. All I'm going to teach you now is, you know, how to render that information and how to render, you know, admin stuff. For example, we're going to create some reusable components for the admin. But yeah, if you're just looking at how to create the login and stuff, you're done. Yeah, you don't have to watch further. But if you're interested in stuff like this, you know, continue watching. So we have space Y4 here. And now let's create a div with a class name here. Flex, flex row, item center, justify between, rounded LG, border, padding three, and shadow small. and inside of here we're going to create a paragraph representing what we are trying to show so this is going to be an idea of the user which is going to have a text small and font medium like that and then we're going to create a paragraph here which is going to render user.id like that and there we go we have the user id here so let's go ahead and give this a class name of truncate in case the ID gets long. Text extra small. Max width is going to be 180 pixels. Font is going to be mono. Padding is going to be one. BG slate 100 and rounded medium. There we go. So now we have a nice little representation of our ID here. And now we can just copy and paste this entire div and render the next item, which is going to be the username. So user.name right here. There we go. We have the name. And what I recommend you actually do for now is log out if you're using an OAuth and go inside of your credential login, because we're going to have to show different things depending on whether we're logged in as credentials or as something else. So let me try this again. so I should be redirected to the settings page and if I go to the server here there we go I can see my new name and everything perfect so let's go ahead and copy and paste this again so this one is going to be our email and it's going to render user.email here there we go we have the email and let's go ahead and copy it again this one is going to be our role so we can get user.role here there we go you can see how it says user here and let's go ahead and do the last one which is going to be our two factor authentication like that and inside of here we're going to render a user is two factor. Oh, so we didn't extend the user to have the status of two factor authentication here. So for now, let's simply write off like that. And let's go ahead and go inside of our out file. So we can extend the callbacks here, right? So we need to extend the token and we need to extend the session here to accept whether the user has two-factor on or off so we can render that. So we can easily do that by adding token dot is two-factor enabled to be existing user is two-factor enabled like that. And then in here, we can copy and paste this and check if we have token is to factor enabled and if we have session dot user in that case let's go ahead and add user is to factor enabled is going to be token is to factor enabled like that and let's actually just check for session user because this can be false right so we don't want to mess that up so all in one line like this and now obviously we have to go back inside of our next out dot d dot c s and add the is to factor enabled to be a boolean like that and then we can change this to be as boolean like this and now the errors are gone and you can see how the our session.user now expects the property is to factor enable great so now let's go ahead and let's go back inside of our app protected server sorry no components user info here and then in here we can check if user is to factor enabled on, otherwise off like that. Perfect. So let me just style this is to factor enabled. If you want to, you can leave it like this. What I'm going to do is I'm going to add a new component from ShadCN called badge. So npx ShadCN UI latest add badge like this. And then I'm going to go ahead and import the badge. from .slash UI badge or components UI badge. And then in here, instead of rendering it inside of a paragraph here, what I'm going to do is render a badge. So we have like a clear indicator. So variant, if user is two-factor enabled, we're going to use success, otherwise destructive. and I think we don't have the variant success. So we are getting this error here. So what we have to do is we have to go inside of the badge component. So inside of your components UI badge right here, let's simply add a new variant here. So success is going to be border transparent, VG Emerald 500 and text primary foreground. and then inside of your user info, you will no longer have an error or the success variant because it can exist. So make sure that you added success and that you wrote success here like this. There we go. So you can see how now it says off, like it's a danger thing. So let me just see if I have my Prisma Studio running. I don't. So let me go ahead inside of the terminal here and run MPX Prisma Studio like this. And I'm going to find my user here. And I'm going to go ahead and enable two-factor authentication for this user. So true, save the change. And once I refresh here, there we go. You can see that now two-factor authentication is on. Perfect. So that's it. We finished an example of how to use user data in a server component. So basically, all we need to do is we need to use our hook. Not our hook, sorry. Not in the, is it server here? Yeah. So await current user, our lib current user, which gets the session like that. As easy as that. Now let's go ahead and copy the server component and paste it here and rename it to client. And what we have to do now is we have to replace this with client page. And we have to mark this as use client. So this will be a client component that you have, right? And we've already done this, you know, but I just want to explicitly tell you now. So you can here use current user like that. And that's it. That's absolutely it. So when you click on the client here, there we go. We have exact same information and let's just change this client to actually tell you that it is a client component and let's use a different icon like a phone, for example. There we go. So this is a client component. This is a server component and we can get the exact same information using different methods, one with a hook and one with a lib. Perfect. So we just wrap server and client components. What left is to create admin example where I going to create another reusable component called a gate So you can specifically hide some content from normal users by using that component And we're going to demonstrate an API call being protected for admins and a server action being protected for admins only. And then lastly, we're going to create our settings. Now let's go ahead and let's create an admin example. So in order to do that, we have to create the admin page. So let me just reload my window. I have some errors in the cache, it seems. And let's go and inside the protected here, create a new folder called admin, like that. And inside of here, let's go ahead and create a new file page.vsx. And let's go ahead and let's mark this as use client and let's export const admin page here. and let's return a div admin page. Like this. And now when you click on the admin here, oh, my apologies. So yeah, whenever you're working with pages, you have to do export default. Like this. There we go. Great. Now we have our admin page here. And now what I want to do is I want to create separate hook and separate lib to directly fetch the role, right? So let's go ahead and do that. First, since we marked this as use client, let's go ahead and go inside of our hooks and let's create use current role dot VS. So if we don't want to fetch the entire user, well, here's the thing. We are not fetching the user here. We are simply decoding the session. So that's important to know. You're not making any unnecessary API requests. But just in case you want a specific hook to get the role, you can do that as well using the exact same method. So what you have to do is import use session from next out react and export const use current role like this, get the session to be use session and then simply return session dot data user role. like that. That's it. And then you can go back inside of that admin page. And in here, you can get the role using use current role. And I can then write current role is going to be role. And there we go. Current role here is user. Perfect. And let me now show you an alternative way of doing it by creating the lib for this. So let's say this is a server component. For that, you would go inside of the lib here, inside of out. And similar to use current user, this would be current role, for example, and it will directly ask for the role. That's it. So that's how you can create, you know, reusable hooks and libs for out. So let's remove use client from here, let's make this an asynchronous function. And let's go ahead and await current role from lib out. And as you can see, no errors. And we are officially able to fetch this both in server and in client components now. Perfect. So I think you've mastered, you know, how to create custom hooks and roles now. So now we're going to demonstrate how to actually create a reusable component, which will protect specific content from some users. So let's go ahead and style this page a bit. So let's import card from components UI card. And I'm simply going to bring this back to use client, because I want to work with client components for this. So role is going to be use current role like that. And I can remove this. Yeah. Great. So let's go ahead and let's in this card, let's give it a class name of width 600 pixels. Let's add a card header from components UI card. Let's add a paragraph, which is very simply going to use some emoji here and write admin. Let's give this a class name of text to Excel, font semi bold and text center. There we go. So we have a header for the admin page. And now in here, let's create a card content. And in this card content, I'm very simply going to add a class name space Y4. And now I want to create a reusable component called a role gate. So you're going to see how that's going to look like. And it might be useful, you know, if you're going to do a lot of stuff, which is role based access. So let's go inside of components out and create a new file role gate dot TSX. It's going to be a client component, which will have an interface role gate props. is going to accept children, which are React, React node. And it's going to have allowed role to be a type of user role from Prisma client. So let's export const role gate here. Let's go ahead and structure the props. Role gate props. We have the children and allowed role. And now let's get the current user role using use current role, which we've just created. And in here, if role is not allowed role, we're going to go ahead and return. You can return whatever you want. I'm going to reuse our form error component from dot dot slash form error or components form error. And in here, I'm simply going to pass a message which will say you do not have permission to view this content. otherwise we're simply going to return a fragment which renders the children which will be allowed only for the specific role there we go so that's how you can create a reusable component and hide some content from some users so now let's go ahead back inside of our admin page so app protected admin and i just want to tell you something i don't know if you know this or not but I hope we kind of made it clear. The only reason this is called protected is so I organize all of my routes inside of that. It doesn't have to be called protected. You don't even have to put it inside of this folder, right? I only do that so I can reuse the layout. So I just want to make sure that none of you are confused by that. So all of our routes are defined in here, in routes.ts. You can see that we don't explicitly tell what are the private routes. all routes are private by default we only tell the middleware what the public routes are so if you create something random in the middle of app folder that's going to be automatically a protected route you don't have to put that inside of the protected folder that just came to my attention I hope I didn't I hope I made that clear right so this protected is purely organizational this can be named absolutely anything all right hope I made that clear let's go back inside of the admin folder here and let's actually use that role gate component. So we actually don't need use current role here at all. You can remove that. And let's go ahead inside of here and let's add role gate component from components out role gate. So make sure you add the import for role gate here. And inside, you're going to write something that only admins will be able to see. So for example, I'm going to add a component here, form success. So I'm going to reuse it. And I'm going to go ahead and write a message here to be, for example, you are allowed to see this content. And now we have to define which role can see that. So allow the role. Let's import user role from Prisma client here as well. So only admin will be able to see this. and there we go since my role is a user I don't have permission to view this but if I go inside of my Prisma Studio here and if I change my role to admin and save this right here I hope I have my Prisma Studio running and if I refresh here there we go I am now allowed to see this content if I change it back to user and save there we go I don't have permission to view this content. Perfect. So our role gate is working. And now I simply want to create an example to test the API route and a server action and show you how to you can protect something in there as well. So let's simply create a div here with a class name, flex, flex row, items center, justify between rounded LG border padding three and shadow MD. So just to make it a little bit stylish and let's add admin only API route with a class name of text small and font medium. And let's add a button component from components UI button. So make sure you add this import. And in here, I'm going to write click to test like that. Great. And now let's copy this and paste it below. And this is going to be admin only server action. And this is going to be click to test as well. Great. Now let's go ahead and let's create both of them. So first, let's go ahead and let's create an admin only API route. So let's go inside of our app folder. And inside of the API here, create a new route called admin or whatever you want. and inside route.ts. And in here, let's export asynchronous function get. And let's go ahead and by default return new next response with a status of 403. So you can use next response, which you have to import from next server, or you can just use a response. I'm going to use next response. I believe the type script is better for next response. right so by default this will always return 403 so let's go ahead back inside of our page and let's add on click to try and go to this route so I'm gonna go inside of protected admin page and let's go ahead and create const on api route click is very simply oops it's going to call fetch to slash API slash admin dot then we're going to get response and then if response dot okay I'm going to go ahead and just console log okay\nelse console error forbidden for now. So just this, right? And now let's try it out. So I'm going to add on click to this button for admin only API routes. On click on API route. Click. And let me just prepare my console here. So when I click here, there we go. I have forbidden. then you can see 403 error right here. So how do we allow this route for someone? So now this is what we're gonna do. We're gonna go inside of our route here. And which method can we use inside of an API route to get the current role? Well, we can use the one where we used, which we used in server components. So this right here, which we've created, the lib folder out, this can be used in server components, server actions, and API routes. So basically anything server side and those hooks that we created can be used in all client side. So if you want to use this inside of your route, you can do that. So all you have to do is get const role await current role from lib out. If role is equal user role from Prisma client dot admin. In that case, return new next response. Now with a status of 200 like that. That's it. That is all you have to do to check the current role and if they have permission to see this. So now if I go ahead again, I'm still going to get an error in my console log here. So let me just expand this. But if I go ahead and change my role to admin here and save and try this out again. So let me refresh. There we go. I am allowed to see the content. I will test this. And there we go. I got okay. Perfect. So now I just want to prettify this with toasts. So let's just go ahead and add a new component inside of here. So let me just open a new terminal. MPX chat CNUI at latest add Sonar. Or you can manually install Sonar if you already used it before so it's just you know for some toast information here then you have to go inside of the app folder layout here and import toaster from s slash components ui sonner and simply add a toaster somewhere in your project for example i'm going to add it here and then you can go back inside of the admin page right here and in here, instead of doing this, we can add a toast.success. You can import toast from Sonor itself like that. So toast.success allowed API route and in here, we're gonna do toast.error forbidden API route. there we go, like that. So, and I click here, there we go, allow the API route. If I go ahead and change this back to user and click again, forbidden API route, perfect. And it is exactly the same for server action, right? So, let's just try it out. So, let's go ahead and create actions, admin.ts, use server, export const admin asynchronous function and in here let's get the role using await current role if role is equal to user role dot admin we're going to return actually we can well we have we can do this return error forbidden otherwise let's return success allowed like this. Make sure you've marked it as a use server here. Go back to the page here and in here, let's do const on server action click. So that's going to tell the admin. Make sure you import admin from actions admin like that. Dot then we're going to get the data. if data.error, those.error, data.error, if data.success, those.success, data.success. As simple as that. And let's use the on server action click here for this button. So admin only server action. And let's try it out. So I should be getting an error for the server action. There we go. Oh, I'm not getting an error. Let's see what's going on. Maybe I have to refresh. No, I'm still allowed. All right, so we're going to check that out. So admin, if role is user role, that admin. Oh, my apologies. This is allowed. And this one is forbidden. My apologies. I did the opposite logic. so let's try it out now there we go forbidden server action uh and yeah let's also change just the results so this is a success and this is an error like that okay sorry about that there we go so now forbidden server action if I get my prisma studio and change my role to admin and save and try the server action again. There we go, allowed server action. Perfect, so now you know how to use our reusable hooks, reusable components and reusable libs inside of server actions, route handlers, server components and client components. All that's left is to create the settings page so I can show you how you can create some best practices for change password, for example, where user has to verify their password first, how to change email. And we're also going to create a little toggle to turn a two-factor authentication on or off. Great, great job. So let's go ahead and let's learn how to update the settings and how to update the active session of this user, because just modifying the database is not enough. We also have to update all of those libs and hooks which hold the current session. So I want to start by creating a schema. Let's go inside of schemas here. And let's export const settings schema, which is going to be an object. And for now, we are only going to try and update the name field. So go ahead and add z.optional because user doesn't have to always update that name. And it's going to be a string like that. And now let's go ahead inside of our actions and let's create a new file called settings.ds. Let's mark it as use server. Let's import everything as z from zod. Let's import the settings schema. Let's import the database. Let's import get user by ID from data user. and let's import current user from lib out. And now let's export const settings to accept the values, which are a type of z.infer type of settings schema. And in here, let's get the user using await current user. If there is no user, we are going to return an error, unauthorized. and we also have to turn this into an asynchronous function so make sure that you add asynchronous here so this await doesn't have an error and now we checked for this user here but let's also confirm that they actually exist in the database and it's not some leftover session so let's do const database user is going to be await get user by id using the user.id here if there is no database user, we can return the exact same thing. So if there is no database user, return unauthorized like this. And then let's go ahead and let's do await database.user.update, where we have a matching ID of database user.id. And the data is simply going to spread all the values we pass. And at the end, let's go ahead and let's return success settings updated like this. So now let's go back inside of our app folder protected settings page right here. And we can remove this too. So we no longer need that. And let's go ahead and remove the imports as well. So just leave use client and instead let's go ahead and import from add slash components UI card, the card, card header and card content. Let's go ahead and replace this entire thing to use the card element. Let's max the width to 600 pixels. Let's add a card header with a paragraph, which is simply going to say settings. and a little settings emoji. And let's simply style it so it looks the same as our other titles. There we go. And now inside of card content, I simply want to add a button component. So go ahead and import a button from components UI button. We're going to add a button and say update name. So for now, we're just manually going to try it like this. And let's add onClick to call the settings server action. So import this. And let's manually pass in the values for name to be new name. And now let's use this onClick. And let's pass onClick here. And let's also import from React use transition. So I want to do this so I can see when it's pending. So const is pending start transition from use transition. Let wrap this inside of start transition like this And then we can use this is pending to disable this button so we know something is going on There we go. So open up your Prisma Studio, make sure it's running, and confirm that you have only one, that you don't have any users which have the name of new name. So we're trying to update this, So make sure that you only have some other names inside of here. And now we're going to try and click on update name here. There we go. See, it's completed. And now I'm going to go ahead and refresh this. And there we go. You can see that my name in the database has updated to new name. But if I go back to my server right here, you can see that my name is still my old name tutorial. if I go into the client. My name is still my old name tutorial here. So what we have to do is we also have to manually update the session every time we change a specific field. So for that, I want to go back inside of auth.ts. And in here, this is what we're going to do. We're going to add this little console log here. I am being called again, like this. And let's go ahead and open our terminal so we can see exactly when this is being called. And this is what I want to do now. You have two options to update the session. So I'm going to show you both of those. So you can do it completely using client site inside of settings page right here. If you want to, you can do the following. You can import use session from next out to react. And then in here, you can go ahead and use this session. And you can destructure update. So what you can do is call dot then here. And then manually update the session like this. So let's take a look at that now. I'm going to click save here. And once this is completed, you can see that it's being called up all the time. So it's constantly being updated. But still, even though it's being updated, you can see that my names are simply not changing. They're still staying at my old names, even though I'm obviously updating, as you can see in my database. My name is no longer tutorial. So how do I update my name? Well, we have to do the following. We have to go inside of our token here and we have to assign the name manually. So token.name is existing user.name. And while we are here, let's also do token.email to be existing user.email. Let's also go ahead. I believe this is all we need. So name, email, role is the factor. I think that's the only thing we're going to update from the settings. And then we have to do the same thing here. So if session.user, session.user.name is token.name. And let's also do session.user.email is token.email. Like that. So let's try it out again. I'm going to go ahead here. And you can see how it immediately works now, because our token now is passing the new values, name and email. So the moment I added that, even without testing, my name has been updated. So this is what I want to do now. I want to go back to my settings page and change this to say something different. So let's confirm my name is still my old name, but if I click on update name here, wait for it to confirm. There we go. My name has been updated in real time to something different. Great, so now we are ready to actually create the form. So let me just remove the console log from my token here. We no longer need this. Do I have a console log? okay, I no longer have a console log. Great. But just before we do that, on the settings page, we cannot show the same settings to all users, right? So if the user has logged in using Google or GitHub, we have to show them different settings, right? Because they cannot change their password, for example, because they don't have a password. They also cannot change their email because it's linked to the account model in the database. So what I want to do is I want to go back to out.ts here and I want to fetch the account. So let's go ahead and create data account.ts and let's import the database from s slash lib database and let's export const get account by user ID using the user ID which is a string. Let's make this an asynchronous function. Let's open a try and catch block. Let's return null here. And in here, let's get the account using await database dot account, find first, where we have a matching user ID. And let's return account like this. So make sure you have this little util. Now let's just visit our next out dot d dot ds. And let's add a new field here called is out. to be a boolean like this and now let's go back inside of out.ts and inside of this token after we confirm that we have this user let's get the existing account using await get account by user id and let's pass in the existing user.id and then what we're going to do is add a token is OAuth is going to be existing account. But we are going to turn that into a Boolean by adding double exclamation points at the end. And then in our session here, we can do session.user.is OAuth to simply be token.is OAuth. And let's add as Boolean here to get rid of this TypeScript error. Great. So now we have that. And now we are ready to go back inside of our app, protected settings page right here. And we are ready to create our form. So let's go ahead and import everything as Z from Zod. Let's go ahead and let's import use form from React hook form. Let's go ahead and let's import Zod resolver from hook form resolver Zod. Let's import our settings schema here from schemas. and let's go ahead and let's import everything we need from components UI form. So we are going to need the form element, the form field, form control, form item, form label, form description, and form message to show the errors. message. And we're also going to need a separate input component from components UI input like this. Now let's go ahead and let's actually define our form to be use form. And we have to give it a type of z.infer type of settings schema. And in here, let's go ahead and let's write a resolver to be zod resolver and pass in the settings schema and let's go ahead and let's give it a default values of name that's the only one which we have defined so for now leave it as empty later we're going to fill it with the actual user name and let's change this on click to instead be on submit which accepts the values which are a type of z.infer type of settings schema. And then we are simply going to pass in the values here inside like that. And let's also prepare some states here. So import use state from react. So we can show the errors and success messages. So const error, set error, use state. And let's go ahead and give it a type of string or undefined. And let's do the same thing for the success message. And then in here, we're not always gonna fire the update. Instead, if we have data.error, we're gonna show a set error to be data error. And if we have success, we're going to call the update and set success data success like this. Great. And you can also add a dot catch here manually to set error something went wrong. So if we don't catch something in our server action, we have a fallback for this. Great. And now we can remove this here. And instead, what we can do is we can render our form element and we can spread the form inside. And then we can add a native form element, which is going to have a class name of space Y6. And on click, which is going to be form handle submit. And let me just collapse these fields. Form handle submit our on submit function. So we successfully pass those values here. now inside of the form let's add a self-closing tag form field which is going to have control of form.control it's going to have a name of name which is the field we are trying to update and it's going to have a render from where we are going to destructure the individual field and then we can render the form item with the form label which will say name then inside we can add a form control and we can add our input component finally and we can spread this field prop which we destructured above and let's give it a placeholder of john doe and we can use the disabled prop to be is pending which we get from our start transition here and the structure is pending here like that. And now let's go outside. Let's go and wrap this form filled inside of a div like this. And let's give it some different spacing. So space Y4, because we're going to have multiple inputs inside and outside of this div now render a button, which is simply going to say save and let's give it a type of submit like this. So now it time for us to give this an actual default value So why is this submitting when I click on it Something seems a bit wrong Let me just see on submit values. So this seems to be immediately submitting once I click on that. So let's see what I did wrong. Oh, it's because in the form I use on click. It should be on submit. My apologies. So in your native form, make sure you use on submit. Great. So now when I click, it doesn't submit. So now we have to fill this with the currently logged in user information. And for that, we can use our hook. So let's go ahead and get the user using use current user from hooks use current user. And in here, let's go ahead and add user question mark name or undefined. So don't put an empty string because then that will update the Prisma inside of our settings. You can see that we just spread the values. So it's gonna receive name to be an empty string and it's going to change that in the database. But if you give it explicitly undefined, then it's not even going to add name field to the values here. So that's why I want you to do it like this. and now if I refresh here I believe I should have my name here but I don't seem to have that let's see why that is happening so I'm gonna go ahead and console log the user to see if we have some issues here so I have my user and my name seems to be empty I believe That's because I just submitted the name from before. Yeah, because we had that on submit function, on click, and then we had a default value of an empty string, which submitted it. All right, so yeah, we made a small mistake. You probably have the same thing if you do. No worries. So let's try and change our name. So I'm going to call this new name change and let's go ahead and save and let's see if that's going to update our property. So if I go to server component, there we go new name change client component new name change settings new name change if I refresh here there we go new name change perfect you can see how now it's working just as fine so the reason our name was empty is because by default I was holding an empty string here and inside of this form I had on click so when we clicked on the field it fired a submit action and it changed the name to an empty string. So just bring it back to this and you can freely update your name. Let's go ahead and try this one more time. If I click save here, this is going to update my name and there we go. Server and client components are updated. Great. So now I want to go ahead and actually show these errors and success messages. So let's go ahead and let's import form success from form success. And let's do the same thing for form error to be form error, if we have an error in any case. And above this button here, let's enter form error and have a message of error. And let's do the same thing for form success to have a message of success. And let's give this button a disabled if is pending. So now when I update my name again, I should get a success message that everything was okay and that my settings are updated. Great. So now we have to add some fields from ChatCNUI so that we can update the other stuff. So let's add the fields switch and select. So inside of our terminal here, I'm going to shut down the app. I'm going to shut down my Prisma Studio. Now I'm going to write npx ChatCNUI latest ad switch. So that's going to be on or off for two-factor authentication. And after switch, we also have to add select so we can change our user role. Obviously, changing the user role in settings is just for development, right? Usually you do that using direct database access or by implementing an API to update the roles. So that's a new how we're going to handle that. So just refresh your page if you've shut down the app. And now let's go ahead back inside of our schema so that we can add those new fields properly. So let's go inside of schemas right here. And let's go ahead and let's add is to factor enabled to be z.optional and z.boolean. Then let's go ahead and add a role to be z.annum, an array of user role from Prisma Client. So make sure you add this import, dot admin, and user role, dot user. And now let's add an email field to be z.optional, z.string, dot email. And password is going to be z.optional, z.string.minimum of six. And a new password is going to be z.optional. It's going to be the exact same thing. And now, here's a cool thing with Zod. We can use .refine to check if the password and the new password match. So if data.password, sorry, not if they match, but we can check that by default they are optional, right? But if the user enters a new password, they also have to enter their current password. So let's get data here.password. So if data.password is entered and data.newpassword is not entered, return false. and we can do if data.newPassword is entered and there is no data.oldPassword also returned false. Otherwise return true. And we can write custom messages for that by adding a little comma here and opening up an object. So message is going to be newPassword is required and path is going to be newPassword. Right. or you can uh you can write two defines if you want to for example you can change this refine with another one and this one is going to check if new password is written and old password is not and then change this one to password is required and the path to be password like that so then you handle both cases in the refine here great so we have added everything we need inside of our settings schema. So now let's go ahead and let's go back inside of our app protected settings page right here. And we have this existing form field. So now it's time for us to add a new form field to change the email. So let's copy and paste this. And let's go ahead and modify our default values for the email. You can see how now we have that to be user.email or undefined. All right. And now in here, change this one to be the name of email to have a type of label of email. Let's change the placeholder to be johndoexample.com. And let's give it a type of email. And now, as you can see, we have our email field here. Just refresh and this will give you the current email that you're logged in with. So when you're testing this, make sure that you're logged in with the credentials user. So don't use OAuth providers for this part. All right, so we have that. And now let's go ahead and let's add a form field for the password. So let's copy this. Name is going to be password. And we're going to have a password here. And let's go ahead and give this a type of password. And let's go ahead and give it the placeholder of 123456. like this and by default let's add password to be undefined so we're not going to fill the password because we don't have the user's password we only have the hash so by default we're going to put it as undefined meaning if user saves this form we are not going to update the password at all so this is for the password field and now we have to copy this field and paste it and this one has to be new password. So this is going to be new password. And let's go ahead and modify that as well. So new password is also undefined like this. There we go. So now we have the fields to update the name, the email, the password, and the new password. So now what we have to do is we have to create a form which is going to be able to change the role for. So in order to do that, We have to import some things from our new component, which we added called select UI select. So just confirm that you've added that, right? We added npx chat cnui select and switch. Make sure you run those commands. So in here, import select, select content, select item, select trigger, and select value. and now we can go back and actually create this so let's copy the last form field which is the new password form field let's change this one to have a name of role and let's clear up the form item so let's just add form label which is going to be role and then inside we're going to use the select component and let's give it a prop disabled of is pending let's give it on value change to be field on change and let's give it a default value of field dot value and inside let's open up a form control and let's add a select trigger with a select value which is going to be a placeholder of select a role outside of select trigger sorry outside of select outside of form control add a select content which is going to have select item which is going to have it's not going to be a self-closing tag so like this the string is going to be admin as an option and we have to pass in the value to be user role which we can import from Prisma Client. So make sure that you import this. So this one is going to select admin. And then you can copy and paste this. And this one will select user. And the label is going to be user like this Great So now you can see that we have an option to select the role Perfect So let's go ahead inside of our default values here. And let's add the role. So role is going to be user role or undefined as well. Like that. So when I refresh here, I believe this should be preselected. For me, it's admin. I might have changed it from the last time. Also, if the form is too big for you, if you can scroll up or down, you can just zoom out. I'm not really making this responsive or anything. Great. So one thing I've noticed we are missing is the form message, which will show an error. So we are not using the form message anywhere, as you can see. So let's go ahead and simply add it to the fields we need. So first we have the name field. So outside of form control, add form message. So this will handle any errors. And let's do the same thing for the email outside of form control. For the password, same thing. And for the new password, same thing. And finally, for here, for here, we have to put it, I'm not exactly sure where. I think we can just do it outside of select like this. so now uh for example if i enter a password there we go you can see that new password is required or if i do the opposite thing let me refresh if i do the opposite thing and enter this password then the password is required so our refine is working quite nicely great and there's a last field which we have to allow for updating with which is the two-factor authentication so for that we have to import switch component so import switch from components ui switch and let's go down here and copy this field form field new password so this one with the item label control because the select you can see that it's quite custom right so we have them we're gonna have to modify it and let's change the name of this one to be is to factor enabled like that and let's go ahead now and let's modify this a bit so i'm going to remove everything inside of form item here and i'm going to give this form item a class name of flex flex row item center justify between rounded large border padding three and shadow sm and then inside i'm going to open up a div with a class name space y.5 and I'm going to add a form label component to say to factor authentication and in here I'm going to add form description to say enable to factor authentication for your account. and outside of this div we can add a form control and simply render the switch component inside it's a self-closing tag and let's give it a disabled prop of is pending a checked of field dot value unchecked change to field on change like this there we go we can now modify this and we can save all of those things. But obviously we have to modify our settings a bit because right now I can change my email to whatever I want without any confirmation. Same thing for the password. We are not exactly checking if they are matching. So let's go ahead and revisit our settings action and let's modify it so that it can actually handle all of those different changes because when we enter a new password, we have to hash that password, right? And let's go ahead and do the following. So the first thing I want to do is I want to check if the user who is trying to change these fields is logged in using credentials, or if they're logged in using OAuth. So in here, after we check that we have a database user, let's check if user is OAuth. Let's go ahead and do the following. Values.email, which is a field they should not update, is automatically going to be undefined. Values.password will be undefined. Values.newpassword will be undefined. And values.newfactor.enabled is going to be undefined. So these are the fields that OAuth users cannot modify because their email is handled by the provider, they don't have a password, and two-factor is also handled by their provider. So it makes sense that if we disable those fields in the API or the server action, we also hide those fields from them right here on the client side. So let's go ahead and do that. So I'm going to find the field which I don't want to show. So we don't want to show the email and the password. So let's find where the email starts right here. and let's do if user is a out is equal to false then we're going to show this form field which has the email this form field which has the password and this form field which has the new password so all the way until we get to the role so obviously we have an error because we don't have a parent component so wrap the entire thing inside of a fragment all the way to the end here of the new password. And then if you want to, you can indent that so it looks a little bit better, like this. So right now, everything should look exactly the same, right? So I'm going to log out and I'm going to log in using my GitHub. And now those fields should be hidden from me because I'm logged in using OAuth. Looks like they're not hidden. That's very interesting. Oh, let me just refresh this. Perhaps the logout didn't work exactly as expected. So let me try logging in again because I stayed logged in as the previous user. There we go. Now, when I'm in OAuth, you can see that I can only see my name and my role. And I should also hide the two-factor authentication. So let's do that as well. I'm just gonna copy this query here. I'm gonna find my field for two-factor authentication. And I'm only gonna render it if OAuth is false. So right here to the end. like this and then I can indent that as well and no need for a fragment here because it's a single field there we go so all out users should only be able to update their name and their role perfect so I suggest that you log out now and log in back to your credentials so we can continue developing our settings great so you can see how we have much more options here as credentials. And in here, we confirm that if client side somehow fails, and they send updates for this, we will never allow them to update those. Great. So now we have to do the logic if the user is trying to update their email. So if values that email, and if values that email is not the same as user that email, so we are only going to send a new verification token if the user is trying to update an email which is different from what it was before. So let's do const existing user to be await get user by email from data user. So we have to confirm that the email they are changing to isn't used by another user. So values.email. If we have an existing user by that email, we are going to return an error which is going to say email already in use. And we also have to confirm that we are not that user. So existing user and if existing user dot ID is not the same as our user ID. So now if I try and use an email from my other account, I believe I should get back an error because I'm trying to take someone else's account. There we go. So email already in user. So what I meant to say was email already in use. There we go. So you can see how that now has switched, is protecting my other users. So try going to your database and take a look at another email that you have and try to use it here. And you should be prevented from updating to that email. There we go. You can see how when I refresh, it's back to normal. Great. And now if we pass all of that verification, we have to create a new token for them to verify. So const verification token is simply going to be await generate verification token from lib tokens right here. Make sure you import that and pass in values.email so we know to which email to send that. And then let's do await send verification email. So make sure you add this import from libmail. And in here, we're going to pass verification token dot email as the first argument and verification token dot token as the second argument like that. And let's break this function and let's write success verification, verification email sent like this. So if I try to update my email to something else, gmail.com, in production, this is going to send the email verification to this email. There we go. Verification email sent. Right now, this email is not being sent because remember, we have to add a domain to resend so that we can send emails to anyone else. Great. So we just handled the case for emails, but how about for passwords? So we have to do the same thing for passwords now before we update it. So if values.password and if values.newpassword and if the database user has a password at all, in that case, first we have to check if they have entered a correct password. So const passwords match is going to be await bcrypt. So let's import bcrypt. Again, you can use bcrypt or bcrypt.js. I'm going to use bcrypt.js here. bcrypt.compare values.password with the database user password, which is a hash. So we are going to know whether the password is correct or not without actually knowing what is the password. So if the passwords don't match, we're going to return an error, incorrect password. Like this. Otherwise, we have to hash them.\nnew password. So hashed password is going to be await big crypt dot hash values new password and 10 salt rounds like this. And then we can manually modify values dot password, which will be updated here in the database to be the new hashed password. And let's do values new password to be undefined because we don't even have that filled in our database. Great, great job. So now if I try and enter a wrong password here and try to update some other password, I believe I should get an error because I don't have a matching password, right? So let's try this out. Let me just refresh here. Should I've gotten an error here? I believe I should have. So let's try it one more time. So password here, I'm going to change this to something wrong my new password let's click save here and there we go now I have incorrect password so just make sure you refresh because if you have that email here then it's going to send you a verification for that email but now if I try and use 123456 and my new password is going to be 654321 let's click save and then I should get settings updated like this great and obviously we have to remove the error from when we submit so let me try and log out now and let me try tutorial mailing at gmail.com let me try my old password one two three four five six so if I try to log in with that wrong six five four three two one if I try to log tutorial mail-in-law.com. If I try to log in with this, so my new password, there we go. It is officially working. Perfect. And now let's just confirm that our roles are working. So right now I'm an admin, but if I change this to user and if I click save, let's see what's going to happen. There we go. I don't have permission to view this and I should get forbidden API route. In here, I'm getting forbidden server action. If I change it back to an admin and click save and then try again. There we go. Allow the API route and allowed server action. And lastly, let's change two factor authentication. So you know that I just logged out and logged in without two factor enabled. So let's try enabling this now and let's click save to see if we're going to get any errors or if this is going to work just fine. So if I refresh here or simply go to the server, for example, there we go, two-factor authentication is now on. Perfect. So let me go ahead and log out and let me try and log in. So tutorialmailingatgmail.com, 123, no, wrong password, 654321, because I changed my password. So let's try that out. Now it should prevent me from logging in and it does. Perfect. And I have a new email. Remember, this one only lasts five minutes. So make sure you enter this fast. Let's go ahead and confirm it. And if this is still working, I should be redirected back to my settings page. And looks like I am. Great, great job. And looks like one thing here is not updating correctly. So my two-factor authentication seems to not be updating here. So it's on here, but here it's off. Oh yeah, I think inside of our page, we forgot to add the default value for that. Yes, we don't have a default value for two-factor authentication. So let's add is two-factor enabled, user is two-factor enabled or undefined. Like that. So I think that now when I refresh here, there we go. You can see how now it's turned on. great so everything is working just fine I think this was a very cool exercise we did a big project but we exclusively focused on you know mastering next out and all the ways you can do things so yeah about this update thing I'm not even sure if you need it or not so it's very inconsistent on my part I actually when I developed this the first time I did not need to do this so I didn't need to manually update my name and email every time in this sessions and callbacks. So it's very inconsistent. I'm not sure how it works. But this is, you know, a bulletproof way for you to know that it's been updated. But it's always updated once you log out and log in. And here's another thing that you can do. So you don't have the update on the client, you can also extract update from here and then let's go back inside of app protected settings page let's remove the update from here and let's remove the import of use session here we no longer need that so let me find there I go remove this import let's go back inside of our actions settings so imagine if this is your route handler you can do this as well so you can now also import update from out I believe and then simply at the end of this thing, after you update the user in the database, call the update here manually or maybe this is not how it's working. So perhaps I need to do this const user like that or let's call this new user updated user and then in here name updated user.name. Is this working? Wait, what is this accepting? it's accepting a user oh so i have to write the user manually and then name is going to be updated user.name email updated user.email what else do we have is to factor enable updated user is to factor enabled and we also have role updated user role so you can do it like this if you want as well Is that all the fields we have? Let me just quickly check. We also have the password. Yes. So, but I mean, password really doesn't matter. You don't even have to update that. Yeah, we don't store that in our session. So I think that this should still be working now. So if I try changing my name, let's try update server test and click save here. I think this should still work just as fine. Let's go ahead and see. There we go. So name is update server test. yeah you can see how oh you can see how on client it didn't update yeah so maybe it's not the perfect solution so yeah you can see there's a lot of things you have to take care of when working with next out it's obviously a great library but yeah sometimes it's just a little bit inconsistent uh so yeah bring back use session here in the settings page bring back the use session hook and call the update here. And then, well, you can leave it here as well if you want to update it from the server side as well. Great, so we've wrapped that up. What's left is to deploy our actual application. So deployment is pretty easy. The only thing that we have to take care of is the Google and GitHub callbacks. So before we wrap up our Auth masterclass, I want to talk about this video's sponsor, Clerc. You've probably seen me use Clerc in a lot of tutorials that I have, primarily because it's so easy and so fast to implement. So before we wrap up, I want to show you how quickly we can do with Clerc what took us seven hours to do with NextAuth. So I'm going to give my application a name, authmasterclass or authtutorial. I'm going to select email address, Google, and I'm gonna select GitHub as the options to log in. I'm gonna click create application and I'm gonna copy my environment keys. So I'm gonna go ahead in this new project that I created and I'm gonna go ahead and create a new .environment file like this, .environment and I will paste those keys here. Then I'm gonna go ahead and continue in documentation where I have to install this package. So I'm gonna go inside of my terminal here and I'm gonna install the package. We already set the environment keys, so we don't have to do that. All I have to do is add clerk provider inside of my app layout So let go ahead and do that here I going to go inside of app layout right here I going to import the clerk provider and I simply going to wrap the entire app inside of clerk provider like this. Let's go ahead and see what I have to do next. I'm going to go ahead and add a middleware. So I'm going to create a new file inside of my project called the middleware and I'm going to paste the snippet from the documentation right here. Now I'm going to go ahead and skip to the bottom to create custom sign-in and sign-up pages. So I'm going to go ahead and create the following folder structure here. Inside of my app folder, I'm going to create a new route called sign-up. And then I'm going to create another catch-all route, sign-up. And inside, I'm going to create page.vsx. And in here, I'm going to paste this code snippet like that. I'm going to copy and paste this and rename it to sign-in. And I'm going to rename the inside route to sign in as well. And then I'm going to go ahead and copy the other snippet for the sign in. Like this. And then I have to update my environment variables with those new routes, which we've just created. So let me paste that here. And now let's go ahead and let's run our app. So make sure that I have npm run dev. I believe I already have that right here. I will refresh my page right here. and I will be redirected to the login right here. There we go. I can use GitHub, I can use Google or I can sign up using the email address and the password. And this will come with validation, with two-factor authentication, email verification, absolutely everything. So let me go ahead and create a new GitHub account here. And now let me show you how easy it is to fetch the current user session. So I'm gonna go inside of my app folder. I'm going to go inside of page.psx here. I'm going to remove absolutely everything from here. And the only thing I'm going to render is my user button component. So let's go ahead and import user button from at clerk slash next JS. And let's save this right here. And as you can see, there we go. I have a user button. and from here I can manage my entire account by changing my email. I can even connect a new linked account. I can add a password. I can check my active devices. I can delete my account and inside of my dashboard here I can control how many users I have and I can also ban the users, impersonate users, visit their profile and also delete the users. And let me show you how we can easily create a public route. For example, I'm going to create a new folder public right here and page.vsx. And I'm simply going to call this a public page with a div. This route is public to everyone. All I have to do is go inside of my middleware and I simply have to add public routes slash public. That's it. So now what I can do is I can log out from my account here and demonstrate to you that I can go directly to localhost 3000 slash public. And there we go. This route is public to everyone. But if I go to any other route, I have to log in again. So as you can see, it took us five minutes to implement with clerk what took us seven hours to implement with next out. So that's why I love to use clerk inside of my tutorials. It's extremely simple to implement. It is extremely secure and it is an absolutely great deal. and I'm very thankful that they've been sponsoring my channel from the earliest videos and allowing me to create so much free content. And now let's go back to wrapping up our tutorial. So before we deploy our project, I just wanna go ahead and add a couple of improvements inside of our app. So sign out of your application. And yeah, here's one thing, whenever you're signing out, make sure that you don't have two tabs open of the same application because then you're kind of not going to be signed out completely. So just always make sure when you sign out using NextOut that you only have one tab open of your session. Otherwise, it's just going to be a little bit weird. So sign out of your application. Make sure that you can see the login screen. And let's go to localhost 3000 where we can find the sign in button. And in here, what we have to do is we have to install a dialog component. So let's go ahead and shut down the app and run npx chat CNUI at latest, add dialogue like this. So this will add the dialogue component inside of our app. Let's do npm run dev. And then let's go back inside of components out right here. And let's go inside of the login button. And now we have to handle this case if mode is model, right? So we can do that by importing the dialog. So let's go ahead and import everything we need from add slash components UI dialog. So we need a dialog itself. We need the dialog content and we need the dialog trigger like this. And then inside of this mode model, all we have to do is render the dialog. Then we have to render the dialog trigger. and let's give this dialogue trigger our as child prop and inside we're simply going to render the children and then dialogue content is gonna render our login form from dot slash login form or components out login form. So that's how easily we can reuse our form. So if you wanna do the same thing for register, feel free to do that. And let's just give this a class name of padding zero with auto BG transparent and border none. So now if we go back to page.vsx where we use our login button and change the login button mode to model, we can go ahead and click sign in. And let me just refresh my page to confirm that this is up to date. Yeah, if you shut down your app, make sure you run it again. And there we go. Now it's in our model. And to fix this hydration errors, which we have, all we have to do is also pass the prop as child right here. There we go. So now no errors. And you can see that I'm opening this inside of a model instead of a redirect. So if I remove this mode, and click sign in, then it will redirect me to the page. Great. So we have that covered. Now here's another thing that I want to cover. So currently, when I log in into a specific page right here, and when I get redirected, sorry, if I go, for example, into admin, and if I log out, in my URL, it's just a slash says slash out slash login. But what I wanted to do is I wanted to tell me what's the last place I visited so that when I log in back again, it will redirect me back to that path. So let's go ahead and visit our middleware.ts where we can modify that. So find this if clause if we are not logged in, and if this is a public route. And in here, let's define let callback URL to be nexturl.pathname. And then if nexturl already has a search, let's do callback URL plus equals nexturl.search. And then let's encode our callback. So encoded callback URL is equal to encode URI component with the callback URL. And all we have to do is append that to this new URL here. So let's go ahead and do that. Let's change this to use backticks. And let's add a question mark and encoded callback URL query. So now make sure that you're logged out. So when you refresh, it should not show you the settings. If you log out and it's showing you the settings page again, it means that you have another tab open of this page. So make sure you close our tabs and then log out. So now I'm logged out. And if I manually go to slash settings, you can see that I'm redirected back. But this time I have a query from where I came from. So if I try to access admin, now that is saved in the URL. So when I log in, I will be redirected back to that page. So what we have to do is we have to use that callback URL inside of our login form So let go inside of components Let go inside of out login form right here And let go ahead We already have the search params Great So now besides the URL error let's also get the callback URL to be search params.get callback URL inside of our URL. And then we can pass that as the second argument of the login server action, like this. And let's go ahead and go inside of login here. And the values are going to be the first argument. But the second argument is going to be the callback URL, which is going to be optional and a type of string. And then we can go ahead and add that to the redirect to so-called back URL or the default login redirect. So that's why we use that here. So let me try this out now. post a tutorial, mailing at gmail.com, 654-321. I have two-factor authentication on, so I'm going to get an email. So usually once I log in, I'm redirected to the settings page, but now since I have this inside of my URL, once I confirm this two-factor token, I should be redirected back to the admin page. So let's just try that out if it works. So it looks like I made some mistake here. So let's go ahead and see what I did wrong. Callback URL here. How about I try logging out and console logging the callback URL right here. Oh, I think what we did wrong inside of our middleware here is that we forgot to add the callback URL here. So make sure that in your response redirect new URL, you add the callback URL. like this. So let's try it out now. So make sure you're logged out. Let's attempt to enter admin, for example. And there we go. Now you can see how our query has the callback URL. And it seems like we are having this little type error here in the login. So let's change this to be string or null. There we go. That will resolve the typescript error. So if I try this again, tutorial mailing at gmail.com 654 321 and I log in I'm gonna get a new two-factor authentication code now there we go let's go ahead and use this and now I should be redirected back to slash admin instead of slash settings because that's the last page I visited or attempted to visit while I was logged out and there we go it's finally working perfect so now we just have to do the same thing for the social buttons here. So let's go ahead and try that out. So go inside of your components out and find the social button here. And in here, you can do the same thing. So import use search params from next navigation here, get the search params from use search params, and then get the callback URL to be search params.get callback URL. And finally, you can do this here. So callback URL or the default login redirect. So if I try logging in with GitHub now, I should also be redirected to slash admin instead of slash settings. There we go. Perfect. But there is one more thing we have to take care of. and that is inside of our mail library. So in here, we are hard-coded to localhost. So we have to change that to dynamically use our environment variable. So let's go inside of .environment and let's create next public app URL to be HTTP localhost 3000. So don't add a slash at the end. So just localhost 3000 and make sure it's HTTP. And then let's go ahead inside of here and let's add const domain to be process.environment.next public app URL. And then we're simply going to change this from instead of being hard coded to use the domain variable. So domain slash out slash new password, which will translate to localhost 3000 slash out new password. So later when we deploy, we can easily change the environment variable to our new deployed URL. So let me just try this out. I'm going to go ahead and attempt to log in. And now I should get an email and the redirect link. Oh, sorry, this is to factor out, not this. The new, let's try new password. So let's try new password. it. So now I should get the new email being sent here. But let's see if it's gonna lead us to undefined or to localhost. There we go. It's still working. It's leading us to localhost. Perfect. So we've wrapped up everything we need for our app. Make sure again that you're only using one application open for this tutorial. Great. So we've wrapped up the entire tutorial. Now it's time to deploy. So the first thing we have to do is we have to create a new repository in the GitHub. So I'm gonna give this tutorial next 14 out masterclass. I'm gonna make this a private repository and I'm gonna go ahead and click create repository. Then what we have to do is go inside of our terminal here and we can shut down the app and run git add and then git commit and I'm gonna go ahead and call this for example deployment and I'm gonna now run these three commands which we have so this is if it's a new repository and this is if it's an existing repository so let's use these three commands here, copy them, and paste them here. And after you push, and when you refresh this project right here, project deployed, sorry, not deployed, you should see it in GitHub right here. Perfect. So now let's go ahead to Vercel.com. Let's click add new project. And let's select our new GitHub repository. And now we have to assign the environment variables. So let's go to dot environment like this and let's copy everything and let's paste them inside. So obviously we're going to have to change the next public app URL, but we can only do that once we deploy. So I'm going to click deploy. I'm going to pause the video and we're going to see if I get any errors. If we do, we're going to go ahead and fix them together. So I got an error with my deployment and I believe I know why. Yeah, I forgot to add that we need to initialize our Prisma client. So let's go ahead and go inside of package.json here. And in here, let's add a post install script and let's run Prisma generate. And here's another tip for you. If you don't want to wait until Vercel does the build, you can manually do npm run build. so this is not going to deploy or anything this is just locally going to attempt to build so run this and we're going to see if we have any problems here so we don't have to wait for and there we go here it looks like everything is okay so now what we have to do is we have to redeploy we can do that very easily with Vercel all we have to do is run git add git commit I'm gonna call this fix and get push and once you push the new deployment is going to automatically start so if you take a look at here go inside of your projects here select the new project and in here if you look at the deployments tab you can see that it's building a new one right here so you can click on it and then you can expand it again and there we go this time it was successful and you can see our page right here so let me just go ahead and expand this I want to go to the root of my project in the project so I can get the actual domain so don't use the deployment domain use the actual domain right here so when I visit this there we go we have our login here but our github and google are not going to work So let go ahead and do the following Copy the URL like this this one and let go back inside of your GitHub OAuth where you defined your client secret right here You can find you know in the beginning of tutorial where I did that Change the homepage URL to be that new page and replace the backslash. And now we have to change the authorization callback URL as well. There we go. So your new domain URL slash API OAuth callback GitHub. And go ahead and click update application. And you don't have to update the keys. So just make sure that you have those new URLs here. And then you have to do the same thing in the Google Cloud. So find your project, go into credentials here. Let's go inside of web client. Let's change the URL to be that, remove the backslash. And in here, also replace this to go to slash API auth callback Google with this URL here. And let's go ahead and click save here as well. And now you have to go to the OAuth consent screen right here. and in here you have to add, let me close this, you have to add, where is it? So click edit app and you have to add authorized domain. So for me, it automatically added this domain right here the moment I added the credentials. So just confirm that in your authorized domain, you have that domain and you can just confirm and save and continue. Great, so now let's go ahead and try it out now. So I'm going to go ahead and attempt to log in with GitHub here. Let's see if that's going to work now. So there we go. I'm logged in. Perfect. And let me try Google. And there we go. Google is working as well. Perfect. Great. So you officially deployed. What we have to do now, well, if you want to, you can continue watching the video where I'm going to show you how you can add the domain to resend so that you can send emails to anyone and so that inside of your libmail.ts you can change the from to be from whatever domain you want. For that you're going to need to purchase a domain right you can find very cheap ones for I believe five dollars so I'm going to do that I'm going to purchase a domain and I'm going to show you how to change the DNS settings. So there we go you can see you can find very cheap ones so I'm going to go ahead and purchase this one and then I'm going to show you how to modify the DNS settings. So after you've purchased a domain or used whatever domain you want, go ahead and go to recent, go inside of your account, go to domains. And I'm going to go ahead and add this domain. So Alt Masterclass Tutorial right here. I'm going to click add. And now we have to add this DNS records and then we have to verify them. So let's go ahead and do that. So I'm going to go ahead, go inside of your DNS. I'm using GoDaddy and let's click add a new record. So the type is MX for the first one, I believe. The host is send. And let's go ahead and copy the value right here to be the value. Let me zoom out so you can see. And the priority, it says 10. So let's copy 10 as well like that. And let's add more records. So the second one is TXT. It's also send. So TXT, send. And the value is this one. and let's add one more domain, also TXT. This is the name, and this is the value, and let's click Save All Records, so make sure you've added all of those to your DNS right here, and then what you can do is go ahead and click on Verify DNS Records. Sometimes it can take a couple of minutes, maybe even hours for DNS to propagate, So this is quite optimistic of me trying immediately. So if it doesn't work immediately, I'm gonna wait a couple of minutes and then try verifying again. If it's still not working, perhaps we did something incorrectly, but it's just a matter of copying these values and adding them to your DNS records. So this is the same process if you're using any other, I'm using GoDaddy simply because I know how to use it and access the DNS settings. You can use Namecheap or Cell, whatever you want to do, wherever you add your DNS variables. So I'm just going to pause the video and see where this is going. There we go. So my domain was officially verified. And what we can do now is we can send the domains. We can send emails using that domain. So let's go back inside of our lib folder mail right here. And I'm going to change this to send from, for example, you can do anything you want. I'm going to use mail at outmasterclustutorial.com. So from now on, that's going to be my from domain. Let's go ahead and change that here. And let's go ahead and change that here. So I'm going to go ahead and first attempt this locally if it's working. And then we're going to go ahead and deploy again. So I'm going to npm run dev here. And I should also be able to send emails to anywhere now, not just my logged in account. So we're going to attempt to do all of that. So I'm going to go ahead and sign in. I'm going to use tutorialmailing at gmail.com, 654321. And I'm going to check my email now. And let's check my email. And there we go. You can see that the name is Mail and it's coming from mailoutmasterclass.com. So we officially linked our mail. What I want to try now is whether we can send the emails to some other mail. So let's try that by creating an account. So I'm gonna call this test and I'm gonna go ahead and use, I don't know, something at example.com. So I think this should fail now, but I still believe that we should see it inside of our resend as something that has failed. So let me just go ahead and refresh. And there we go. You can see how now we can send emails to any account that you want, any email that you want. Perfect. So this is now full on production. Everything is working. And to push that to production, all you have to do is do git add, git commit. Let's add domain and git push. And that's going to re-trigger the deployment. But we didn't even add anything here, right? Except changing our mail, of course, to use this one. So you can do anything. You can make this send. You can make this whatever you want. All that matters is that you have your domain here. Perfect. Great, great job. You've wrapped up the entire tutorial. Thank you so much for watching. Remember to leave a like, share and subscribe and watch my other videos if you like them. So just one thing that I forgot to cover. If you use the domain right here from Vercel and for example, if you try to log in, use your password and get the two-factor authentication token the domain that will be sent to you uh sorry not this one new password let's try this again so new password for tutorial at tutorialmailing at gmail.com we forgot to change the environment variable of next public app so let me just wait for this to come there we go and when i click here you can see that it leads me to localhost 3000 instead of this domain. So it's quite an easy fix. We just have to copy the URL. We have to go inside of the settings of the project inside of environment variables here. And we have to find next public app URL. So right now you can see it's localhost. So our mail system is going to add URL links with that. So just change it, not with the slash to go to HTTPS, your Vercel link and don't put a slash at the end and click save. And after that, you have to go in your deployments, select the last deployment and click redeploy like that. And then we're going to demonstrate this again, just to prove that it is working. All right, the app has been deployed. So I'm going to visit it on Vercel again, and I'm going to send myself an email. and let's just confirm that this time the URL link is correct. So let's see if this is the correct email. Looks like it is. So this time when I click here, I'm redirected to the correct website. Perfect. So that's it. We've wrapped up the entire tutorial. We fixed the bugs. We enabled resend. We enabled absolutely everything. Thank you so much for watching one more time and see you in the next tutorial.",
  "transcript_chars": 407958,
  "transcript_filled_at": "2026-06-06T15:41:01.227702+00:00",
  "transcript_filled_by": "tk-bulk-groq-retry-20260606"
}