3.4 Implementing the Server's Login Endpoints
Our client needs some URL endpoints to make requests to, so in this lesson we'll write our
/login endpoint to authenticate users.
1.Introduction2 lessons, 12:18
2.Getting Started2 lessons, 20:32
3.Users and Authentication5 lessons, 54:30
4.Managing Currencies5 lessons, 46:15
5.Managing Our Portfolio5 lessons, 50:13
6.Security1 lesson, 10:49
7.Conclusion1 lesson, 00:38
3.4 Implementing the Server's Login Endpoints
In the previous lesson, we wrote the majority of the client code necessary for logging in. And in this lesson, we're going to focus on the server code so that we can validate the user credentials and then supply the client with what it needs to authenticate users. So we're going to start with our sever folder, were going to go to routes. And we are going to create two files. The first is going to called api.js. Now the reason why we're going to use two files is because here, inside of api, we are going to uses kind of like a joining file. So that we will have a separate file for the login in route, we will have a separate file for the currency route, we'll have a separate file for the portfolio route. And this API file is just going to join, all of those together. So what we will end up with is something like this. We will pull in login which is something that we will create here in a moment. And we will simply add this to our router. And the reason why we're doing this is so that our main file will be as clean as possible. So here we're going to use our login, and then we will simply export our router. So now let's create that login.js. And this file is going to contain more than just logging in. We will also have the registration route in here as well, but we will get to that in another lesson. One thing we do need here is JWT, because we are going to be creating the token that is going to be sent to the client that will then be use for authentication with the rest of our API. So if we are going to create our token, we do need the config as well because there is that secret value that we need to refer to. So we will pull that in. And then we will simply just start our route. So the URL is going to be log in. Now we're not going to say API /login, because that's going to be done inside of main. In fact, let's just go ahead and do that. So let's open up main. What we're going to do then is say apiRoutes and we will pull that in from routes and then API. And then after we set up our basic routes, we will say app.use, then we will specify the URL of /api and then apiRoutes. The reason being is first of all, it keeps this file as clean as possible. Second, if we ever decide to change the base URL for our API, if we decide to put versions in here. Then that makes it just one place that we have to do that. As opposed to having to go to our login file and then the file for a portfolio and currencies and all that. So this makes it nice and easy. All right, so our login, we are going to first of all check to see if the user exists with the supplied email. So let's grab that from the body. Because we're going to use this email value in several places. And we will use the findone method. We will search on our email. And if we have an error, some security experts say that you shouldn't return a 500. If you do encounter a server error, return a 404 or something, because an attacker could see that as a potential exploit. And they might focus on that particular resource that's providing a 500. Now that's great from a security stand point, but from a development stand point is horrible, unless if you have lagging going on, so that you can be alerted that hey and error occurred. But in this case we're just going to return a 500. In a real application, we would have some very thorough lagging, so that we could hopefully easily find errors and fix them. So if we don't have an error, but if we don't have a user as well, then let's return a status of 401, and let's just send some information. We could have an object that has an off properties, set it to false. We could also supply [LAUGH] a null token. Hey, here's a token but yeah, it's null. But as I mentioned in the previous lesson we don't want to be specific. We don't want to say, that this user is not found. We just want to say that, the information you supplied doesn't match anything in our records. And that's going to be fine. So if we are to this point, then we have a user. So we need to compare the passwords. So we're going to use bcrypt for that. It has a compare sync method that we will use to compare the password from the body with the password from the database. And so if that is not valid, then we will do what we did when there wasn't a user. Where we will jus return an object that says auth: false and a null token. But if we are pass this point, then we have a valid user. So we want to create our token and we will use jwt to do that. And we are going to create a token based upon the user ID. That way whenever we receive requests with a token we can find out what the user is for that request. So that is why we are using the ID. We are going to use our secret value to help sign that token. We can also set and expires value. So let's do that. ExpiresIn: 86400 is 24 hours. And you could argue that that's too long, but I would make the argument that we are targeting people that are interested in cryptocurrencies. They know that this information is publicly available. The only thing our particular application does is group them together so that you could say if somebody did happen to get on a computer where somebody didn't log off and the token was still available. The only thing that they would see is that whoever was using that computer has these accounts, and that's it. So not a big deal, in my opinion. So if we are to this point, then we are going to say that authentication was true. Here's your token, and here's your user information. Now notice what I'm doing here. I'm not passing along the user object from the database because that contains too much information that the client needs and I'm thinking of the password. There's no reason for the client to know what the password is. Because once something leaves our server, it is out of our control. So in this case, we're going to say that, here's your email address and whether or not you are an admin. Now the ladder, this admin flag is going to be used strictly for the client. As far as the server is concerned, our source of truth is the database. So never rely upon the information coming from the client because we don't control that. So we do need to export this, so we will export our router. And then we can hop on over to our client. I think we're done as far as the server is concerned for logging in. Let's make sure all of these things are saved. And in the client, let's go to the app component because we need to do something when we actually log in. And what we want to do then Is store our stuff in local storage. So we will set the token with data.token. And we will do the same thing for our user. But our user needs to be serialized. So we will say jason.stringify. I hate that name, why can't they just say serialized but well, that's a dead horse. Then I'm going to continue to be that every time I have to type stringify, it's just dumb. So we have this authenticated. We'll go ahead and set these flags as well and admin is going to be based upon whether or not. The user isadmin was true or false. And let's also send the user to if they log in they don't want us to be seating looking at the log in forms. So eventually we want to pass them on to the portfolio, but for now let's do not home, let's do about because we're going to write the log out method as well. Now, as far as the servers concerned there really is no log out because you know we aren't managing sessions or anything like that. The only way that we know that a request is valid is because of that token. So what we will do then is on the client is we will remove the token and the user information. That way, we remove it from the client, and it cannot be sent to the server. So we will remove those. We will also set the isAuthenticated flag to false. The isAdmin to false. And then we will redirect the user to the home page. So with that done, let's hop on over to the browser and let's go to our login page. And hopefully we will see our login form. And we aren't seeing anything. Well, yeah, nothing. So, there's an issue, let's look at the console in the browser, let's see if we have any errors and we do, cannot assign to read only property exports that is inside of api.js. So let's open that up that file, and let's take a look. And yeah, that's not the right syntax. We want export default. [LAUGH] I wish I could say that that doesn't happen to me very often, but whenever I'm working with yes, six modules and then your typical node modules in the same project. Yeah, that happens more than I want to admit. So with that fixed, let's go back. We do have our form, which that's great. So let's go ahead and let's login. So that hopefully this will work. And there was an issue logging in, okay. Let's look at the browser console once again and we can see that the connection was refused. So let's look at our server console routers is not defined. This is inside of the other API file. Great, so let´s open that up and module.exports router should be single. Okay, so we shouldn't have to refresh the page cuz that was server side. So let's log in. And did we set up any code to hide this? Let's look at that hopefully we aren't getting another error so let's look and see no. We don't. So let's add the code that is going to reset the error. Because in this particular case, we definitely don't want the error to remain there because no if there is not an error, we shouldn't be displaying an error. So let's attempt to log in. Okay, we're not getting an error, so what then is the problem? If we look at the error console, the connection was not well. That might have been old. Let's look here. Okay, so we don't have any errors. One thing we can look at is the application tab. And if we look at the local storage, we do not have our token in our user. So that basically means our login method is not executing, which is supposed to execute whenever the login event occurs And that's why. It's not a create hook. It is a created hook. So now, hopefully that is the last problem that we will encounter. There we go. We logged in. We were sent to the about page, which is what we would expect to see. And we can look up in the top right hand corner and our links changed. Now remember that I have gone ahead and just pasted all of that in. So we have some links for users that are authenticated, we have other links for users that are admins like managing the currencies. And then if somebody is logged in then there is the log out Link which whenever you click is going to call that log out method. So whenever we click on log out, that should properly log us out. It does and it redirects us back to our home. So we have our login up and running. I think the next logical thing to do is implement our registration, so that we will be able to create just a normal user and use those whenever we need to.