
No Comments Yet
Be the first to share your thoughts and start the conversation.
Be the first to share your thoughts and start the conversation.
Complete source code available till this point of lesson is available at
By logging in, you'll unlock full access to this and other free tutorials on JSM Pro.
Why? Logging in lets us personalize your learning experience, track your progress, and keep you in the loop with new workshops, coding tips, and platform updates.
You'll also be the first to know about upcoming launches, events, and exclusive discounts.
No spam—just helpful content to level up your skills.
If that sounds fair, go ahead and log in to continue →
Enter your name and email to get instant access
In this lesson, we explore how to implement a comprehensive user authentication system in a Next.js application. This includes dynamically rendering components based on user authentication status, ensuring secure login/logout functionality, and displaying user information throughout the application.
00:00:02 In this lesson, we'll replace the always present login and sign up buttons, which shouldn't really be there if the user is authenticated,
00:00:09 with actual values we're getting from the session, along with adding all the necessary checks.
00:00:14 We can do that by heading over to components, navigation, and then left sidebar.
00:00:20 Within here, we can fetch the user session by saying const session is equal to await auth coming from add forward slash auth.
00:00:30 And from it, we can extract the user ID by saying const user ID is equal to session?user?id.
00:00:39 And of course, to be able to use await, we have to turn this into an async function.
00:00:44 Now, based on that user ID, we can dynamically render something within this div.
00:00:50 What do you mean by that?
00:00:51 Well, if we have access to a user ID, then we can render a new form within which we can render a button within which we can say log out.
00:01:04 But else, if we don't have a user ID, we will render a new React fragment within which we can put all of the existing code,
00:01:13 meaning the two buttons for sign in and sign up.
00:01:17 That's going to look something like this.
00:01:18 And back in the browser, since we already have an existing session, you can see a logout button.
00:01:23 Now let's style this button just a bit by giving it a type of submit.
00:01:28 And a class name equal to base-medium w-fit.
00:01:34 And oh, is it good to be back at writing some class names on the front end side?
00:01:38 We've been stuck on implementing the authentication logic for so long.
00:01:42 So it's good to be back.
00:01:44 Let's also put an exclamation mark and say BGTransparent because we want this button to be transparent with padding X of 4 and padding Y of 3. Then we
00:01:54 can render a logout icon.
00:01:56 So let's import it at the top by saying import logout from Lucid React, which contains our icons.
00:02:06 And we can render it right here by saying log out.
00:02:09 We can also give it a class name of size 5, text-black, and in dark mode, text-white.
00:02:18 And we can put this logout text within a span because we want to hide it if the device length is not too large.
00:02:24 So we can say class name, maxlg hidden, text-dark300, And now we have our logout button.
00:02:35 Implementing the logout functionality will be super simple.
00:02:39 We can do that by adding an action property to this form.
00:02:43 You know, we use actions to render forms completely on the server side.
00:02:48 And here we can define an async callback function within which we can call the use server.
00:02:55 and right below, we can await sign out, which is coming from add forward slash auth, and you have to call it as a function.
00:03:03 If you do that, let's try out the logout functionality.
00:03:07 Okay, that worked like a charm, immediately.
00:03:10 So now we can head back over to login, and I'll try to log in with Google, and we're back in.
00:03:15 Now, let's display some user information here.
00:03:17 We can do that by passing over the user ID, As a prop over to nav links, then head over to the nav links component and accept it right here by saying user ID,
00:03:30 optional of a type string.
00:03:32 And you can also add it right here to the types user ID, optional of a type of a type string.
00:03:38 Oh, but here we're just getting that user ID so we don't have to declare the types.
00:03:41 Now we can replace this fake user ID by using the one we're getting right here at the top.
00:03:47 What this did is now if you reload the page and click on the profile, you'll actually be redirected to your own real profile ID stored in the database.
00:03:57 You can see it ends with ED2 in my case.
00:04:00 And if you check your MongoDB Atlas, you'll see that your user will also end with that specific ID.
00:04:06 Similarly, we want to make the changes for the mobile navigation as well.
00:04:10 So head over to mobile-navigation.tsx and scroll to the top.
00:04:15 Here, we also want to accept that session by saying const session is equal to await auth coming from add forward slash auth.
00:04:27 And we can put an async right here and then extract the user ID by saying const user ID is equal to session?user?id.
00:04:38 And now we can dynamically render things on the mobile sidebar as well.
00:04:42 If we find the div that has flex, flex call, and a gap of three within it, we can make a check and see if a user ID exists.
00:04:52 Then we want to render a sheet close.
00:04:56 that's going to have as child because it'll have a form in between.
00:04:59 So I'll create a form here.
00:05:02 Else, if we don't have a user ID, we have to show the login and sign up buttons.
00:05:08 So I'll just move this sheet close and the second sheet close right within it.
00:05:14 One, two steps up.
00:05:17 And we have to wrap them in a React fragment if you have more than one element within this block of code.
00:05:23 So I have to close them.
00:05:25 Save it.
00:05:27 And this is how that is looking.
00:05:29 So let's actually check it out on mobile.
00:05:32 You can see that if you open it up, currently we don't see anything because we're locked in.
00:05:39 So let's actually head back over to our left sidebar and copy this entire form.
00:05:45 And we can paste it instead of this form within a sheet close.
00:05:49 Of course, make sure to import the sign out functionality and the logout icon.
00:05:53 And let's see if we have to change some styles.
00:05:55 Nope, this is looking good to me.
00:05:57 The only thing we're going to do is hide this max LG hidden.
00:06:00 We don't need it in this case.
00:06:03 So now if we go back, you can see the logout button on mobile as well.
00:06:08 Great.
00:06:09 Finally, we have to show the user information on the navigation bar because right now we don't really know who we're logged in as.
00:06:17 So let's create a new component in the components folder called user avatar.
00:06:26 Run RAFCE and we can implement it.
00:06:29 And soon enough, we'll implement it.
00:06:31 But for now, head over to the navigation component.
00:06:35 That's the navigation nav bar.
00:06:37 Within here, get access to the session by saying const session is equal to await auth.
00:06:43 I mean, you can see how easy it is to get access to the session now that we have implemented all the logic.
00:06:48 Once you have the session.
00:06:50 right below where we have the theme here.
00:06:53 If a session exists, so if session?user?id, that means that we can show this user avatar.
00:07:04 So let's render the user avatar coming from components.
00:07:08 And to it, we can pass the ID equal to session.user.id.
00:07:12 We can pass a name equal to session.user.name, and we can put an exclamation mark saying that it'll always be there.
00:07:19 And we can do the same thing with the image URL saying session.user.image like this.
00:07:28 We can put a question mark here just in case it doesn't exist.
00:07:31 Now we can head over to the user avatar and we can implement it.
00:07:36 I'll just collapse my code just a bit so we can see how that user avatar looks like right here on the top, right?
00:07:42 First things first, we have to accept all the props, such as the ID, name, the image URL, and maybe even some class names,
00:07:52 which we can later pass.
00:07:54 By default, they're just going to be an H of nine and W of nine.
00:07:58 Then we can return a link component because once you click on this user avatar, we're going to actually link to a user profile.
00:08:06 So we can point to an href equal to routes.profile.
00:08:12 And I think we have to call it and pass in the profile ID.
00:08:16 That's going to look something like this.
00:08:18 Let's just head into it to make sure.
00:08:20 Yep.
00:08:20 It accepts an ID.
00:08:21 And then within that link, we can install a ShadCN component called avatar.
00:08:26 So that'll look something like this.
00:08:28 MPX, ShadCN add latest, add avatar.
00:08:32 Let's press enter and we can now just say avatar right here, coming from UI avatar.
00:08:38 We can pass some class names to it by saying class name is equal to class name.
00:08:43 And if an image URL exists, then we want to render an XGS image tag with a source equal to image URL, an alt tag of name,
00:08:55 a class name of just object cover like this.
00:09:00 We also want to give it a width of 36, a height of 36, and a quality of 100. Because it's very small anyway, so we can at least make it quality.
00:09:12 If we don't have an image, we can display something else.
00:09:15 But immediately, you'll see that our application will break because Next.js is forbidding us to show a Google rendered image because we haven't yet verified
00:09:24 this hostname.
00:09:25 So copy this hostname right here, head over to next.config.ts, and under images remote patterns, Add a new pattern right here with a protocol of HTTPS
00:09:40 and lh3googleusercontent.com.
00:09:43 We'll also have to do the same thing for GitHub.
00:09:46 So you can do the same thing here.
00:09:49 Protocol, HTTPS, hostname is avatars.githubusercontent.com with an empty port.
00:09:56 If you do that and reload, you should be able to see your own avatar image right here at the top, which is looking great.
00:10:04 But now what if a user doesn't have an image?
00:10:07 Let's say that image URL is empty.
00:10:10 In that case, we can use a ShadCN avatar fullback property coming from UI avatar.
00:10:17 We can pass it a class name of primary-gradient font-space-grotesque.
00:10:26 font-bold, tracking-whiter, and text-white.
00:10:33 And within it, we need to pass some initials, like the initials of the first and last name, like JSM maybe, or JS.
00:10:42 First, we need to create that.
00:10:44 So right here at the top, let's say const initials is equal to name dot split.
00:10:51 So we're going to split it into different words by spaces.
00:10:54 Then we're going to call a dot map over each word.
00:10:58 So we can take only the first character of that word, and then we're going to join them together.
00:11:05 We're also going to make them uppercased and we're going to splice them to only take the first two characters.
00:11:11 So from zero to two.
00:11:14 So this is basically how you can get from the full name to just initials of those words.
00:11:19 You can just pass word is going to be a string right here.
00:11:22 And now you can just pass the initials right here within the avatar foldback.
00:11:27 So if we don't have the image, we'll actually show just the initials within the avatar looking photo.
00:11:32 Let's make sure that this is looking good.
00:11:34 We have a name.split.map.join.toUpperCase.slice.
00:11:41 This was supposed to be slice, not splice.
00:11:43 And in this case, we'll still see the image because I'm logged in using my OAuth.
00:11:48 But if I log out now and try to log in with my JavaScript mastery email, where we didn't yet get the opportunity to upload an image,
00:11:56 you can see here that it only says A, as in Adrian, because that was the only thing that was in my name.
00:12:02 So we're making sure that it still looks good, even if a user doesn't have an image.
00:12:06 Finally, we need to define the types right here by saying the interface will be of a type props, and then we can define the interface of props right above.
00:12:16 where we can say the ID will be of a type string, name will be of a type string, image URL optional or null of a type string,
00:12:25 and class name optional of a type string.
00:12:28 And now check this out.
00:12:30 Looking great.
00:12:31 We can also head over to a mobile view and you can see the avatar here as well, which when you click on it, it leads you to your profile.
00:12:39 Of course, I prefer having an image there, so I'll sign in to test this out one last time.
00:12:44 Let's do GitHub this time.
00:12:46 You can see it works wonderfully well.
00:12:48 Oh, this is interesting.
00:12:49 This time I see a different image, but there is an error saying hydration failed because the profile URL actually changed.
00:12:58 Interesting.
00:12:59 You know that hydration errors can happen because of different browser extensions as well, but it would definitely be interesting to see why this specific
00:13:06 error happened.
00:13:07 For now, I'll just close it and reload the page.
00:13:10 As you can see, we are now logged in with GitHub, and it also works on mobile.
00:13:15 So if we head over here, that's looking good.
00:13:18 But we do have to look into that hydration error.
00:13:21 But now that we're seeing the user and that we have the user avatar, let me actually head back over to the homepage.
00:13:27 So that's going to be the primary page in the root.
00:13:29 And I will delete this session where we're console logging it because we don't actually need it on the homepage, at least not yet.
00:13:36 Now we can actually see that we're logged in through the changes in the buttons on the left and on the user.
00:13:42 And if I reload now, actually that made the error go away.
00:13:45 So for now we're good, but if it shows up later on, we can definitely look into it.
00:13:50 And with that in mind, you have now successfully implemented a full robust authentication system.
00:13:57 completely from scratch, completely custom, both with credentials authentication and with OAuth.
00:14:03 As I said before, can this be done with Clark in about five minutes?
00:14:08 Sure.
00:14:08 And that's a great way to do it.
00:14:10 Can it be done in half an hour using OAuth and just slapping it all together?
00:14:14 Sure.
00:14:14 It took us some more time than that because we wanted a robust and scalable solution that will not break.
00:14:21 So with that said, let's say implement log out and show user info on home.
00:14:31 And with that, we're done with this whole authentication segment.
00:14:35 It was a long one, but we explored so many crucial concepts into development of production ready applications.
00:14:42 We created some API routes as well as some server actions.
00:14:47 For those API routes and server actions, we created dedicated error handling methods to make your code more reusable, and just overall you learned about
00:14:56 so many concepts which are crucial for next.js app development.
00:15:00 Now, take your well-deserved break, maybe even a day or two, because this was so hard to build, and then very soon we'll get back into developing the core
00:15:10 of our application.
00:15:11 So now that we have the users and the home page UI done, we can focus on asking questions and implementing actual functionalities of our Devflow application.
00:15:20 So the only thing I gotta say is fantastic work.
00:15:24 Oh, and before I forget, if you reach the end of this authentication section, definitely send over a message on Discord and let us know,
00:15:32 hey, I finished auth and share what you thought about the course so far.
00:15:36 feel free to leave a review on Trust Violet as well, specifying that aha moment that you've gotten with this course, and how it helps you understand something
00:15:44 that you struggled with for such a long time.
00:15:46 Thank you, and I'll see you in the next module.