Advanced authentication in NextJs using middleware & server actions

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
in the last video on the channel we added authentication to our next.js 13 app using the next auth package and in this video we're going to take things a bit further by adding a middleware to intercept the requests coming into our site and decide whether or not we want to protect that specific path they're trying to access and if the user is logged in or not redirect them to sign in if they're not or make them more proceed if they are logged in we're going to also add a feature allowing the users to update their profile we're going to use the new update function from the next auth package that allows you to update the session once the user had actually make that update and for the actual mutation or the actual update we're going to use the new server actions in Nexus 13.4 which is exciting it's still not stable but it's a feature that we want to understand because that's the way that we're going to implement mutation or updating data on the server side without the need to have an intermediary API layer hey friends welcome back to the channel if you're new here my name is Hamed I'm a full stack web developer and here on this channel we talk about modern web of topics like react and xjs so let's get into this let me show you what we're working with and then I'll explain how we go about building it if you remember from the last video we built this app together where we had this protected routes on the server some protected route on the client side we implemented this user button which shows you the currently logged in user way to sign out and this link to our profile page now in that video we didn't have a profile form but we're going to implement this together in this video and we're going to use server actions to actually do this update on the server without the need to have an intermediary API layer which is a traditional way of handling form submissions you submit the form to an API that API then talks to your backend to your database and updates the data whereas with the server actions we're going to directly call a function that we Define on the server which then does that mutation to the database and responds back to this form now if you are not familiar with the server actions I have a different video on the channel which talks about the new release of server actions in Nexus 13.4 so you can watch that video too where I build a simple to-do app and allow you to create new to-do's with the server actions we're going to also practice that same concept over here too so you can hear this out if he wasn't very clear to you watch that video to reference the documentation It's A New Concept and a new way of mutating data it's the future way of doing it it's still not stable but it is the way that we're going to be mutating it in the future so that's why we're going to use it in this video then at the end we're going to look at adding a middleware which is again just like an Express middleware intercepting the request coming to your site and then redirecting or rewriting or responding the way that you define to a request in this case we're going to look at authentication Define some protected pages that we want to have an active session on or a logged in user otherwise we're going to redirect them back to the signing page we're going to do that with the next auth package which is very easy but it's a question or a common question that I get in the comments to know how to implement it using a middleware so let's start with this profile page and then we would do the middleware at the end now in order to allow the users to update their profile we first need to persist the user data in our own database for this I'm using mongodb and mongodb adapter from NYX auth which allows us to persist the data coming back in this case from Google social login to our own database which then enables us later on to give this ability to our users to update that data that exists in our own database now the way that I'm doing it or what I have added to our authentication here or to our initialization here is this adapter that you can see down here I'm using the mongodb adapter and I'm passing this client promise which I created connecting to my mongodb instance let me just quickly go to this Library function that I have defined over here inside of a I have this client file where I'm just creating a client promise passing in my URI this is the connection string to my mongodb instance to create a connection and then share this connection throughout my application for any function that wants to use the database so that's the first thing that we're doing uh in the client promise we had another file inside of our utility or Library folder for actually working with the user collection I'm going to explain that later but for now we're just getting a connection to our mongodb client and passing it to this mongodb adapter again I do have a video on the channel where I talk about implementing mongodb in xjs I'm going to link it somewhere in the cards for you if you're not familiar with that so that's what we have added for now just ignore this callback this is something we I also added on top of what we had in the last video but I'm going to get to this where we actually talk about the update function to update the session for now all we had done is added a mongodb database and this adapter to write or persist the user data inside of our own database so that I can allow the users to then update you know their Avatar their image or their name so let's go back to this profile page so on the profile the page and let's see what we're doing here now previously on this page we were using this get server session um from the next auth package this is a new function from the package that allows you to check the session on the backendore in the server side now as you know our Pages inside of the app router by default or react server components which only run on the server so we need we need to use this get server session from next auth food pass it the same auth options that we have used to initialize our next auth as you can see over here these are the same auth options that I'm exporting from this file and using it inside of this get server session to get the session and in that video if you didn't have any session we would redirect the users to sign in with this callback URL to to come back to profile once they completed the signing flow but because I've implemented the middleware I no longer needed these two I'm just going to keep them for now because we're going to implement the middleware at the end so this page is similar to what we had in that video the only thing that we have added is this user profile form so just go to this form and see what we're doing over here now this user profile floor from the first glance is just rendering this form that we can see here there is this input type text where we have a default value for the name of the currently logged in user we have this button that submits and we do have a hidden input over here which is containing the email I'm going to explain why we need this but before we get there you might notice that I'm using this use client directive up top of this component the reason why I'm doing this is that I want to turn this component into a client-side component again by default all the components pages and layouts in the app router are react server component but if you need to use a react hook state or anything belonging to the browser you need to use a client-side component for which you can use this use client directive now the reason why I needed a client-side component is that I need to use this update function which is a new function from the next auth package return from this use session hook so it's a react hook we need a client-side component and this allows us to update the session in the browser so once the user actually updated their name we're going to use a server action and update their name in the database but we then need to update our session in our client-side app so therefore this name for example up top here changes to the updated name that's why we're using this update function and a common question that I get on the next auth implementation is how to update the session on the client side and this is exactly how we're going to do it now to show you how we're using this update you can see after we do this mutation which I'm going to explain how we're doing it we're just calling this update function from the next auth passing it the name that we got from our form the same name that we use to update our session or our database and we're going to just merge this new name to our token or our session on the client side now before I get to uh the details of this update function or Logic Let me just go back to our form and explain how we're handling the form submission now traditionally speaking you would have an on submit prop over here pointing to a handle submit function where it gets the event and prevents the default behavior of a browser gets the form data submits that form data to an API layer that API layer which runs on our backend would talk to our database and then does whatever mutation or update that we want to do and then responds back to our client side now with the use of the server actions we want to eliminate that intermediary API layer and the way we do it is we have this server action functions which are just functions running on the server but you can call them from the client side so I'm defining this action right now this action is called handle submit this is not a server action it's actually a client-side action that calls the server action and the reason why I needed this is because I want to call this update function here so what we're doing here we are pointing it first of all to the action prop of the form it's not the on submit it's the action prop again I don't know if I mentioned or not but I do have a separate video on the channel where I talk about server actions and the release on the 13.4 mixture is 13.4 if this is uh not very clear to you maybe you can watch that video first to understand what the actions are and then come back to this video I'm going to explain the same kind of flow over here too so you can follow along but if you found it a bit confusing you can watch that video to reference the docs to learn it this is still not stable way of maintained data but it will be the future way of mutating data in extras apps that's why we're using it so it's good to understand how these things works so we have this handle submit it receives the form data this is the way that it works from the form data I'm just uh looping or mapping over the entries inside of the form data passing it to object Dot from entries which turns this into an object I'm destructuring the name and email which corresponds to the name prop of this input elements I have on my form so I have a an email and I have a name the reason why I needed to include this email is that I want to pass this email down to my update function on the server to be able to find that specific user with this email and then update their name so I'm including a hidden input over here with the value of the user email the currently logged in user email and then I have this input type text where whatever name that I currently have I'll just show it here as you can see and then they can update it through this form so if you're getting this name and email out of our form data if there is no name or email I'm returning because there's no point in submitting This Server action if I do have the name and email I'm going to call this update name which is a server action I'm going to pass my name and email once that's done we're going to look into the implementation of that in a second but once that's done I'm going to perform this update which updates the next auth session on the client side so therefore I see the correct name up top here or whatever it is that the user has updated and then at the end I'm using this toast notification to just show a notification that the user knows actually that the update went through and for it I'm using this react toastify Library which makes it very easy to show a toast notification all you need to do is to get this toast and a toast container I'm just showing that toast container underneath my form too and just to show you how this works if I update my last name and hit update once this goes through you would see that toast notification up there it Auto hides and if I just click on this user button you see that my last name has actually been updated there now let's look into this server action or update name function the convention is to define a inside of your app directory or app folder you're going to Define this underscore actions or actions.js this is where you would put in all your server actions you would put this use server directive up top to instruct next.js that these function functions are only meant to run on the server and you can then Define any function or functionality that you want to happen in response to a call from a client side so I have defined this update name action which takes in a name and email and then just goes ahead and calls this update user function from my mongodb collection so many update functions but this is a server action calling this update user function which is a function that I defined that works with the users in mongodb so let's quickly look at that inside of our lib folder there was this folder we had the client this was how we defined a client connection I also have this users file which corresponds to the functions or actions I want to take on my users collection so let's see what we're doing here we're just initializing The Connection by just importing this client promise if we're going we're going to get a handle from our users collection and we're going to share this globally with these functions now I have defined two functions one is find user by D find user by email if you're not using any of those we're just using this update user function down here and all it does is it receives an email and an update document if there's no user users collection this is the handle to our users collection again if you're not familiar with mongodb or how to implement it in your an extras app I do have a video on the channel which talks about that I'm going to link it in the card somewhere but we get the handle from our users collection we call the update one function from mongodb we're going to match a document or a user that has this email and we're going to use the set operator to just set the name or whatever update function that they have passed to this user we're going to then return success true there's different ways to do it you can return the document you can return the actual user document that you get out of this by passing different options but I use the simple way of just updating the user as that's what I needed here so going back to our action what we're doing inside of a server action which is just a function that runs on the server if you're getting an email name and email we're going to call this update user function that we just looked at from our mongodb collection passing the email to find the user and then this is the document that we the update document that we also passed to that function which then goes ahead and updates the name okay so this was our actions so going back to our page sorry to our components and this user profile component so going back here again inside of this handle submit client-side action that we had we call this update name server action passing name and email once this is done by calling our update user function that talks to our collection in the mongodb we're going to then call this update function this is a new function from next auth that allows you to update the session on the client side so here I'm passing this name this is named the same name that I got and passed to my update name it's the same name that I used to update my database I'm just passing it here and instead of it going back to refetch the session from the database I'm just passing it here and say hey this is the same name I used to update my database and you just go ahead and update your session with this too now this is obviously coming from the client because this is coming from the code you need to sanitize the value of what you're writing to your database or to your token for now I'm not doing any of those but be careful when you are using a value that's coming from client-side from this form validate it sanitize it and make sure it doesn't contain anything that that it shouldn't contain when you're writing it to the database so when you're writing it inside of this action or when you're writing it inside of the functions we defined in the mongodb collection and also when you're passing it down here so that's something that you need to be careful whenever you're working from any data that comes from the browser or the user but nevertheless we are updating the session passing in the same name we used to update our database to update our session once this is done and complete we're going to then show a toast notification and as I have already shown here if I just turn this back into my last name once the update goes through we show that notification and then my last name is now updated to what it needed to be okay with that out of the way let's actually look at implementing the auth using the middleware so let me just comment these things out for a second to be able to save this go back and to add authentication using middleware all you need to do is add this middleware.js to the root of your application so outside of your app all your Pages directory doesn't matter if even if you're using the next just 12 Pages router you would still add this middleware to the root or if you're using the source directory you would have to add it to inside of the source but at the same level of your app or Pages directory now all you need to do here is to export the default function that comes from next auth middleware and that's how easy you can add authentication using the middleware I'm going to go back to the docs for a second here they have this I'm going to also include the link to this section of the doc index auth in the descriptions here but they talk about using a middleware uh which is a concept in xjs all you need to do is first of all Define the next auth secret this is the same secret that next auth uses to sign your Json web tokens and it is also going to use it for the middleware because it's using or working with the same token you can use a secret option in your config but it's it's recommended to use next auth secret I have explained this in the previous video so if if you're coming from them you already know this so you have to have this environment variable and then once you've done that all you need to do is to export the default function that comes from next auth middleware this way your whole application is now behind authentication as secure if you only want to protect some pages and not the entire application you can export a config option from your middleware file which uses this matcher key to only match some specific paths for example in here it is only protecting the dashboard page and the way that this works if you go to the middleware documentation index.js you would see the convention of defining a middleware and using these matcher keys or these configs inside of your middleware which makes it so this middleware is only running if the request or the path actually matches this path for all other paths it just skips this middleware and Returns the next response as usual but if it the path that the users trying to access matches the path that you've defined in this config it actually runs the middleware and this is what we're doing inside of our application too all we're doing is that hey if the user is trying to access the profile page this is a page we want to this Middle where to run to be able to hide it behind authentication or to protect that page or if the user is trying to access this protected if you remember all of these protected routes I had this them inside of this protected path then we had the client and the server so all I'm saying here is that if the user is trying to protect any path that starts with this protected and then continues with anything after such as the client or the server I need to run this middleware from the next auth which makes sure that there is a session there is a token and if there is they're going to be shown or taken to that specific page they're trying to access and if there is not a logged in user they will be redirected back to the signing page now that we have this middleware we have to go ahead and eliminate the previous way that we were protecting those pages so let's go to this profile page let me just show you what I was doing there inside of the profile page as we saw together uh previously we were using this get server session checking the session if there was no session we were redirecting it to the signing Page by setting a callback URL but right now we no longer need these because the middleware is actually doing the same thing so we don't need these guys also our in-depth protected pages that we had we had a server page as you can see over here again on the server side we were using this server get server session if there was no session we were redirecting them to the signing page we no longer need that I still kept this session over here because I'm using this username inside of that server protected page and if we go to our client protected page and look at that the way again let me just comment comment this back in the way we were doing it before was that we were using this use session Hook from the next auth package which gives us the session but it also gives us the ability to pass in a required true option uh which then requires a session for this page meaning that it will just protect this page with authentication now the downside with doing client-side authentication is that you'd have this flash of unauthenticated content to first they would see this content and then this Hook is going to hook into the session and then if the required true option is passed it requires the user to be logged in if they are they would proceed and see the page if they're not they're going to be redirected back to the signing page now we no longer need to do this because we're going to protect this page from our middleware so we no longer need to pass this required true to use session hook all uh I'm using this use session hook 4 is to get the session and show the username down there so I'm not protecting the page per se I'm just hooking into the session getting the username out of it too so that I can show it if I go to this protected client page this is a particular page but I'm actually using the user showing the username over here and to get the session the coin user I'm using this use session hook but the protection or needing the authentication is happening at the middleware stage so that's why I'm commenting what we had before out of these components whether they're on the server using the get server session or on the client using this use session hook that's our app for this video folks we covered updating this session using the new server actions and the update function from next auth we also implemented protection for our Pages or specific paths using your middleware you can take the middleware even further if you scroll down on the same link that I included in the description you can get down to this Advanced usage of middleware where you can actually get this with auth function out of this next auth middleware and pass it some options you would need to pass it a callback that has this authorized callback it receives the current token and if there is a token it means that the user is logged in for example here they're showing that you can check the role on the token to see if there are an admin for them to be able to for example access an admin page so you require not only there is a token from your authorized callback but you also need the user to be an admin to access this admin by this role and you would have this middleware function also defined in this with auth function this middleware is going to uh only be called if there is actually a token and if this authorized callback returns true which means that the user is authorized so then this is going to be called you can do whatever you want to do here if they are not authorized they're going to be redirected back to the sign-in page hope this was helpful if you have any questions hit me up in the comments and I'll see you in the next one bye
Info
Channel: Hamed Bahram
Views: 6,554
Rating: undefined out of 5
Keywords:
Id: SFQwto0rvps
Channel Id: undefined
Length: 27min 3sec (1623 seconds)
Published: Sun May 14 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.