The Express. js Course - Module 9: Authentication and Authorization | Shivendra Raghuvanshi | Skillshare
Search

Playback Speed


1.0x


  • 0.5x
  • 0.75x
  • 1x (Normal)
  • 1.25x
  • 1.5x
  • 1.75x
  • 2x

The Express. js Course - Module 9: Authentication and Authorization

teacher avatar Shivendra Raghuvanshi, Lead Developer and Online Teacher

Watch this class and thousands more

Get unlimited access to every class
Taught by industry leaders & working professionals
Topics include illustration, design, photography, and more

Watch this class and thousands more

Get unlimited access to every class
Taught by industry leaders & working professionals
Topics include illustration, design, photography, and more

Lessons in This Class

    • 1.

      Class Introduction

      2:38

    • 2.

      Authentication and Authorization - An Introduction

      4:41

    • 3.

      Creating the User Model

      4:04

    • 4.

      Registering Users

      8:33

    • 5.

      Using Lodash

      6:03

    • 6.

      Hashing Passwords

      8:23

    • 7.

      Authenticating Users

      5:20

    • 8.

      Testing the Authentication

      2:48

    • 9.

      JSON Web Tokens

      5:58

    • 10.

      Generating Authentication Tokens

      4:36

    • 11.

      Storing Secrets in Environment Variables

      7:25

    • 12.

      Setting Response Headers

      4:30

    • 13.

      Encapsulating Logic in Mongoose Models

      8:17

    • 14.

      Authorization Middleware

      8:44

    • 15.

      Protecting Routes

      3:50

    • 16.

      Getting the Current User

      4:50

    • 17.

      Logging Out Users

      2:30

    • 18.

      Role-based Authorization

      6:59

  • --
  • Beginner level
  • Intermediate level
  • Advanced level
  • All levels

Community Generated

The level is determined by a majority opinion of students who have reviewed this class. The teacher's recommendation is shown until at least 5 student responses are collected.

1

Student

--

Project

About This Class

Module 9: Authentication and Authorization focuses on the essential concepts of authentication and authorization in modern web applications. You will learn how to implement robust security measures to protect your APIs and manage user roles effectively. From registering users to generating and verifying JSON Web Tokens (JWT), this module equips you with everything you need to secure your backend applications.

What You’ll Learn

  • Build a User Model with authentication capabilities.
  • Implement secure user registration with hashed passwords.
  • Authenticate users with JWT-based Authentication.
  • Protect sensitive routes with role-based authorization and middleware.
  • Utilize environment variables for secure storage of secrets.
  • Manage user sessions, logouts, and access levels for different roles.

Meet Your Teacher

Teacher Profile Image

Shivendra Raghuvanshi

Lead Developer and Online Teacher

Teacher
Level: Intermediate

Class Ratings

Expectations Met?
    Exceeded!
  • 0%
  • Yes
  • 0%
  • Somewhat
  • 0%
  • Not really
  • 0%

Why Join Skillshare?

Take award-winning Skillshare Original Classes

Each class has short lessons, hands-on projects

Your membership supports Skillshare teachers

Learn From Anywhere

Take classes on the go with the Skillshare app. Stream or download to watch on the plane, the subway, or wherever you learn best.

Transcripts

