
Join the Conversation!
Subscribing gives you access to the comments so you can share your ideas, ask questions, and connect with others.
"Please login to view comments"
Subscribing gives you access to the comments so you can share your ideas, ask questions, and connect with others.
How did you manage to remove the blur property and reach here?
Upgrading gives you access to quizzes so you can test your knowledge, track progress, and improve your skills.
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
##Looks like we found a thief monkey By the way, I liked the trick how you reached till here. You have a good sense of humor. You will improve a lot if you join our course with this passion.
var
(function-scoped, outdated)let
(block-scoped, modern and recommended)const
(block-scoped, cannot be reassigned)_
, or $
let let = 5;
is invalid)myVar
and myvar
are different)string
, number
, boolean
, null
, undefined
, bigint
, symbol
Objects
, Arrays
, Functions
Subscribing gives you access to a brief, insightful summary of each lecture to stay on track.
00:00:02 Now that we have our models, database setup, and error handling middleware, we are ready to dive into setting up our auth.
00:00:09 We can do that by opening up a new terminal and installing some packages.
00:00:14 We can run npm install, JSON web token.
00:00:18 Yep, we'll implement a completely custom authentication using JWTs.
00:00:24 And we can also add bcrypt.js, which allow us to generate randomized strings to hash our passwords.
00:00:32 Press enter and head over into our env.development.local.
00:00:37 Let's create a new comment called JWT auth.
00:00:42 And let's create two new environment variables, JWT secret.
00:00:46 This is your string of characters that has to be specific, based off of which you'll be able to generate that randomized string.
00:00:53 For now I'll simply say secret.
00:00:56 You can do the same too, or enter any kind of random characters.
00:01:00 And we can also define when our JWT will expire.
00:01:04 So we can say JWT expires in, And you can set it to any kind of number of days or minutes or hours.
00:01:11 In my case, I'll set it to one day.
00:01:13 Let's not forget to head over to our ENV config and to export those variables.
00:01:18 So right here, we can put this into multiple lines and we can export the JWT secret as well as JWT expires in.
00:01:30 And with that in mind, we can create our first controller.
00:01:35 So what is a controller?
00:01:37 Well, let me create it and show you.
00:01:39 Hopefully it'll all start making sense given the clean code base that we have.
00:01:44 I'll create a new folder called controllers.
00:01:48 And within controllers, I'll create a new file called auth.controller And within it, we'll create a new function and we can immediately export it.
00:02:01 So export const sign up.
00:02:05 And it'll be equal to an async function where we get the request, the response, and potentially the next as well.
00:02:12 And here we can implement the sign up logic.
00:02:15 So this is a dedicated space allowing us to focus explicitly on the logic of what these functions are supposed to be doing.
00:02:24 We can create another one for sign in, which will look like this, and the last one for sign out.
00:02:31 Very soon, we'll add the logic right here.
00:02:34 But the question is, where are these going to be used?
00:02:37 I mean, remember, we already have the auth routes, and we're going to now merge the two together.
00:02:43 Auth routes before handle the logic on their own.
00:02:46 And sure, this is okay if they're all single liners, but this logic can very, very quickly get very, very complicated where we have multiple lines of information
00:02:58 and hundreds and hundreds of lines of logic for each one of these functions.
00:03:02 You can get lost, and at the end of the day, you'll have no idea what different types of routes you have, and this file is all about routes anyway.
00:03:11 So with that in mind, instead of defining the handlers right here, we can define the controller in a separate file and then import it into here by saying,
00:03:21 when you make a POST request to the AuthRouter signup, simply do the logic of sign up.
00:03:29 And that logic will be coming from import sign up from controllers auth.controller.js.
00:03:38 And now we made that connection or the bridge from the auth controller to the auth routes.
00:03:45 Basically, the routes are just the endpoints that you can hit, and the controllers form the logic of what happens once you hit those routes.
00:03:54 So let's also exchange it for the other two.
00:03:57 In this case, we have sign in, as well as sign out.
00:04:03 We're already exporting them from there, so let's just make sure to import them, sign in, and sign out.
00:04:10 And with that, we have a very clean routes file.
00:04:13 So at any point in time, you know which endpoints you can hit.
00:04:18 If you want to, you can even make a comment of something like path API V1 auth sign up.
00:04:26 Why this?
00:04:27 Because in the app, we're actually starting it with API v1 auth, and then in this case, it also has a signup, and this is basically a post route.
00:04:38 You can also write a comment like this for every single one of these routes, but again, might not be necessary because we're writing your code in a clean way,
00:04:47 so it's self-explanatory.
00:04:49 With that in mind, let's head into our controller and let's write our first controller of the day, the controller for the signup function.
00:04:57 I'll do it by opening up a new function block and I'll create a new session by saying con session is equal to await mongoose.startSession.
00:05:10 Now, what does this do?
00:05:13 Well, first of all, we have to import mongoose right at the top by saying import mongoose.
00:05:19 from Mongoose, and this session has nothing to do with the user session.
00:05:25 Rather, it's a session of a Mongoose transaction.
00:05:30 What does that mean?
00:05:31 Well, here, we'll run the session.startTransaction.
00:05:35 And we do this because we want to perform something known as atomic updates, also known as atomic operations.
00:05:43 I found a random photo on Google explaining it, but basically database operations have to be atomic, which means that they either have to do all or nothing.
00:05:52 Insert either works completely or it doesn't.
00:05:55 Update either works completely or it doesn't.
00:05:58 You never get half of the operation.
00:06:00 So if you start authenticating the user but somewhere down the line database fails and it doesn't fully add all of its fields,
00:06:09 if you still add it to the database unfinished you'll get a lot of errors later on.
00:06:14 But if you notice that something went wrong and stop the operation immediately, then you won't have any issues and you'll immediately know that something
00:06:22 wrong happened.
00:06:22 That's exactly what we'll do here.
00:06:24 This is a bit advanced, but bear with me.
00:06:26 We'll open up a try and catch block.
00:06:31 And as I said, if something went wrong, we can immediately await session.aboardTransaction, session and session, and just call the next with that error.
00:06:43 This means if at any point something goes wrong, don't do anything aboard that transaction.
00:06:49 But here, we can actually try to insert a new user, and then at the end, we will commit that session, which means that we are ready to commit to all of
00:07:01 the actions we have done above.
00:07:02 Hopefully it makes sense.
00:07:04 I'll try to explain it as we go.
00:07:06 Basically, the goal of this function is to create a new user, so let's get the data needed to know which user we want to create.
00:07:14 In backend APIs, you typically get all of that information through a request body.
00:07:21 So what is a request body?
00:07:23 Let's explain it.
00:07:24 What is a request body?
00:07:27 A request body is an object containing the data coming from the client, specifically when you have a POST request.
00:07:36 Why am I mentioning the POST?
00:07:38 Well, because if you check the routes right here, you'll notice that the signup functionality is indeed a POST route.
00:07:46 And opposed to just a GET request where you simply make a call and that's it, On the POST request, you're also allowed to pass some data.
00:07:55 In this case, that data will of course be something like the username or email or password.
00:08:02 So we can destructure the past values from the front end right here.
00:08:06 const name, email and password is equal to request.body.
00:08:11 After that, we can check if a user already exists.
00:08:16 We can do that by saying const existing user.
00:08:20 And here we'll make our first database call by using mongoose and saying await.
00:08:28 user, keep in mind this user is referring to the user model that we created not that long ago, and we'll say user.find1 where the email is the same as
00:08:41 the one that the user is trying to create a new user account with.
00:08:44 Then if an existing user or ID exists, In that case, we can create a new error by making it equal to new error, user already exists,
00:08:57 and we can set the status code to 409 and throw that error.
00:09:02 This typically means already exists.
00:09:05 If it doesn't exist, we can hash the password for the new user.
00:09:10 Hashing a password means securing it because you never want to store passwords in plain text.
00:09:17 So how does it work?
00:09:19 Well, first you need to get access to something known as salt, which is like a complexity you want to use for randomizing your hashed password.
00:09:27 So let's say salt is equal to await bcrypt dot gen salt.
00:09:34 So this is a method and typically the default number is 10. Make sure to import bcrypt coming from bcrypt right here.
00:09:45 That's bcryptjs.
00:09:47 Once we have the salt, we can get the hashed password by saying const hashed password is equal to await bcrypt.hash.
00:09:57 You pass the plain text password from the user and the salt you generated right above.
00:10:04 Once you have it, we are ready to create a new user by saying const new user is equal to await user.create.
00:10:13 And now you can pass an array of how many new users you want to create alongside with their info.
00:10:19 For now, we only want to create a single user.
00:10:22 So I'll pass their name equal to name, email equal to email, and password equal to hashed password.
00:10:31 Now, something could go wrong when creating this user.
00:10:34 So that's why we want to also attach a session to it, which means that if something goes wrong and if we later on abort that transaction,
00:10:44 then the user will not be created.
00:10:46 But if you reach the end of the try block means we're good and we'll actually commit the transaction, which will create the user.
00:10:53 Finally, let's generate a token for the user so they can sign in.
00:10:57 Cons token is equal to JWT.
00:11:01 we have to import JWT at the top, coming from JSON Web Token, and we can say JWT.sign, which will take in an object of user ID,
00:11:15 is equal to new user zero dot underscore ID.
00:11:21 We're calling zero right here because when you pass an array of documents you create, it will return the array of new users.
00:11:29 So maybe it's going to be better to call this new users and then we get the first and only one which we create and get their ID.
00:11:36 And as the second parameter, you have to pass the JWT secret.
00:11:41 And as the third parameter, you can pass an object with additional options such as expires in, and you could manually type it,
00:11:50 but we already created a JWT expires in environment variable.
00:11:55 Hopefully this makes sense.
00:11:57 We are attaching a JSON web token to the user with the following ID.
00:12:02 Once you do that, we are ready to commit the transaction, end the session, and finally return the res.status of 201, which means created.json.
00:12:16 And we can return the success is equal to true.
00:12:20 Message is equal to user created successfully.
00:12:25 And we can pass the data such as the token, as well as user, which is equal to new users zero.
00:12:33 This is the function to create a new user.
00:12:37 So let's go ahead and collapse it.
00:12:40 And we're already exporting it and using it right here under our auth routes.
00:12:46 So as soon as somebody hits this endpoint, this function will be executed.
00:12:51 So let's test it together.
00:12:52 You could either watch me do it or you could download one of these HTTP clients and test it on your own.
00:12:58 So we have to head over to API v1 auth, and then make a post request, but we also have to pass a body.
00:13:07 And this body will be in form of a JSON.
00:13:10 So I'll create a new object.
00:13:12 And remember, now we have to specify these three things, which our code will then accept.
00:13:18 The name, email, and password.
00:13:20 So for the name, I'll do something like Adrian JS Mastery.
00:13:26 And we're working within JSON, so make sure to wrap everything in double-quoted strings.
00:13:33 The email will be something like contact at jsmastery.pro.
00:13:40 And finally, the password can be something like 123123. Perfect.
00:13:47 The body seems to be good and we are ready to make a request.
00:13:51 This time, instead of getting simply the title of create subscription, we should get back whatever this function is returning.
00:14:00 a 201 with a success true, a message, and the data of the newly created user.
00:14:07 So let's give it a spin.
00:14:09 I'll click send and we get a 404. Oh, that's because I just hit auth.
00:14:15 But in this case, if you take a look at our path, it actually has a forward slash sign up as well.
00:14:21 So we can differentiate it from sign in and sign out.
00:14:24 So if I append, sign up to it, and click send, you can see false, user is not defined.
00:14:30 Okay, this is good in a way, because at least we got some meaningful information right here.
00:14:35 See, success is false, and then we get the error message.
00:14:39 If that sounds familiar, it could be coming from error middleware, where we actually return a successive false and then a meaningful error message if it
00:14:49 comes to it.
00:14:49 And in this case, the error message is user is not found.
00:14:53 So this is helpful to us.
00:14:55 If we now search for user, you can see that I'm mentioning it a couple of times because this is all about users.
00:15:02 But I think the one that we should be concerned with is this one right here, where we're trying to create a new user.
00:15:10 You don't create it with a lowercase user in mind.
00:15:13 You create it based off of the user model, which starts with a capital U.
00:15:19 It's this model we created based off of all of these properties.
00:15:23 So now that I fixed this typo, I'm actually glad it happened because we got a chance to explore how to handle errors.
00:15:29 I'm ready to send another request.
00:15:32 So I'll click send.
00:15:33 And this time we got yet another error saying JWT secret is not defined.
00:15:38 This time it's referring to this one right here.
00:15:41 And if you can see, I forgot to import it from our ENVs.
00:15:45 So let's get it right here.
00:15:48 save the file, and send it once again.
00:15:52 This time, we get a 201 created.
00:15:56 You can see all the information about the request that is being made, success true, message user created successfully, and the data with the user's token,
00:16:05 as well as their name, email, and a hashed password.
00:16:10 Would you look at that?
00:16:11 We also get a user ID.
00:16:14 and created that and updated that fields.
00:16:16 Now what I would recommend is saving this user token as we might need it for future requests.
00:16:22 So maybe add it, I don't know, let's add it to our ENV development just so we know.
00:16:28 And I'll add a comment of token and paste it right here.
00:16:34 And we can also add another one for the underscore ID.
00:16:39 So if we need to get the ID of our user very quickly, we can always grab it from this file.
00:16:45 Great.
00:16:46 And since we got a 201, that must mean that the user was indeed created in the database.
00:16:51 So if we head over to our clusters and click on cluster zero, head over to collections, you should be able to see users and you can see a new user created
00:17:03 in database.
00:17:04 That means that our first post API call succeeded and it talked to the database and created a new user.
00:17:11 Now, if you have any issues with MongoDB, you might need to head over to network access and add an IP address and click allow acts from anywhere.
00:17:21 Sometimes it stops some IP addresses so you cannot make changes.
00:17:25 So if you do this, it'll just work for all IPs.
00:17:28 Great.
00:17:29 Now let's head back to the code to AuthControllers and let's create the signIn function, which will allow us to sign our user in.
00:17:39 I'll do it right here.
00:17:41 And first I will open up a try and catch block.
00:17:47 In the cache, I'll simply get the error and forward it over to our error handling middleware.
00:17:53 But in the try, I'll try to destructure this time just the email and the password from the body.
00:17:59 No need to get the name because we're already signing in to our account.
00:18:04 Then we'll check if a user exists by saying const user is equal to await user.find1 with a specific email.
00:18:15 And by the way, if you're wondering how this filter thing popped up right here, that is something that WebStorm IDE adds.
00:18:22 It automatically recognizes what this find1 method wants as the first parameter, and it tells us what that is.
00:18:29 Next, if a user doesn't exist, we can throw a new error, something like user not found 404. But if it does exist, we can try to validate their password
00:18:40 by saying const is password valid.
00:18:44 And we'll do it by using await bcrypt coming from bcryptjs.compare.
00:18:52 We want to compare the new password they entered with the user password stored in the database.
00:18:58 Of course, bcrypt will actually hash this new password that you're typing and compare it with the hashed password in the database.
00:19:05 I'll just spell it properly, that is compare and make sure to import bcrypt from the top.
00:19:10 Next, if it's not valid, so if password is not valid, we can throw another error saying new error invalid password with a 401, which means unauthorized.
00:19:21 But if it is valid, we can generate a new token.
00:19:26 which will be similar to what we had before.
00:19:28 JWT.SIGN userID is equal to user.underscoreID.
00:19:33 This time, we don't have to destructure the first element from it because we're not adding that session transaction because this is just a sign in,
00:19:42 not a sign up.
00:19:44 So we're not creating anything.
00:19:46 We pass over the JWT secret and the expires in functionality.
00:19:51 Once we do that, we are ready to return a res.status of 200, which means successful.
00:19:57 And as a JSON object, we'll pass success true, user signed in successfully, and we will return the data of the token and the user.
00:20:07 Great.
00:20:08 So now we have the functionality to sign our users in as well.
00:20:12 Let's make sure that we have actually passed it as a controller to the sign in route.
00:20:17 And we can actually test it out.
00:20:19 If I now go over to sign in.
00:20:23 And I remove my name because we don't need it, but I do need the correct email and password.
00:20:29 We should be able to sign in successfully.
00:20:32 So I'll click send.
00:20:34 And you can see user signed in successfully and we get a new token if needed.
00:20:39 Beautiful.
00:20:40 If you try to sign in with an invalid password, like something like 123456, you can see invalid password, or if you try to sign in with an email that doesn't exist,
00:20:52 maybe you make a typo, you'll see user not found.
00:20:56 Beautiful.
00:20:57 Complete error handling, so if you now had a beautiful front-end application, you would be able to nicely show these errors on the front-end,
00:21:05 allowing your users to see what they did wrong.
00:21:07 We're using this HTTP client to mock what the user would be seeing on the front-end, but basically, this would be a form.
00:21:14 And what does that form do?
00:21:16 Well, it basically calls our API.
00:21:19 That's how it goes.
00:21:20 You make a nice form on the front end, and you make a call to API v1 auth sign up, and you pass all the post request details,
00:21:31 like the post body, such as the name, email, and password, and it creates a new user.
00:21:38 Make sense?
00:21:40 Hopefully it does.
00:21:41 And with that, we're ready to move to the second part of creating our users, and that is authorization.
00:21:47 Once we've created our users, we need to be able to authorize them, meaning make a get call to the database to figure out which users are in there.