DEV Community

loading...

Authentication and JWT in Node.js

eidorianavi profile image EidorianAvi ・4 min read

Alright so this week I'm going to continue working with node. This one should be pretty short and sweet but I'd like to cover how to build out a login request and how to produce a token for verification in the front end. Let's get started.

What's required

So for our form of authentication I'm going to be using Bcrypt and a JWT. What are those you ask? I'm glad you asked.

Bcrypt: A function that uses an algorithm to hash passwords. This is important for user security because if someone were to gain access to your database and the passwords are not hashed the users credentials are compromised.

JWT: JWT stands for JSON Web Token. It is a standard for authentication in applications. Upon a successful login the server sends a JWT to the client as proof of verification. Think of this as the ticket for a user to gain access to gated content or personal content.

Now that we know what the pieces of the puzzle we will be using are lets go ahead and install them:

npm install bcrypt jsonwebtoken
Enter fullscreen mode Exit fullscreen mode

Once they're installed go ahead and require them in whatever file you will be applying authentication to. I will be doing it in my users route.

const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');
Enter fullscreen mode Exit fullscreen mode

We're good to go.

Build it out

Creating a Hashed Password

So the first thing I'd like to handle is making sure when a user signs up we don't store their password in our database as is, that's just not cool. We have to hash it first. That's where bcrypt comes in. It will not only hash a password for us but it will also help verify hashed passwords.

Here is what my creating a user function will look like:

router.post('/add-user', async (req, res) => {
    try {
        const hashedPassword = await bcrypt.hash(req.body.password, 10);

        const user = new User({
            username: req.body.username,
            password: hashedPassword,
        });
        const savedUser = await user.save();
        res.json(savedUser);
    } catch(e) {
        res.json({ message: "Error"});
    }
});
Enter fullscreen mode Exit fullscreen mode

So let's break that down.

  • We created an async post request to our users route for adding a new user.
  • Since it is an async function we handle it within a try/catch block.
  • In the try block we create a hashedPassword constant and let bcrypt create a hashed password. It takes in the password from the request as well as the amount of saltRounds, we set that to 10 which I believe is the default. This is asynchronous so use an await.

Sidenote: Salt is used in cryptography. It is random data to mix in with the core data to ensure improbability of replication.

  • Once we have used bcrypt to create a hashed password we continue like a general post request. Create a user instance with the username and the newly created hashed password instead of the request password.
  • Save this new user instance with the hashed password.
  • In the catch block I have it set so if there is an error it will send a response with the error in JSON format.

Awesome. Now if you make a post and create a new user and go check out the database you will see in the password parameter it is a random string. Try and decode a password from that. You can't.

Logging a User In

Alright so now that we know how create users with hashed passwords in our database let's check out how to login a user.

For this portion we need Bcrypt to handle the hashed password and JWT to provide proof of successful verification. Again I do this in my users route.

First thing let's create a token secret in our .env file for later. This should be a random string that's totally unpredictable you can use the web to generate one. Store it in something like:

TOKEN_SECRET=b91028378997c0b3581821456edefd0ec7958f953f8c1a6dd856e2de27f0d7e0fb1a01cda20d1a6890267e629f0ff5dc7ee46bce382aba62d13989614417606a
Enter fullscreen mode Exit fullscreen mode

Now let's check out the function:

router.post('/login', async (req, res) => {
    const user = await User.findOne({ username: req.body.username });

    try{
        const match = await bcrypt.compare(req.body.password, user.password);
        const accessToken = jwt.sign(JSON.stringify(user), process.env.TOKEN_SECRET)
        if(match){
            res.json({ accessToken: accessToken });
        } else {
            res.json({ message: "Invalid Credentials" });
        }
    } catch(e) {
        console.log(e)
    }
});
Enter fullscreen mode Exit fullscreen mode

What's going on here:

  • It is again an async post request to our users route.
  • First thing we can do is find a user based on their username which ideally will be unique. This is done through using findOne on our User model via mongoose as we have in a previous blog post.
  • We create our try/catch block since again this is an async function.
  • First in our try black we will asynchronously compare the password we received in the request to the hashed one stored in the database using bcryt.compare and passing in first the request password and then the hashed password associated with the user we stored in a constant earlier. Bcrypt will compare and handle the hashing and provide a true or false value.
  • We will also be creating a token using JWT. We use jwt.sign() and pass in first the user data and that token secret we hid in our .env file.
  • Set up an if block and if the match is true it will return that token in a JSON formatted response.
  • If it is not a match it will respond with a message saying that the credentials are invalid.

You should at this point be able to test out a login POST request with a previously created user. If the password and username are correct the response should provide a JWT token as proof of verification. If not you should hit the error message.

Wrap Up

On the back-end you should now have an idea how to safely store users credentials as well as how to verify them and providing proof of verification via a JWT. Now locking content behind authentication and providing authorization is a front-end matter and something we won't be getting into today.

I hope you learned something today and if you have any questions/comments please feel free to reach out.
As always happy coding!

Discussion (0)

pic
Editor guide