1. Class Introduction: Welcome back to the Express Jazz course, Model nine, authentication and authorization. My name is ShenRagunhi, and I will be your instructor for this exciting module. Over the years, I have had the privilege of building secure, scalable backend systems for a variety of applications, and I'm here to share my expertise with you. One of my best projects involved designing a user authentication system for an e commerce platform, which significantly improved its security and usability. Today, you will learn to build something just as impactful. In this module, we are diving deep into authentication and authorization, two critical aspects of modern web development. So you'll learn how to register users securely using hashed passwords, authenticate users with JCN webtkens, protect sensitive routes with middleware, and finally, implement role based authorization to manage user permissions. This knowledge is crucial for securing any backend application, and by the end of this module, you will be confident in implementing these features in your projects. So this module is designed for Bg in developers who want to take their Express JS applications to the next level. If you have completed the earlier modules, you're all set to start. Basic knowledge of JavaScript, no JS, and Monger DB is recommended. Authentication and authorization are cornerstones of any secure web application. By mastering these concepts, you will not only build safer applications, but also increase your value as a developer. These are must have skills for creating user centric platforms in today's tech world. And finally, for the project, you will integrate everything you have learned into the Fair Wheels app. So more specifically, you will implement user registration and login functionality, securely authenticate users using JCN web tokens. Apply role based authorization to protect routes and resources. By the end, you will have a secure and scalable user management system for the Fair Wheels app. This module is a game changer for your Bend development journey. So let's build secure applications together. I'll see you in the first lecture. Let's get started. 2. Authentication and Authorization - An Introduction: All right, so back to our Fair Wheels application. So far, we have built these API endpoints. So we can manage companies, cars, customers, and rentals. Now, nearly all applications out there require some kind of authentication and authorization. So in this section, we're going to take this application to the next level and implement authentication and authorization. So before we go any further, I want to make sure that we are on the same page. So authentication is the process of identifying if the user is who they claim they are. That's when we login. So we send our user and password to the server, and the server authenticates us. Authorization is determining if the user has the right permission to perform the given operation. So in our Fair Wheels application, we want to make sure that only authenticated users or only logged in users can perform operations that modify data. So if the user is anonymous, if they are not logged in, they can only read data from these endpoints. If they want to create a new company or update a car, they have to be authenticated first. Now as an additional security, we want to make sure that only admin users can delete data. So that's a second level of authorization. We're talking about permissions here. So these are the requirements we are going to implement in this section. So to do this, we need to add two new endpoints to our application. First, we should be able to register users. For that, we are going to send a post request to slash API slash USERS because we post, we create new resources. In this case, a new user. We should also be able to log in a user, and that's used for authentication. Now, here's a question for you. What SDDP method should we use to implement login? Because with login, we are not creating a new resource. We are not updating or removing an existing one. So how can we implement this in restful terms? This is one of those scenarios that you may encounter frequently in real world applications. Sometimes the operation you are dealing with doesn't have that create, read, update, delete semantic. The way we model this in restful terms is by referring to this as request or command. So you're creating a new login request or a login command. In that case, we will use post because we are creating a new resource, so slash API slash Logins. Now in your application, maybe you want to store all the logins into the application in a separate collection in Mongo Deb. So you can see using post makes perfect sense here. But even if you don't store individual logins and you just want to validate the user and password, you can still treat this resource as login resource and use post to create it. Now, here's an exercise for you. I want you to implement this API to register new users. So for each user, we want to have these properties, name, email and password. Also, when defining your schema for the email property in the schema type object, set the unique property to true. So when we define the schema, we set the type of email to an object that's our schema type object. We set the type here to string and also set unique to true. So with this, we ensure that we will not store two documents with the same email in Mongo Di B. Okay. So go ahead and implement only this API to register new users. I'm going to do the same in the next lecture. 3. Creating the User Model: All right. So first, I'm going to define a new user model. So here in the models folder at a new file user dot js. Now to save time, I'm going to go to company dot js and borrow some code from here. So copy back touserdtjs, paste it here. Now on the top, so we have this schema. I'm going to define this while calling the model method. There is really no need in this case to define this as a separate constant. So let's get this schema object and add it here. And now we can get rid of this schema. Okay, that's better. So that's our model. We should call this constant user. And the corresponding collection should also be users. Now here, we have name property, which is a string. It's required, and it's 5-50 characters. That sounds good to me. Next, let's add email. So I'm going to grab all this good, duplicate it. And the second property is email. So again, we have these properties. I would like to increase the maximum length to 255 characters. And also, as I told you in the last lecture, we should add the unique property to make sure we don't store two users with the same email in Manga Divi. And the last property is Passwb. So I'm going to copy all this, duplicate and add this password property. Now I'm going to set the max length of passwords to a higher value because we are going to hash these passwords. And here we don't need the unique property. Next is our validate function. So we need to rename this to validate user. Validate user that takes a user parameter. Here we have name which is 5-50 characters, and it's required. We have email, which should be 5-255 characters. It should be required. And here we also call the email method to make sure it's a valid email. And finally, we have password, which is also a string with minimum five characters and maximum, let's say, 255 as required. So this is the password that the user sends in plain text. We're going to hash this, which is going to be a longer string, and that's the string we will store in Mongo TV. So we are done with our valid user function. Now we need to export our user model, so we don't need the first export statement that's for our schema delete. Here we're going to export this as user, and our validate function is also good. Beautiful. So we are done with our user model. In the next lecture, I'm going to add a route to register new users. 4. Registering Users: All right. Now we are going to create a new route to register new users. So here in the routes folder, I'm going to add a new file users dot js. Now, once again, to save time, I'm going to go to companies dot js and copy these required statements as well as the second route into our new module. So copy, paste it here. Again, copy the post route. And paste here. And by the way, I'm only doing this because I don't want to waste your time watching me type all this by hand. In the real world, you should avoid copy paste approach unless you are very careful and you are going to read every single line of the copied code to make sure you didn't make a mistake. It's always better to type the code by hand. Now, let's make changes. So on the top, we need to import the user model instead of company. So from models user, we import the user class, as well as the validate function. We also need Express and Router. So here's our new route which is posed for creating new users. And finally, we need to export this router. So module dot Exports. We set this to this router. Now, we need to go back to our index dot JS and tell Express that for any routes that starts with slash ABS SRS, we should use this router that we're exporting here. So let's go to Index or Js. On the top, let's import this new module. So constant users. We set this to require from the routes folder users. And then here we call app that give our path slash APAs Users and our Router, that is users. Okay, so we have built the big picture. Now, let's go back to our users module and implement this new route. So here we are going to validate the request. If it's not valid, we are going to return a 400 error, which is bad request. Otherwise, we are going to create a new user object and save it to the database. So we are going to keep the first two lines exactly as they are. We want to validate the request. We are using our Joy validate function. So if the name, email, or password properties are not valid, we are going to return a 400 error to the client. Now, next, we need to do another kind of validation. You want to make sure that this user is not already registered. So we call user dot Fine one, pass a query object here. We are looking for user with a given email that is request dot body dot email. So note that here, I didn't use fine by ID because we are not looking up the user by their ID. We are looking them up by one of their properties. So fine one. Now this returns a promise. So we await it and get a user object. Now, in this case, I'm defining a variable instead of a constant because we are going to reset this as you will see in a second. So in this case, if we do have this user object in the database, we should return a bad request error to the client. So return response dot status 400, which is bad request dot SEND and here's the message user already registered. So at this point, we have a valid user object. This user is not registered in the database, so we need to save this user in the database. So here, I'm going to reset this user object because at this point, it should be no. We set this to a new user, and here we set the properties. So name we set this to request dot body, dot name. Email to request dot body dot email. And password to request dot body password. So this is our user object. Now we save it. So await, user dot save, and finally, return this to the client. So we don't need these two lines for working with the company. Delete. And finally, return this new user to the client. Now, let's test this. So back in Postman, I'm going to send a post request to Local Host port 3,000 slash APAS USERS. And then in the body of the request, I'm going to set this to Raw and set the type to JSON. Here we pass as JSON object with three properties, name, Shiv, then we set email. I'm going to set an invalid email, and I'm not going to set the password either. I want to make sure our validation function is working properly. So send. Okay, we got a bad request. That's good. Name length must be at least five characters long. So let me change this to hewnder Alright. Now let's send another request. We got another bad request. Email length must be at least five characters long. So let's change this to 12, three, four, five, six, but this is not a valid email. So now we should get a different error. Email must be a valid email. Beautiful. So let's change this to test 123 at regmil.com. Et's send another request. Okay, now we get password is required. Beautiful. Finally, let's add a password that is at least five characters long. So one, two, three, four, five, Send. This time we got a 200 response. Beautiful. And this is the user object that you stored in the database. So you have the ID property as well as name, email and password. Now, when registering a new user, we don't want to return their password to the client. So that's something we are going to fix in the next lecture. But let's send another request with the exact same values. This time, we should get a different error, telling us that the user is already registered. So send. Okay, we got another bad request, and here's the message. User already registered. Beautiful. So in the next lecture, we're going to modify the response of this API and point. 5. Using Lodash: So back in our post method, we want to modify the response to the client. So there are two options here. One approach is to return a custom object like this, setting the name to user dot name and email to user dot email. So this way, we can exclude the password and the version properties. This approach is perfectly fine, but in this lecture, I'm going to introduce you to a useful library that gives you a lot of utility functions for working with objects. If you are an experienced JavaScript developer, you will probably know what I'm talking about. That's Lourdes. So head over to lodash.com. This is lodash, which is basically the optimized version of underscore. Underscore has been around for a long time. It has a lot of utility engines for working with objects, strings, arrays, and so on. So if you look in the documentation, here we can see all the utility functions that we have for working with various types. We have lots of utility functions for working with arrays, numbers, strings, objects, and so on. So lodash is extremely powerful. And in this lecture, I'm going to show you how to use it in your node application. So back in the terminal, NPM install lodash. So note that the current version I'm using 4.17 0.21. All right now back in the code. So here in our user's model on the top, I'm going to import Lodash. So require Lodash. Now by convention, we store the result in a constant called underscore. You can call this anything, you can call this Lodash, but by convention, we use underscore because that's short and clean. Now this underscore object that we have here has a utility method called pick so underscore dot PIC. We give this an object. In this case, our user object, and then pass an array of properties in this user object that we want to pick. And this will return a new object with only those properties. So here in this array, I'm going to pass name and email. So when we call this pick method, we'll get a new object with these two properties, name and email. Now instead of manually repeating the user dot, we can use this Pi method. That's more elegant. So we can replace this object here with what we get from the PI method. As simple as that. Now, perhaps here, you may want to include the IR property as well. So that's how we use the PIC method. Similarly, we can modify this code, and instead of repeating request dot body dot, request dot body dot, request dot body dot with underscore dot PIC. So I'm going to replace this object with underscore dot pig. We gave it request dot py. Now here, we might have 50 properties. A malicious user may send us properties to be stored in the database. We only want to hand pick a few of these. So we pass an array. We are only interested in name, email and password. Okay, so we create the user and save it and then return this custom object to the client. Let's test this one more time to make sure everything is working. So backend terminal. Let's run the application. Beautiful. Now back in the Postman. I'm going to send a new request through the server. Send. Okay, here's our new user object. We only have ID, name, and email. Now in all these requests so far, I have sent really simple passwords. If you want to enforce password complexity, there is an NPM package built on top of Joy called Joi password Complexity. So back in Google search for Joy password Complexity. That's the NPM package. So with this, you can configure an object that determines the password complexity in your application. Minimum number of characters, maximum, how many lower as or upper case you want to have, how many numbers, and so on. So the name of the package is Joy password complexity. In this course, we are not going to use this because that's something you can do on your own. All right, our new API endpoint is in good shape, but we are storing our passwords as plain text, and this is very, very bad. So in the next lecture, I'm going to show you how to hash these passwords. 6. Hashing Passwords: In this lecture, I'm going to show you how to hash passwords. For that, we are going to use a very popular library called B crypt. So here in the terminal, let's install B crypt. Install. Note the version that I'm using. That's version 5.1 0.1. Now here, I'm going to create a playground file, so you'll learn how to work with this BCRP library, and then we are going to take that code and put it in a route for hashing the password of new users. So let's create a new file, hash dot js. In this file, first, we need to load BCRP. This retains an object. We store it here. B crypt. Now to has a password, we need a salt. What is a salt? Well, imagine our password is one, two, three, four. When we hash it, let's imagine we will get a string like this. Now, this hashing algorithm is one way. So if we have A, B, CD, we cannot decrypt this and get one, two, three, four. So from a security point of view, that's great. If a hacker looks at our database, he or she cannot decrypt these hashed passwords. However, they can compile a list of popular passwords and hash them. And then they can look at the database of our application. They find this hash password, and they know that ABCD represents one, two, three, four. So that's why we need a SALT. As SALT is basically a random string that is added before or after this password. So the resulting hashed password will be different. Each time based on assault that is used. Okay, let me show you this in action. So here we call B crypt dot Gen SLT. Note that this method has two versions. The first one is asynchronous. The second one is synchronous. As a best practice, you should always use asynchronous methods because as I told you at the beginning of the course, in node applications, we have a single thread. We don't want to keep that thread busy because then we can't serve other clients. So we call GenSLT as an argument. We pass the number of frowns. We want to run this algorithm to generate the SLT. Higher the number, the longer it's going to take to generate the salt. And also the SOT will be more complex and harder to break. So the default value is ten. We're going to use that now because this is an asynchronous method. We can either pass callback here, and that's what you see in their official documentation, as well as a lot of tutorials on the web. But this method also has an overload that returns a promise. So instead of passing a callback, we get a promise, await it, and then get the salt. So now we need to wrap this in an acing function like run. Okay, so let's lock this salt on the console. And finally, let's call this run function. Now, back in the terminal node has to Js. So this is an example of salt. You can see the number of rounds we used included in the salt. So if we use 20 here instead of ten, we would have 20. So here we have a long random string that is included as part of hashing our passwords. And with this, every time we hash our password with a new Salt, we get different results. So now that we have a Salt, we can use this to hash our password. We have another method here on this B crypt object that is hash. So we give it our data. Let's imagine our password is one, two, three, four, and give it the salt. And again, look, the third argument can be a callback. We are not going to use this. Instead, we're going to get the promise that is returned from this method. So we await that promise and get the hashed password. So now let's log this on the console as well. Hashed. Okay, back in the terminal node, hashed or chase. Okay, look, on the first line, we have our salt. On the second line, you can see the salt here as well. So the SALT is included in the hashed password. The reason this is included is because later when we want to authenticate the user, we want to validate their username and password. So there the user sends a password in plain text. We need to hash it again, but we need to have the original salt that was used to generate this hash. So during comparing the plain text password with the hashed password, B crypt needs to know the original salt that was used to hash the password. So now that you know how B Crypt works, let's take these two lines and put them in our route handler. So cut them from here. Let's go to users dot js. Okay, here's our user object. Let's pase those lines here. So we generate a salt, and then we need to has the password. Instead of one, two, three, four, we're going to use user dot password. That's plaintext password. So we hash it with a salt and then reset it. So user dot password. Now on the top, we also need to import B crypt, so constant B crypt. We set this to require Bcrypt. Now, here in Compass, I want to delete the user's collection because all the users we have so far, they have plain text password. So delete. All right. Now back in the terminal, let's run Node mod. And then here in Postman, we are going to send a new request. S. Beautiful. So here we have a new user. Now back in Compass, let's refresh the list. We have the user's collection, and here's our new user with a hashed password. Beautiful. So we have implemented this endpoint to register new users. We are done here. Next, we are going to look at authenticating users. 7. Authenticating Users: All right, so here in the routes folder, let's add a new file. We call this logins dojs or auth dot js. Either works, but auth is more common. So auth Js. Again, you save time, I'm going to borrow some code. So let's go to users dot js. I'm going to copy all the code that we wrote in this section, and then paste in auth dot js. Now before we get into the details, let's go to index dot JS. On the tap, import this new module. So Cs OT, we set this to require routes OT. So if we have any request to this endpoint, slash API OT or one of its child endpoints, we are going to delegate this to the OT router. So the big picture is done. Now let's look at the details. So back in the Auth module, on the top, first, we need to validate the body of the request. But this validate function we have here, this is the one that we imported from our user module. So this is validating that. In the body of the request, we have three properties, name, email and password. And in the real world application, you might have other properties as part of registering a user. So this validate function is for validating a new user. It's not for validating the email and password that we expect in this endpoint. So here we need a different validate function. So I'm going to remove this validate function from here and define a separate validate function in this module. Now to save time, I'm going to go to user module and copy that validate function. So we have validate user. Let's copy this back to the Auth module, paste it here, and then change this to validate. Now we can change the parameter to request. Now for this schema object, we need only two properties, email and password. So let's delete the name and we are done. So back to our route handler, this is our first validation. Next, we need to make sure that we do have a user with a given email. So we load the user. If we don't have the user, so here we apply the not operator. If we don't have the user, we should send a 400 error to the client, which means that request and the message should be invalid email or password. Note that here, I'm not sending a four or four, which means not found because we don't want to tell the client why the authentication fail. We don't want to tell if this email is correct or the password. So we don't want to say we don't have a user with a given email. We just tell the client that this is a bad request. It doesn't have the right data to be processed. So this is for validating the username and email in this case. Next, we need to validate the password. For that, we need to use BCRP. So let's delete all this code. This B crypt object that we have has a compare method. We use this to compare a plain text password with a hashed password. So our plain text password is in request dot body dot password, and our hash password is in user that password. So as you saw earlier, this hash password does include the SLT. So when we call the compare method, B crypt is going to get that salt and use that to rest this plain text password. If they are equal, then this will return true. So here we need to await this promise and store the result here in valid password. Now, if the password is not valid, again, we are going to return a 400 error with that vague message, invalid email or password. So I'm just going to copy this and paste it here. And finally, if we get this to this point, that means this is a valid login. So for now, I just want to send a simple, true value to the lint. 8. Testing the Authentication: So for now, let's test this authentication route. Before that, so an of dot js. Here, I forgot to mention load joy. So let me quickly do this. Last Joy, set this to require Joy. Okay. And at the bottom for validate function, here we need to return schema dot validate request. Okay, so we're all set. Let's open postmin. So here I'm going to open a new tab. Let's select post here. Let me type the endpoint. It's TP, local host OT, API OT. And in the body of request, I'm going to send the GSN out. So let me select Json here. And now we have two parameters. So first one is email. The email that we have used is a 23, four, five, six, seven, at the rate gmail.com. And our password is one, two, three, four, five. So let me send this request, SN. Beautiful. We have a status 200. Okay. And here's our response through. Now, let me change the password here to something else like 67 and then send the request again. See, we have a 400 bad request and the generic command, which is invalid email or password. Now let us change the email to something else with the correct password. So something like this. And let me send the request again. So send. See, again, we have a 400 bad request and the generic message which is invalid email or password. So our authentication endpoint is working fine. In the next lecture, we're going to see the response that we are sending here and change it to something what we call adjacent web token. 9. JSON Web Tokens: So we have an endpoint for authenticating users. Now, we need to modify the response here, and instead of returning a true value, we need to return a JCN web token. AJCN webtoken is basically a long string that identifies a user. As a metaphor, you can think of it as your driver's license or your passport. It's very similar to that. So when the user logs in on the server, we rod this JCN web tooken, which is like a driver's license or passport, we give it to the client and then tell them, Hey, next time you want to come back here and call one of our API endpoints, you need to show your passport. You need to show your driver's license. This is your ID. So on the client, we need to store this JCN web token, which is a long string, so we can send it back to the server for future API calls. Now, the client can be a web application or a mobile application. If it's a web application, if you're building an application with angular or react, you can use local storage. That is a special storage place that is available in every browser out there. If you're building a mobile app, you have a similar option depending on what platform you use. So now let me show you an example of AJCNwbTken. Head over tojwt dot IO. On this website, here we have a debugger for working with JS and WebTkens. So here in the encoded section, you can see a real example of AJCN Web tooken. This long string that you see in the encoded section represents a JCNObr so when we decode this, we'll get a CN object. Okay? Now we can see that this string has three parts, and each part is color code. So the first part is red, the second part is purple, and the third part is blue. On the right side, you can see this string decoded. So the red part is what we call the header of a JCN web token. In this header, we have two properties. One is L, which is short for algorithm that determines the algorithm used for encoding this token. The type is GWT, which is JCN web token. Now we never have to worry about this header. It's just a standard. What matters to us is the second part, the payload, which is the purple part. So here we have a JCN object with three properties, sub, which is like a user ID, name, and Admin. Now, the payload that you see is different from the payload I have because I have modified the fold JS and webtog and it is on this website. So I generated a custom web token, put it here. What I want to point out here is that this payload includes public properties about the user. Just like on your passport, you have some properties about yourself, like your name, your date of birth, your place of births, and so. So we have the exact same concept ren adjacent web token. Can include a few basic public properties about the user. And with this, every time we send a token from the client to the server, we can easily extract the user ID from the payload. If we need to know the name of the user, then we can simply extract that here as well. We don't have to query the database, send this ID to get a user object and then extract the name property. By the same token, if you want to know if the user is an admin user or not, we can include that here. So again, we don't have to send an extra query to the database to see if the user with a given ID is admin or not. Now you might be concerned about this approach from a security point of view, because you may think anyone can simply set this admin property for themselves to true, and then they will be treated as admin on the server. But that's not how Jason ptocans work. The third part of this JCN Web token, which is in blue, is a digital signature. This digital signature is created based on the content of this JCN WebTken along with a secret or private key. The secret or private key is only available on the server. So if a malicious user gets the JCN WebTken and modify the admin property, the digital signature will be invalid because the content of the JCN WebTken is modified. Now we need a new digital signature, but the hacker cannot generate this digital signature because they will need the private key, which is only available on the server. So if they don't have access to the server, they cannot create a valid digital signature. And when they send this tempered JCN Web token to the server, the server will decline that. The server will say, this is not a valid JCN web token. So this is how JCN Web tokens work. 10. Generating Authentication Tokens: So on this page, if you look at the libraries, here you'll find different modules or libraries for different platforms. So if you scroll down, you will see you have Tad net one C B, C, C plus plus, node, and so on. So let's open the terminal and install JCN webtook and BM and install ASN web took. Okay, if you look at the package a JSON, you will see the version that we are using is 9.0 0.2. So let's close this. We have the path for authenticating users. Now, we need to create a JCN webTken before sending a response to the client. So before that, you need to load JCN WebTken. So on the top, require Jason rep, choke and store it in a constant called JWT. Great. Now here, we'll use sine method of JWT. So JW dot sine so it will accept two arguments. First one is the payload and the second one is the secret or private king. So the payload could be a simple string or it could be an object like the one that you see here. So let's make our object with only one property. We will include underscore ID, and we will send this to user dot underscore ID. Now as per the second argument, we will send this to a string. But ideally, you should not store your secret or private key in your source code. Later, I will show you how to store this in an environment where o. But for now, let's just put this to JWT secret. It doesn't need to be JWT secret key. It could be anything, any string. But for now, let's use this. So we have used a sign method, and as a result, we get a token. So let's store it in constant and we'll call it token. And now we'll return or send the response to our client as hook. So there's this back to the terminal. Let's run down on. Now, let's go to postman. Here we will send the correct email and password to this authentication route. Then let's send this. And here's our JCN web token. So let me copy this. And let's go back to the debugger. In this encoded section, you can just delete this one and paste the one that we copy. And here, in the decoded section, you can see our object that we send as a token so here we have the ID property, which is set to the object ID in the Mongaim database. And in addition, we have IAT which is the current time and the time on which this token is created. This is used to H the token. So we have successfully created the token and send it as a response to our clime. In the next video, I'll show you how to include this secret key in an environment variable. 11. Storing Secrets in Environment Variables: Earlier in the course, when I was talking about Express advanced topics, I introduced you to a node package called Config. We use this package to store the configuration settings of our application in JSON files or environment variables. So in this lecture, we are going to take this secret key out and store it in an environment variable because as I told you before, you should never store your secrets in your code base. Otherwise, these secrets are visible to anyone who has access to your source. So back in the terminal, let's install the config module. I can note the version number that's 3.3 0.11. Now first, we need to create a new folder that is config. In this folder, we add a default configuration file that is default dot JSM. So a simple SN object. We could have several settings here. But for now, I just want to add one setting. That is JWT secret key. Now in this default GSN file, we are going to set this to an empty string. The actual value is not here. So here we are just defining a template for all the settings in our application. Now we need to create another file that is custom environment variables dot JS. Note the spelling, make sure to get it right. Otherwise, what I'm going to show you in this lecture is not going to work on your machine. So as I told you before, in this file, we specified the mapping between our application settings and environment variables. So I'm going to go back to our default dot Son, copy all this, paste it here. So this is the structure of our application setting object. Now we want to map this to setting to an environment variable called JWT secret key. But as a best practice, again, as I told you before, it's better to prefix this with our application name so you don't end up one application setting overriding another application setting. So fair wheels align JWT secret key. Now with this, we can go back to our Auth module, so Js. So this is one place we have referenced the secret we're going to replace this with a call to Confic dot get method. So first on the top, we need to import the config object. So Cs config. We set this to require confit. Okay. Now, back in the post method, we call config dot cat and pass the name of the application setting. In this case, JWT secret K. So now, this is not a secret. It's a name of our application setting. The actual value, the actual secret will be in an environment variable. One last change, we need to go to index dot js. When the application starts, you want to make sure that this environment variable is set. Otherwise, we have to terminate the application because our authentication endpoint cannot function properly. Once again, on the top, we load the config module. Config, we set this to require config. Now, one more time, we're going to call config dot get pass the name of the application setting JWT secret key. Now, if this is not defined, we're going to log a fatal error. JWT secret key is not defined. And then we need to exit the process. Earlier, you learn about this process object. This is one of the global objects in node. Here we have a method called exit. We give it a code, zero indicates success. Anything but zero means failure. Quite often, we use exit one if you want to exit the process in case of an error. So currently, I have not set this environment variable. Let's run the application and see what happens. So Norman index dot JS, Okay, look, we got this fatal error. App crashed, waiting for file changes before start. Now you might think the application is still running because Norman is still there. It's not terminated. But if you go to Postman and send an SDDP request to the application, you will see that our application is not responding. So if the application crashes, Norman is still working. Norman is still running. Let me show you what I mean. So if instead of non mod I run application with node see the application crashed, and now we are back in the terminal. So we are not responding to any request received from the clients. Now, let's set the environment variable. As I told you before, on Mac, you use Export, on Windows, you use set for command prompt and dollar ENV column for Power Shell. Dollar ENV column, fair wheels, underline, JWT Secret key. We set this too. Let's say my secure key. Now let's run the application again. Node index or Js. Okay, beautiful. We connect it to Mongadib and if we go back to Postman and send another request to Login, here is our valid cheese and web token that is signed with our secret key, which is stored in an environment variable. 12. Setting Response Headers: So in the current implementation, when the user logs in, we generate a JCN web token and return it in the body of the response. Now let's take this application to the next level. Let's imagine when the user registers, we want to assume they are logged in, so they don't have to log in separately. Of course, this requirement does not apply to every application. Sometimes you want to enforce the user to verify their email address. So after they sign up, you send them an email, they click on a link. So the process is different. But in this course, let's imagine that Fair Wheels is an application that runs locally in a car rental. So people who use this application are people who work in this car rental. We don't need to verify their email address. So the first day they join the store, they need to create an account, and boom, they're logged in. So let's go to our user's module. Okay, here's the post method. This is where we register a new user. Now, if you look at the response we are returning here, we're returning an object with these three properties. Now we can add the JCN web token as another property here, but that's a little bit ugly because it's not a property of a user. A better approach is to return the JCN web token in an SDDP header. So just like we have headers in our request, we also have headers in our response object. So I'm going to go back to our Auth module and borrow this line of code for generating the token. So copy this now back to the user's module. Before we send the response to the client, we generate the token and then we call a response dot header. With this, we can set a header. Now, for any custom headers that we define in our application, we should prefix these headers with X. Now we give it an arbitrary name like authentication or token. This is the first argument, which is the name of the header. The second argument is the value, which is in this case, our token. So with this simple change, we set this header and then send this response to the client. Okay? Now on line 20, we are using JWT as well as the confit module. So we need to import these on the top. So constant TWT we set this to require JCN web tooken. And similarly, constant config, we set this to require confit. Now let's test this. So in the last lecture, I ran the application with node instead of nor mode. So my changes are not visible. So we need to stop the application and run it with node mode. Okay, beautiful. Now, back in Postman here on this tab for registering a user, I'm going to change this email to an email that I have not registered before. Sent. Okay, take a look. So here's the body of the response exactly like before. Now in the Headertab Look, we have this new header, X token. And this is set to our JCN webTken. So in our client app, when we register a user, we can read this header. We can store this JCN WebTgen on the client. And next time we are going to make an API call, we will send this to the server. 13. Encapsulating Logic in Mongoose Models: Now, there is a problem in our current implementation. In the user's module on line 22, this is how we generate a JCN web token. We have the exact same core in Auth module on line 20. Now look at the payload of this JCN web token. In this payload, currently, we only have the ID property. Chances are tomorrow we are going to add another property in this pay maybe the name of the user, maybe their email address, maybe their role. We want to know if there is an admin user or not. With the current implementation, every time we want to change this payload, we have to remember that we have to go to another module and make the exact same change. And in the long run, you're going to forget about these requirements. So in this lecture, I'm going to show you how to encapsulate this logic in a single place. Now, where should we move this logic to? An amateur programmer may think, Okay, I'm going to create a function like generate authentication token, put this function somewhere that we can reuse, maybe in another module that we can import in both Earth and user's modules. And with this, we have the logic in a single place. Well, that is true. That works. But with this approach, you will end up with a lot of functions hanging all over the place. In object oriented programming, we have a principle called information Expert principle. That means an object that has enough information and is an expert in a given area. That object should be responsible for making decisions and performing tasks. As a real world example, think of a chef. A chef has a knowledge of cooking. That's why the act of cooking in a restaurant is done by a chef and not by a waiter. The waiter doesn't have the right knowledge, the right information about cooking at a restaurant. So if chef is an object, we should give the act of cooking to the chef. Now, take this principle and apply it in this code. So here, as part of creating this Chase and web token, what do we need in the payload? We need the ID of the user. Tomorrow, we may need the name of the user or their email. So all this information is encapsulated here in the user object. So it's the user object that should be responsible for generating this authentication token. So the function that I wrote here, generate authentication token, that function should not be hanging somewhere in a module. That should be a method in the user object. So here we have a user object that we load from the database. We need to add a method in this user object like this. User that generate authentication token. And this will give us a token, simple as that. Now, how can we add this? We need to go to our user module where we define the user model and make a simple change there. All right. So here in our user model, we need to extract the definition of this schema and put it in a separate constant because we are going to work with that separate. So I'm going to select all this coe. So when creating the user model as a second argument, we pass user schema, and we have not created this yet. We are going to do that now. So constant user schema, and we set this to this expression like this. Okay. So here's our user schema. Now, you want to add a method in this schema. So user schema that we have a property, methods, this returns an object. We can add additional key value pairs in this object. So we can add a key, generate authentication or token. We set this to a function. So key value pair. When we do this, then our user object will have a method called generate authentication token. Now here, in this function, we can have parameters. So if we have a parameter here, then when calling this method, we can pass arguments. In this case, we don't need any parameters, so I just need to grab this logic for generating the token, get it from here and move it inside this new method. So here in the payload, we need the D of the user. How do we get that? Well, this method will be part of the user object. So in order to reference the object itself, we replace user with this. And this means here you should use the regular function syntax. You cannot replace this with an arrow function. Because as I told you before, arrow functions don't have their on this. This in an arrow function references the calling function. So typically, we use arrow functions for standalone functions. If you want to create a method that is part of an object, you should not use an arrow function. Now, let's revert this. So here we have the token, and finally, we return it as simple as that. Here we are referencing JWT and conflict modules. So we need to import this on the top. So I'm going to go back to our Auth module on the top. We no longer need these required statements. So I'm going to get these from here. And put them on top of the user module. Now, back in the authentication module, so we generate the token and then send it to the client. We need to make the same changes in our user's module. User's module, we delete this line and set the token to user that generate authToken. So finally, let's test this and make sure everything is working. So I'm going to register a new user here. Send, beautiful. And here's our authentication token. 14. Authorization Middleware: So at the beginning of this section, we decided to protect the operations, that modify data and make them available only to authenticated users. So let's go to our companies dot CHASE Piers the post method for creating a new company. This API endpoint should only be called by an authenticated user. So how can we enforce this? Well, here, we did some logic like this. We need to read the request headers. So this request object has a method called header. Here, we specify the name of the header, that is X token. We expect a JCN web token stored in this header. So we store this as constant token. Now, we want to validate this. If this is valid, then we give access to this API endpoint. Otherwise, we will return a response like this. Response for the status code of four oh one, which means the client doesn't have the authentication credentials to access this resource. So this is the big picture. Now we don't want to repeat this logic at the beginning of every route handler that modifies data. So we need to put this logic in a middleware function. Remember, middleware functions, we talked about them in the section called Express advanced topics. So we put this logic in a middleware function, and then we can apply that function in route handlers that need to modify data. Let me show you. So let's get this from here. Alright, here we want to add a new folder called middleware. So we put all our middleware functions here. In this folder, we had a new file t dot has. Now here we want to define a function called OT that takes three parameters, request response, and next, which we used to pass control to the next middleware function in the request processing pipeline. If this concept sounds unfamiliar to you, you need to go back to the section called Express Advanced Topics because there we explore middleware functions in detail. So here in this funtion, we should implement this logic. We get the token. The chances are we don't have a token at all. In this case, we return a 41 response with a message like this. Access denied. No token provided. So this helps the client to figure out why they cannot access this resource. Now, otherwise, there is a token. We need to verify that this is a valid token. So here we need to use our JCN Web token module. So on the top, let's require JCN web token and store it in JW. Now here we call jwt dot Verifi. As the first argument, we pass the token, and as the second argument, we pass the secret key for decoding this token. So we stored that secret key in an environment variable. We need to use a config module to read them. So require config and store it here. So just like before, we call config dot g JWT Ccret key Now, this verify method will verify our JCN webTken. If it's valid, it will decode it and return the payload. So here we get the decoded payload. However, if this token is not valid, it will throw an exception. So we need to wrap this line with a try cache block. So try Cache, we get an exception. If we get here, we want to tell the client that this is an invalid token. We set this status to what 400 because there is a bad request because what the client sends us doesn't have the right data. So send invalid token. Again, with this error message, we can troubleshoot the authentication issues. So if on the client, we cannot access a given API endpoint, we'll look at the error message. We realize we send an invalid token. Then we'll look at the logic on the client where we get the token and send it to the server. So this is our cache block. Now, back to the tri block, here we have the payroll. So we can put that in the request. So our request object, we add the user property to it, and we set that to this decode. So earlier, we put only the ID of the user in the payload. Let me show you. So let's go to our user module. Here's our user's schema. We added this generate authentication token method. We created the JCN webTken and this is our payload. So when we decode this JWT, this is the object we will get. And we will put this in the request as a user object. So in our route handler, we can access request dot user dot underline dot ID and SOW. Okay? So in the tri block, we set request dot user, and then we need to pass control to the next middleware function in the request processing pipeline. In this case, that's our route handler. So we call next. So as I told you before, in middleware functions, we either terminate the request response sl cycle or pass control to the next middleware function. Now, just a tiny issue in this code, in case we don't have a token, we are going to send this response to the client. But I want to make sure that we exit from this function. All right, we are done with our middleware function. Now at the end, we set module dot exports to OTO a shortcut for that is to set it right here. So we set module dot exports, we set it to a function. We don't need a em here. And then we can get rid of line 17. Done. 15. Protecting Routes: So now that we have a middleware function, we can go to index dot JS, Look, here we are applying middleware functions. So we can add that here, and then it will be executed before every route handler. But we don't want to do this because not all API endpoints should be protected. Some of our API endpoints should be public, like registering a user or logging in or getting the list of companies or customers. So in this case, we want to apply this middleware function selectively to certain endpoints. So back to the company's module, here's our post route handler. The first argument is a route. The second is optionally middleware, and the third will be the actual route handler. So let's go on the top, impart this middleware function. So require. Now we need to go one level up and then go to the middleware folder and load the Earth module. We get it and put it here. And finally, here in the post method, we pass OT as a middleware function to be executed before this other middleware function, which is in this case, a route handler. So now let's test this back in Postman. I'm going to open a new tab, send a post request to the company's endpoint. So STDP local host, API Companies. Now, in this case, I'm not worried about the body of the request. I just want to know if we can call this API endpoint or not. So send Okay, look, we got this 401 or unauthorized error. And here's the error message. Access denied, Noto can provide. Now let's provide an invalid token. So we go to the headers tab, set the key that is X token. I put an invalid token, send, we got 400 or bad request with this message. Invalid token. Beautiful. Now finally, let's go back to our register, use a tab. We got a valid authentication token. So let's copy this back to the third tab. Put it here and then send. Okay, now we got a bad request, but this is not because of the authentication. This is because we expected the name property in the body of request. So if I add a JSON object here in the body, name new company. Now, we should be good. Send. So we got a 200 response, and this is the new company. So as an exercise, I want you to apply this middleware function to other route handlers that modify data. 16. Getting the Current User: In a lot of applications, there are times that we need to get information about the currently logged in user. So in this lecture, we're going to add a new API endpoint for getting the current user. So let's go to our users module. Currently, we only have one route handler. That's for creating a new user. Now we need to add another handler for get methods. Now here, add a path or route. We can pass a parameter, but this means the client should send the ID to the server. While this approach is perfectly fine, there are times that perhaps for security reasons, you don't want to have an endpoint like this because then I can send the ID of another user and look at their account where there might be some information that perhaps should not be visible to me. So the approach that we use quite often to get information about the current user is to have an API endpoint like me. With this, the client is not going to send the user ID. We get it from the JCN Web took. So I cannot forge someone else's JCN WebTken because I told you that in order to do so, I need to create a new digital signature for that JCN WebTken. So let's add the route handler, Async request and response goes to this code block. Now, this API endpoint should only be available to authenticated users. So here we need to add our OT middleware. On the top, let's load OT from middleware Auth. Just to clarify, here, author represents authorization, not authentication because authentication is about validating the username password. Here, we want to see if the user has permission to access a resource or not, and that is authorization. We add our middleware here. Now with this middleware, if a client doesn't send valid JCN web token, we will never get to this route handler. However, if you get here, as you saw in the implementation of this middleware function here, we will have request dot user object. So we can access the ID property here. So instead of passing the ID property in the path or in the route, we get it from request dot user dot ID, which actually comes from our JSON web token. So this is more secure approach. Now, we want to find a user by the given ID. So we call user dot fine by ID. Pre pass this value, then await the promise and get the user object here. Now, we don't want to return the user's password to the client. So here, let's call Select and exclude the password property. In your application, maybe you want to exclude other properties as well. Maybe address, maybe phone number. So here we have a user object. Finally, we send this to the client. So send user as simple as that. Now, let's test this. So here in Postman, I'm going to add a new tab, send a Get request to Local host for 3,000 API users me. Now, initially, I don't want to send a JCN webTken. So send Xs denied no token, provided. Beautiful. Now let's go to the headers tab. Add X or token here, and then pass a wedded cheese and web token. SN. This is my user account. We can see password is excluded. 17. Logging Out Users: So in our Auth module, we define this route for authenticating users. What about logging out users? Do we need a separate route for that? No, because we are not storing this token anywhere on the server. So we don't need a separate route handler to delete this token. So technically, you need to implement the logging out feature on the client, not on the server. So on the client application, when the user wants to log out, you simply delete that token from the client. Now, I've seen courses and tutorials that teach you to store this token on the server in the database. This is a very bad practice because these tokens are like keys that give a client access to protected API endpoints. If a hacker can get access to your database, they can see all these tokens for authenticated users. They don't even need to know the password of a user. They can simply get their authentication token and send it to the server to execute request on behalf of a user. You should not store tokens in your database. And if you know what you are doing and you really want to store the token in the database, make sure to encrypt it. Make sure to hash it. Just like the passwords. Storing a token in a plain text in database is like getting the passport or driver's license of all users, put them in a central place, and then anyone, any malicious users who has access to that central place, they can simply get these passports. They can get these driver's license and imitate other users, other clients. So once again, do not store the tokens on the server, store them on the client. And as a security best practice, whenever you are sending the token from the client to the server, make sure to use HTTPS. So a hacker sitting in the middle sniffing traffic, they cannot read the token sent from the client to the server, so the data is encrypted between the client and the server. 18. Role-based Authorization: So far, we have implemented authentication and authorization successfully. Now let's take this application to the next leve. Let's imagine certain operations such as deleting data can only be performed by Admins. So let me show you how to implement a role based authorization. First, we need to go to our user model. So this is our user schema. Currently, we have three properties, name, email and password. Now, we need to add another property to see if a given user is an admin or not. So let's add a new property is admin of type Boolean. So that's the first step. Now I'm going to go in Mongo DB Compass, grab one of these users and make them an admin. So edit. I'm going to add a field after password, and the order doesn't really matter. So is admin and by default, you can see the type of this is string. We are going to change that to Boolean. So let's say this to true and then finally apply the update. So here I have a user that is an admin. Now, when the login, I want to include this property in our JSON Web Token payload. So next time they send this JCN WebTken to the server, we can extract this property directly from the token. We don't have to get the ID, go into the database and see if they are admin or not. And again, as I told you before, with the digital signature included in a JCN webtken, a malicious user cannot change the value of his admin for their user account. If they make any changes, they have to regenerate the digital signature, and this requires knowing the private key that we stored in an environment variable on the server. Now, back in our user module, when we generate the authentication token, we want to add this property in the payload. So here's our payload. Let's add a new property is admin. We set this to. This dt is Admin. So this is the benefit of encapsulating this logic inside the user object. So there is a single place that we need to modify. Previously, we had this in two different places. And with this symbol change, we had to remember to apply this change in two different places in the code. So this is our knee token. Now on the server, we need a new middleware function to check if the current user is an admin or not. So here in the middleware folder, I'm going to add a new file, admin Js. So here we set module that exports to a middleware function. And takes a request, a response, and a reference to the next middleware function in the request processing pipeline. So here we are assuming that this middleware function will be executed after our authorization middleware function. So our authorization middleware function sets request not user, so we can access that in this function. So I request that user that is admin, or let's say if they are not in admin, we're going to return response status. We set the status 24 oh three, which is forbidden. This is one of the areas that a lot of developers get wrong. So we have 41, which means unauthorized, and we have four oh three, which means forbidden. We use unauthorized when the user tries to access a protected resource, but they don't supply a valid JSN webTken. So we give them a chance to retry and send a valid JCN WebTken now if they send a valid GSN web token and they're still not allowed to access the target resource, that's when we use four oh three, which means forbidden, which means don't try again, we just can't access that resource. So back here, we set the status and send a message like access denied. Now, otherwise, if the user is admin, we pass control to the next middleware function, which is, in this case, the route handler. Now we are going to apply this middle ware function on one of our routes. So back to companies dot JS, let's look at the delete method. So, here's our delete method. We want to apply two middleware functions here. So we need to pass array. The first one is OT and the second one is admin. So these middleware functions will be executed in sequence. First art if the client sends a varied Jas in webtken and then we'll get to the second milware function. The user is an admin, then the third middleware function or route handler will be executed. Now, we need to import this on the TA. So constant admin, we set this to require go to the middleware folder and load the admin module. Now, let's test this. Back in Postman. I'm going to open a new tab and send a delete request to this endpoint, STDP local host for 3,000 API companies. Now let me grab an ID for a company from the Mongo IV database. So go to Companies Collection, here, copy this ID and put it in our endpoint like this. Do not forget to include the odds token. Otherwise, you will get the authorization error. Excess denied, no token provide it. So we will put the token and now send this. Beautiful. We have a 200 response, and here's the company that was deleted.