Web Authentication APIs Hashing & Login Endpoint
Transcript from the "Hashing & Login Endpoint" Lesson
>> Again, this might be simple for a lot of users, but yeah, maybe for some user, may be new, the whole idea. So what's wrong right now? Well, I'm writing the password in plain text, okay, which is not a good idea. Makes sense? So if I write a user, then the password will be actually available in my JSON file.
[00:00:26] I can see that, I'm the backend owner, I can see all the passwords. So it doesn't look nice. Okay, can we hash it? Remember the hash operation means it's typically one-way method to encrypt the data, that if you have the hash, you cannot go back to the password.
[00:00:48] But as we mentioned before, that's not 100% true. Mostly, when we are hashing passwords created by the user, that may be similar to other users' passwords. And then there are databases of hashes out there that can actually get, not 100% of the passwords but a lot of them, okay?
[00:01:13] So to hash the password, so let's change that. So instead of password, we're going to write the hash password, Okay, that we need to create somehow first. So for that, we are going to use another library that is already in the dependency, and it's already imported here, it's ready to use, okay?
[00:01:34] It's called bcrypt. It's an npm, it's open source, it's all the dependencies we are going to use. So to import this, we actually need, first, something known as a salt. I am not going to be so deep into the security part on this. So we're going to generate the salt synchronously with 10 rounds.
[00:02:01] And you say, okay, what's that? This has to do with random numbers and seeds. I'm not sure if you have ever been in that area, okay, of computer programming, or how random works. It's kind of how the random is been generated. Don't worry, we just create the salt.
[00:02:19] And with the salt, we can actually create that hash password. And it's basically talking to bcrypt. We're going to say hashSync because this one is asynchronous. Why I'm not using the asynchronous API, I could, but you simplify a little bit the code, I'm using the sync API. So we don't need to deal with responses in the async world.
[00:02:43] HTTP response with the async world are not so simple, okay? So many way, just to make the code cleaner to understand. So we're going to take the password that is coming from the body and the salt that we have just created, and that's going to give us a string.
[00:03:01] It's a text, so the result is a text, not an array buffer or something like that. It's a text, and that's what we're going to save in the database. So if someone is hacking my server and getting the database, so he or she will get not the password as plain text, but as a hash.
[00:03:24] It's better. It's not 100% secure, but better than not having a hash. That means that now, you cannot see the password, you cannot send the password by email. If you have a recover password, now it's more a reset password, Okay? Make sense? And that's for now, despite the TODO for Data Validation that we're not doing, the rest, I think, it's fine.
[00:04:01] And just because we are here, we can also create the endpoint for login. So it's also a post, and the route will be auth/login. Request response is always the same thing. I'm old school, so you will see I'm using the semicolons [LAUGH] all the time, is in my brain.
[00:04:25] So unless I'm writing Swift or Kotlin, where it's not mandatory, yeah, in shell script, I tend to use semicolon. So if you don't like them, that's okay, you can remove them. At least on Node.js, on client side, it might be a problem when you're using, it depends on the library that you're using to compress your code.
[00:04:49] Okay, anyway, just a quick tip. Well, for the login, so what we need to first check is if the user exists, okay? So let's see if we have a userFound. And we can call the same findUser library that we did before, taking from the body the email, okay?
[00:05:13] And if the user is found, we should check credentials, so check the password, okay? So User Found, we check the password. But before, I wanna see, or I wanna discuss with you what to do if the user is not found. What do you think? What should we do?
[00:05:35] Yeah, the user does not exist with that email address, okay? So of course, we are going to send a message saying, we're going to respond. It can be with a sendStatus with an error, okay, with a code number, or it can be just as we did with register, ok, false.
[00:05:55] But what about the error message? Should we say the account doesn't exist? Check your email or something, Check your email address. That was actually pretty common for a while, okay? But today, it's a bad idea. Why? I mean, if you have that, it's okay. I mean, it's okay, it's not the worst of the problems.
[00:06:21] But the problem is that a hacker that is trying to attack your database can now try a list of email addresses to see which one has an account with you first. Okay, so it can filter his list of email addresses, because now you're telling him who has an account with you, okay?
[00:06:45] So it's better to just say that credentials are wrong or some generic message that you will return, also is the password is wrong. So then whoever is calling your API will not know if, credentials are wrong, but they don't know if it's because the email is wrong or because the password is wrong.
[00:07:07] So the email is okay, he has an account, that email has an account, but the password is wrong. So credentials are wrong or whatever. And that's the generic message, and that's a better idea, okay? Again, if you have it, it's okay. But if you don't provide that information, okay, you are removing a percentage of, we are reducing the risk a little bit.
[00:07:38] And reducing risk is always mother of making a lot of things. And you'll reduce here a little bit another here, another here, another here. Okay, we are trying to reduce the risk of our data to be breached. Okay, and if the user is found, well, what do you need to do?
[00:07:57] We just check the password. Should we compare? So we have the userFound.password. Should we compare like this the password that is coming from the form, from the login form? What do you think about this? We have a problem, is that our store password is hashed.
>> So I don't have the original password to just compare as a strings.
[00:08:25] Makes sense? So this is the password that the user has typed. So it's the password, but this one is hash because we saved here at the bottom a hashed password. So comparison will always be false at this point. So to solve the problem, what we're going to do is we're going to call another function for bcrypt, for our library, that's called compare.
[00:08:50] So we are going to compare synchronously. We pass first. First, we need to pass the password without the hash, and then the hashed password. So we need to reverse the order of what I wrote. So it's userFound.password. First, the one that is not hashed, and as a second argument, the one that is hashed.
[00:09:12] And if they are okay, it will give me a true. So now I will respond with ok: true. So the login was successful. And if not, again, I will use the same error message as when the user was not found. So then an attacker will never know what happened, okay?
[00:09:34] And a login API typically also need to respond with the current user, okay? With the data, like the name, the email, because typically, the user interface, the client typically needs to render your name. Hello, Max. Okay, so typically, we pass here some data. I can pass the name from the userFound and the email.
[00:10:03] Public data only, don't send the hash password, or don't send the whole object. I've seen that a lot, big, big security problem that a developer is taking the object from the database and sending the object to the client. And now, sometimes it will contain private data, your private ID.
[00:10:24] And maybe in the future, you add more APIs or more things to that, you attach more things to that object. And now, the client will get that information. So no, don't do that, okay? So that's our code for login and registering. The last part that we need is to connect the client.
[00:10:48] So we go to the public > scripts > API.js, yes.
>> Why are you sending the password value for the email property in the response?
>> Let me see.
>> Line 43.
>> The email, this one? Yeah, why? Because it's wrong. So thank you for that, is not the password.
[00:11:11] That happens typically when you're teaching, because you're saying something about something, and your brain is typing at the same time, and sometimes, yeah, you mess up. So thank you for that. Is the email, not the password. So now, in the API, what I need to do is here add the API client side part.
[00:11:31] So for example, I will have a login function. If you don't like to use this syntax, use wherever syntax you want. I'm using a syntax, I'm using a code that I know will work on every browser without transpiling this code, without passing this through Babel, okay? So I don't have any build process in my client-side code, so I'm just writing code that I know will work on every browser these days.
[00:11:54] So I'm creating an async function. And the idea will be to call the makePostRequest, okay? And because this is async as well, I'm going to await. So I'm going to return awaiting for API makePostRequest. The endpoint is just a prefix that you might change later in case you move this to a different server, or something like that.
[00:12:22] So I will take the endpoint, and I will concatenate the login name. And that's the data, okay? I'm going to send the user that I receive as an argument. So I receive a user as an argument, that's an object that I will populate with the form. And I'm sending that through post, okay?
[00:12:46] And something similar is actually register. So I receive also a user object. And it's going to be pretty much the same thing, but changing the endpoint. Making a post request to endpoint plus register, and then I pass this user as an argument. For now, we have only these two endpoints.
[00:13:13] I know that for some, well, this way of coding seems a little scary because it seems like blind coding, right? Because we haven't tested anything, and it's like, ooh, we are writing out of code and we haven't seen it, and it is working. Well, we'll see later.