6. Login

In this chapter, we will do the login part. First, we will create a login component same as we did for registration. So, create a new file named “LoginComponent.vue” in your components folder.

Video tutorial:

Then we need to include it in our main.js file and add it in our routes array too:

import LoginComponent from "./components/LoginComponent.vue"
const routes = [
    …

    { path: '/login', component: LoginComponent },
];

The following code should go in your LoginComponent Vue JS file:

<template>
    <div class="container" style="margin-top: 50px;">
        <div class="row">
            <div class="offset-md-3 col-md-6">
                <form method="POST" v-on:submit.prevent="doLogin">
                    <div class="form-group">
                        <label class="text-white">Enter email</label>
                        <input type="email" class="form-control" name="email" />
                    </div>

                    <br />

                    <div class="form-group">
                        <label class="text-white">Enter password</label>
                        <input type="password" class="form-control" name="password" />
                    </div>

                    <br />

                    <input type="submit" v-bind:value="isLoading ? 'Loading...' : 'Login'" v-bind:disabled="isLoading" name="submit" class="btn btn-primary" />
                </form>
            </div>
        </div>
    </div>
</template>

<script>

    import axios from "axios"
    import swal from "sweetalert2"

    export default {
        data() {
            return {
                "isLoading": false
            };
        },

        methods: {
            doLogin: async function () {
                const self = this
                const form = event.target
                const formData = new FormData(form)

                this.isLoading = true;

                const response = await axios.post(
                    this.$apiURL + "/login",
                    formData
                );

                if (response.data.status == "success") {
                    // get access token from server
                    var accessToken = response.data.accessToken;

                    // save in local storage
                    localStorage.setItem(this.$accessTokenKey, accessToken)

                    // to go to home page without refreshing
                    // this.$router.push("/")

                    setTimeout(function () {
                        window.location.href = "/"
                    }, 500);

                    form.reset()
                } else {
                    this.isLoading = false;
                    swal.fire("Error", response.data.message, "error");
                }
            }
        }
    }
</script>

It is almost identical to our register component. It creates a form with 2 input fields, email and password, and a submit button.

On form submits, we call an AJAX to our Node JS API endpoint. The API should return the status as “success” or “error”. In case of error, we are simply displaying an alert dialog.

In case of success, API will also return an access token. An access token is used for upcoming requests. We will attach that access token to other API headers.

So in order to do that, we need to store that token in our local storage. We will remove it from local storage when the user log-out.

Create a global properties variable in your main.js file:

app.config.globalProperties.$accessTokenKey = "accessTokenKey"

And at the end, we are simply redirecting the user to the home page.

At this point, your login page will look like this:

login
login

Creating an API

Now, we need to create an API in our Node JS server that will handle this request.

First, go ahead and install the JsonWebToken (JWT) module by running the following command in the CMD opened in api folder:

> npm install jsonwebtoken

Then open your server.js file and include this module.

// JWT used for authentication
const jwt = require("jsonwebtoken");

// secret JWT key
const jwtSecret = "jwtSecret1234567890";

The secret key can be any string and it should NOT be shared with anyone. If someone else has access to this secret key, he can generate any user’s access token and sign in on behalf of that user.

Then you need to create a POST login route in the same place we created the registration route.

// route for login requests
app.post("/login", async function (request, result) {

    // get values from login form
    const email = request.fields.email;
    const password = request.fields.password;

    // check if email exists
    const user = await db.collection("users").findOne({
        "email": email
    });

    if (user == null) {
        result.json({
            status: "error",
            message: "Email does not exists."
        });
        return;
    }

    // check if password is correct
    bcrypt.compare(password, user.password, async function (error, isVerify) {
        if (isVerify) {

            // generate JWT of user
            const accessToken = jwt.sign({
                "userId": user._id.toString()
            }, jwtSecret);

            // update JWT of user in database
            await db.collection("users").findOneAndUpdate({
                "email": email
            }, {
                $set: {
                    "accessToken": accessToken
                }
            });

            result.json({
                status: "success",
                message: "Login successfully.",
                accessToken: accessToken
            });

            return;
        }

        result.json({
            status: "error",
            message: "Password is not correct."
        });
    });
});

We are first checking if the email exists or not. Then we are verifying the plain text password from the input field with the hashed password stored in Mongo DB.

Then we are generating the user’s access token based on his ObjectId stored in Mongo DB and our JWT secret key.

After that, we are saving that generated access token in that user’s document and we are also returning it to the client-side. The client can then store it in its local storage and send it with other API calls in the headers.

Run the app now, first try by entering the wrong email address, you will see a pop-alert that the email address is not found. Then try with the correct email, but the wrong password. This time you will see a different error message saying that the password is wrong.

Finally, try with both correct values and you will be redirected to the home page. Right now, you will not see your name on the top navbar, instead, you are still seeing the login and registration link (which you shouldn’t be). You should be seeing a logout button.

But you open your browser console and type localStorage, you will see something like this:

local storage
local storage

It means that the access token has been stored in our local storage. And if you refresh your Mongo DB compass, you will see the same string in that user’s document too.

access token mongo db
access token mongo DB

Conclusion

In this chapter, we have learned how to authenticate a user using Node JS and Mongo DB with Vue JS as the frontend. We also learned how to generate access tokens using JWT and save them in local storage as well as in Mongo DB.

In the next chapter, we will learn how to get the user’s data based on this access token.

How to pass this value as headers in API?