API Design in Node.js (using Express & Mongo) Exercise 11 Solution
This course has been updated! We now recommend you take the API Design in Node.js, v4 course.
Transcript from the "Exercise 11 Solution" Lesson
>> Scott Moss: Anyone want to explain to me what this function is doing? Anyone want to take a guess and just tell me? I already told you, so I'm not gonna tell you again. I just wanna know, if nobody has any questions then that means everybody knows what this function is doing.
>> Speaker 2: [INAUDIBLE] Request.
>> Scott Moss: Yeah so it will look for the token on rec.headers.authorization, and then because we set it up, up here op top, we already set it up it already knows the secret because, we told it up here. It told him the secret enclosure. And it's going to match It's going try to JWT.verify this token with this secret and if it finds it, it gonna grab whatever object is returned and attach it to req.user and then call it next.
[00:00:52] If the verification process fails, it'll call it next with an error. If it doesn't find the token, it'll call it next with an error.
>> Scott Moss: Cool, so let's just start with the one that probably we're doing first, and that's verifyUser. So, anybody tell me what verifyUser is doing?
[00:01:22] What were we supposed to do? What was verifyUser supposed to do? I hope everybody knew what it was supposed to do, because you just tried to make it, so if you didn't know what it was supposed to do it'd be really hard. Yes?
>> Speaker 3: It's supposed to say, is this user in the database?
>> Scott Moss: Right and what else?
>> Speaker 3: And were the credentials correct?
>> Scott Moss: Right. So, it'll check to see if this user exists in the database, and if the password they supplied to me it is the same password that they signed up with, that's what this is doing and it attached the result of that to the inquiry.
>> Speaker 4: This kind of came up when you talked about decode token, where is it being used.
>> Scott Moss: We're not currently using that, we will be on step 12 That was the question I was hoping somebody would ask me like, where were you using this thing? Because we aren't using it anywhere.
[00:02:08] We just made it. But yeah it's not being used. So, verify user has absolutely nothing to do with tokens. This is just passwords right here. It has nothing to do with JWT. So this is my solution for it. So what I do, first I make sure that there is a username and a password and if not, I just send back a 400.
[00:02:26] You need a username and password. Also you should be doing this validation on a friend, as well. So you shouldn't even let the form even submit without the username or password, but this is just to catch it, anyway. So they got two types of validation. Saving those DV queries.
[00:02:43] And once the status quo for 400 is like resource, let me see, I have all the cheats right here, let's see. Bad request, so I think that's appropriate.
>> Scott Moss: I don't know why I couldn't remember that.
>> Scott Moss: And then if that's true, and then I put this return key right here, right?
[00:03:05] So it doesn't continue to go down here. You could also just put a else if you want to. I just don't like that sometimes, so I'll just put a return, so stop. You could also just put the return in front of this, and it will still work. Whatever you want to do.
>> Scott Moss: And then I say User.findOne with this username. because we all know usernames are unique, we did that, right? We only want to query things that are unique, so I'm gonna find the first user, or the only user who's user name is username. And then, grab that user so if it didn't error out, but there is no user because remember .then is a promise resolution.
[00:03:41] So, this will resolve as long as there was no error. But that doesn't mean that there is also a user. The database could have went to. This mongoose could have went to the database, search for a user, didn't have an error, but came back with no user. That's not going to throw an error if there is no user.
[00:03:57] So, we need to check to see if there is actually a user there. So if it's not an error, sorry, if it's not a user, then I'm going to set a 401, no user with that given user name. And, you can see I did an L statement. Returning a promise is just kind of weird as we saw.
[00:04:16] So else, and then what I do is I say user.authenticate password, which I told you about if you go look in the user model authenticate is this. It takes in a plainTextPword, and it will return this method bcrypt.compareSync, which will take the plain text password and hash it and see if it matches the password that's already saved.
[00:04:40] Then it'll return it true or false. That was already there for you. And remember, I said this is on the methods. So, if you say user scheme.methods, that's on the instance. We went over this, so that's how I was able to say user.authenticate. So, I was saying, hey user, authenticate yourself with this password.
[00:05:01] Because it's on the methods, which means it's on the instance. Does everybody follow me there? Because these two methods authenticate an encrypt password exists on userkeepers.methods every instance of a user model can call these methods. And when I say the key work this I'm referring to the actual instance.
[00:05:21] So in this case, when we say user.authenticate, pass in is password text, and I would come in here and password log this, this key word right here, it would be this user.
>> Scott Moss: So I'm saying hey, authenticate yourself. If you're not authenticated, if the passwords don't match, 401, wrong password.
>> Scott Moss: or else, it did match. Cool, you're good to go. There was a user, the passwords did match. I guess you are who you say you are. Req.user = user;next();.
>> Scott Moss: And then error handling. So remember I said with promises, you can either call a .catch or you can also pass in a second callback to do optional error handling.
[00:06:12] That's what that is right here. Because this version of Mongo is using m promises, and I don't thing they have a .catch or a .fail. They probably do, I probably just don't know it enough, but I know if you do this it'll catch the error.
>> Scott Moss: So what do we have on this?
[00:06:30] I need questions on this one. This one's kinda tough. I saw a lot of people like. I'm curious, what do other people do? What else did you guys do?
>> Scott Moss: Everybody did it like this? No? Yeah.
>> Speaker 5: I have a question. Is there a reason why you don't use error middleware and set the status sending back a message?
>> Scott Moss: Is there any reason I don't use error!
>> Scott Moss: So, the reason I did it here, that's a good question, so his question was could I just use error middleware like we did before to just send a message back. Yeah, we could totally do that. But I guess the reason I did it here was, one, I haven't registered this middleware verify user anywhere, so I have no idea in what middleware stack it's gonna be.
[00:07:22] So depending on what router it's placed in, or what route it's placed in I have no idea what error handling is going to come after it. The only guarantee I do have is like, if I set up on globally, and like serve it out JS. Like this one.
[00:07:32] This is the only one I'm guaranteed to know that it will hit, cuz this is after everything. So, and I also wanted to do like a specific error for these conditions. So, if I wanted to send it to global air message, I would have to send along some type of metadata along to let the airware know that this airware is coming from a verified user.
[00:07:53] Maybe I'll put a different message on it, or I'll put a different type on it, so I can check and be like if airware.type equals from verified user, send back the status code of this. So you can totally do that and that might be better, but it was just like, I would have to figure out how to check for those different types of errors.
>> Scott Moss: Or the appropriate error messages.
>> Scott Moss: An even better approach is just to abstracts or, what I would do, I would make a middleware that just mocks out error handling forever, or just have something like req.. You know, or I could like req and it'll just send back a 404 and give it a message, and it will send that message.
[00:08:34] I would do something like that.
>> Scott Moss: So that looked kind of funny to you, is because I'm using brackets in text, because it's a number. I can't say req.404, that won't work, so I have to say req.
>> Scott Moss: All right, any other questions on this, on verified users?
>> Scott Moss: You look like you got some questions. You got some questions. How do you
>> Speaker 6: Okay, I have a question.
>> Scott Moss: Thank you. Yes, there we go.
>> Speaker 6: Line 75. It's calling next after it sets the user on the request.
>> Scott Moss: Yes.
>> Speaker 6: Then server.js.
>> Scott Moss: Yes.
>> Speaker 6: I see auth as the last app.use.
>> Scott Moss: That's true.
>> Speaker 6: So how does it
>> Scott Moss: What's next?
>> Speaker 6: [LAUGH]
>> Scott Moss: Yeah, that's a good question. Let's follow it. So auth. You start right here. And then let's go to where it's being pulled from, which is auth, routes.js. So it's being pulled from here. This is the router right here, right?
[00:09:44] Are you following me there? Okay, here's verify user. That's the method that was calling next. So, when it calls next, it goes to the next function, which is this one. So, that's where it's going when it calls next. It's going here. Because remember, we can put middleware in between these stuff too.
[00:10:03] Remember, we can put an array of middleware here. Right? We can put. We can put another middleware here. You could put unlimited middleware here. It doesn't matter and every time you call next this is gonna go to the one to the right of it. So this is route specific middleware.
>> Scott Moss: So, it does sounds like middleware is very powerful. But yeah, so only called next gonna go here. Good question.
>> Scott Moss: Any other questions on this guy?
>> Scott Moss: No, okay and then back to where I just showed you. So of course, and this is where you would use verifyUser.
[00:10:46] You'd verify the user before you'd sign them in, because when we get to controller.signin that's where we're actually gonna sign the token. We don't want to sign the token for somebody who hasn't even given us the appropriate username and password combination. So, we want to verify that they are who they say they are first, before we give them a token.
[00:11:05] Does that make sense why we have verifyUser here? Let me check the passwords, first let me see if they have a username and password. Right, make sure they have a user name and a password. Then also make sure that their is a user with that user name. And then check that the two passwords the hash passwords match each other, and only if all those conditions meet all they sign are who they say they are.
[00:11:33] Then I'll type to direct that user and you can go for it. So, those are only conditions that this will work. So that's how we able to say, all right, somebody goes to /auth/signin. You must verify them first, and then we can sign them. So, now if we go to the controller method, this one, we have the exports.signin.
[00:11:55] It's the controller method for the sign in, and because if you go to verify user we notice we put rec.user here. We attached rec.user. We now have access to. So whatever comes next after this, which is the controller.sign in as we saw can now call it rec.user, which is what this is saying here.
[00:12:15] So, reg.user will be here from the middleware. Verify user. Then we can just create a token and send it back for the client to consume. So we can use the sign token method that exists on the auth object. So if we just require auth, that was already made for you, auth.js is the signed token right here.
[00:12:33] As it takes an ID and it returns a token, that's it. Give it a Mongo ID, and it will sign it and give you the token back. That was already there for you. So that's what I did, I just required it, I just did .signToken here.
>> Scott Moss: And then I'm saying, okay token equals signToken pass and ID which is req.user_id cuz we got the req.user, and then just send back the token.
[00:12:59] So I send back an object with a token property with a token value.