Open In App

Multi Factor authentication using MEAN

Last Updated : 18 Apr, 2024
Improve
Improve
Like Article
Like
Save
Share
Report

Multi-factor authentication is important and common in every website or app to securely login the user. In this article, we will see how we can implement Multi-factor authentication using MEAN Stack. MEAN Stack includes the use of Angular for frontend, Node JS and Express for backend, and MongoDB as the database.

Project Preview:

Mean Multidactor authentication preview

PROJECT PREVIEW IMAGE

Prerequisites:

Approach to Create Address Book using MEAN Stack

Backend:

  • Set up a new node js project
  • Set up the server using express with CORS as the middleware in file server.js
  • Create the app instance using const app = express()
  • Twilio a cloud communication platform used to send or receive text message which can help developers to implement the multi-factor authentication functionality.
  • Set up a twilio account and get the configuration details
  • Create controllers folder which will define the API methods
  • Create models folder to create the database schema for user
  • Create router folder and mention all the routes related to user login, register and verify for checking the code entered by user
  • Set up local Mongo DB database.
  • Set up the connection to local Mongo DB in server.js file
  • Create collections within the database to store and retrieve the data from database
  • One collection is created – user
  • Create .env files for storing the environment variables for config details related to twilio account, database and port
  • Implement the core logic of register, login and generation and verification of the code
  • Test the API endpoints using postman

Frontend:

  • Create a new angular project
  • Create components folder and seperate components for seperate routes – user component for login and register functionality and verify component for verification code feature
  • Create HTML, CSS and ts files for all the components
  • Create service to establish communication between frontend and backend routes
  • Create various routes in app.routes.ts folder for login, register and verify
  • Test the frontend application in browser at https://localhost:3000

Steps to Create the Application

Step 1: Create the main folder for complete project

mkdir multi-factor-auth
cd multi-factor-auth

Step 2: Initialize the node.js project

npm init -y

Step 3: Install the required dependencies

npm install express mongoose jsonwebtoken bcryptjs nodemon twilio cors

Project Structure (Backend):

Screenshot-2024-04-16-003525

PROJECT STRUCTURE FOR BACKEND

The updated dependencies in package.json file of backend will look like:

"dependencies": {
"bcryptjs": "^2.4.3",
"body-parser": "^1.20.2",
"cors": "^2.8.5",
"dotenv": "^16.4.5",
"express": "^4.19.2",
"jsonwebtoken": "^9.0.2",
"mongoose": "^8.3.1",
"twilio": "^5.0.3"
}

Example: Create the required files as seen on the project structure and add the following codes.

Node
// authController.js

const User = require("../model/authModel");
const bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken");
const twilio = require("twilio");
require("dotenv").config({ path: __dirname + "/config/.env" });
const twilioClient = twilio(
    process.env.TWILIO_ACCOUNT_SID,
    process.env.TWILIO_AUTH_TOKEN
);
exports.register = async (req, res) => {
    try {
        const { username, email, password, phone } = req.body;
        let user = await User.findOne({ email });
        if (user) {
            return res.status(400).json({
                message: "User Already Exist",
                success: false,
            });
        }
        user = new User({
            username: username,
            email: email,
            password: password,
            phone: phone,
        });
        const salt = await bcrypt.genSalt(10);
        user.password = await bcrypt.hash(password, salt);
        await user.save();
        const token = generateJwtToken(user._id);
        res.status(201).json({
            success: true,
            token: token,
            message: "User registered successfully",
        });
    } catch (error) {
        res.status(500).json({
            message: "Server error! New user registration failed",
            success: false,
        });
    }
};

exports.login = async (req, res) => {
    try {
        const { email, password } = req.body;
        const user = await User.findOne({ email });
        if (!user) {
            return res.status(400).json({
                message: "Invalid credentials",
                success: false,
            });
        }
        const isMatched = await bcrypt.compare(password, user.password);
        if (!isMatched) {
            return res.status(400).json({
                message: "Invalid credentials",
                success: false,
            });
        }
        const verificationCode = Math.floor(100000 + Math.random() * 900000);
        const codeExpires = new Date();
        codeExpires.setMinutes(codeExpires.getMinutes() + 10);
        user.loginCode = verificationCode.toString();
        user.codeExpires = codeExpires;
        await user.save();

        await twilioClient.messages.create({
            body: `Your login verification code is ${verificationCode}`,
            from: process.env.TWILIO_PHONE_NUMBER,
            to: `+91` + user.phone,
        });
        return res.status(200).json({
            message: "Verification code sent to your phone.",
            userId: user._id,
        });
    } catch (error) {
        return res.status(500).json({
            success: false,
            message: "Internal Server Error, Login unsuccessful",
        });
    }
};

function generateJwtToken(userID) {
    const payload = {
        user: {
            id: userID,
        },
    };
    return jwt.sign(payload, process.env.JWT_SECRET, { expiresIn: 3600 });
}

exports.verifyUser = async (req, res) => {
    const { userId, code } = req.body;
    console.log(userId);
    try {
        const user = await User.findById(userId);
        if (!user) {
            return res.status(404).send("User not found.");
        }
        const currentTime = new Date();
        if (currentTime > user.codeExpires) {
            return res
                .status(400)
                .send(
                    "Verification code has expired. Please login again to receive a new code."
                );
        }
        if (user.loginCode !== code) {
            return res.status(400).send("Invalid verification code.");
        }

        user.verified = true;
        user.loginCode = null;
        user.codeExpires = null;
        await user.save();
        const token = generateJwtToken(user._id);
        res.status(200).json({
            message: "User successfully verified.",
            token: token,
        });
    } catch (error) {
        res.status(500).json("Verification error: " + error.message);
    }
};
Node
// authModel.js

const mongoose = require('mongoose');

const userSchema = new mongoose.Schema({
    username: {
        type: String,
        required: true
    },
    email: {
        type: String,
        required: true,
        uniques: true
    },
    password: {
        type: String,
        required: true
    },
    phone: {
        type: Number,
        required: true
    },
    verified: {
        type: Boolean,
        default: false,
        required: false
    },
    loginCode: {
        type: String,
        default: "",
        required: false
    },
    codeExpires: {
        type: Date,
        required: false
    }
});

module.exports = mongoose.model('User', userSchema);
Node
// authRoutes.js

const express = require('express');
const router = express.Router();
const authController = require('../controller/authController');

router.post('/register', authController.register);

router.post('/login', authController.login);

router.post('/verify', authController.verifyUser);

module.exports = router;
Node
// server.js

const express = require("express");
const cors = require("cors");
const mongoose = require("mongoose");
const authRoutes = require("../backend/route/authRoute");
const dotenv = require("dotenv");
dotenv.config({ path: "../backend/config/.env" });
const app = express();
app.use(cors());
app.use(express.json());

mongoose
    .connect(process.env.DB_URI, {
        family: 4,
    })
    .then(() => console.log("Mongo DB connected"))
    .catch((err) => console.log(err));

app.use("/api/auth", authRoutes);

const PORT = process.env.PORT;
app.listen(PORT, () => console.log(`Server started on port ${PORT}`));
Node
// .env file

PORT=5000
DB_URI=mongodb://localhost:27017/multi-factor-auth
JWT_SECRET=jwtSecret
TWILIO_ACCOUNT_SID=YOUR_TWILIO_ACCOUNT_SID
TWILIO_AUTH_TOKEN=YOUR_TWILIO_AUTH_TOKEN
TWILIO_PHONE_NUMBER=YOUR_TRIAL_TWILIO_PHONE_NUMBER

To start the backend run the following command.

nodemon server.js

Step 5: Install the angular CLI

npm install -g @angular/cli

Step 6: Create a new angular project

ng new frontend

Step 7: Create components for different functionalities in angular

Syntax - ng generate component <component-name>
ng generate component user
ng generate component verify

Step 8: Create services for communication between frontend and backend

Syntax - ng generate service <service-name>
ng generate service user
ng generate service shared

Project Structure (Frontend):

projectStructureFrontend

PROJECT STRUCTURE FOR FRONTEND

The updated dependencies in package.json file of frontend will look like:

"dependencies": {
"@angular/animations": "^17.2.0",
"@angular/common": "^17.2.0",
"@angular/compiler": "^17.2.0",
"@angular/core": "^17.2.0",
"@angular/forms": "^17.2.0",
"@angular/platform-browser": "^17.2.0",
"@angular/platform-browser-dynamic": "^17.2.0",
"@angular/platform-server": "^17.2.0",
"@angular/router": "^17.2.0",
"@angular/ssr": "^17.2.3",
"@auth0/angular-jwt": "^5.2.0",
"express": "^4.18.2",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
"zone.js": "~0.14.3"
}

Example: Create the required files as seen in project structure and add the following codes.

HTML
<!-- user.component.html -->

<div class="error-message" *ngIf="errorMessage">{{ errorMessage }}</div>
<div class="success-message" *ngIf="successMessage">{{ successMessage }}</div>

<div class="container" *ngIf="loginActive">
  <h2>Login</h2>
  <form (ngSubmit)="login()">
    <div class="form-group">
      <label for="email">Email:</label>
      <input
        type="email"
        class="form-control"
        id="email"
        name="email"
        [(ngModel)]="email"
        required
      />
    </div>
    <div class="form-group">
      <label for="password">Password:</label>
      <input
        type="password"
        class="form-control"
        id="password"
        name="password"
        [(ngModel)]="password"
        required
      />
    </div>
    <button type="submit" class="btn btn-primary">Login</button>
  </form>
</div>
<div class="container" *ngIf="registerActive">
  <h2>Register</h2>
  <form (submit)="register()">
    <div class="form-group">
      <label for="username">Username</label>
      <input
        type="text"
        id="username"
        class="form-control"
        [(ngModel)]="username"
        name="username"
        required
      />
    </div>
    <div class="form-group">
      <label for="email">Email</label>
      <input
        type="email"
        id="email"
        class="form-control"
        [(ngModel)]="email"
        name="email"
        required
      />
    </div>
    <div class="form-group">
      <label for="password">Password</label>
      <input
        type="password"
        id="password"
        class="form-control"
        [(ngModel)]="password"
        name="password"
        required
      />
    </div>
    <div class="form-group">
      <label for="phone">Phone Number</label>
      <input
        type="number"
        id="phone"
        class="form-control"
        [(ngModel)]="phone"
        name="phone"
        required
      />
    </div>
    <button type="submit" class="btn btn-primary">Register</button>
  </form>
</div>
HTML
<!-- verify.component.html -->

<div class="verify-container">
    <h3>Please Enter your Verification Code</h3>
    <form (ngSubmit)="onVerify()">
        <div>
            <label for="code">Verification Code: </label>
            <input type="text" id="code" [(ngModel)]="code" name="code" required>
        </div>
        <button type="submit">Verify</button>
        <p *ngIf="errorMessage">{{ errorMessage }}</p>
    </form>
</div>
HTML
<!-- dashboard.component.html -->

<div class="verify-container">
    <h3>Please Enter your Verification Code</h3>
    <form (ngSubmit)="onVerify()">
        <div>
            <label for="code">Verification Code: </label>
            <input type="text" id="code" [(ngModel)]="code" name="code" required>
        </div>
        <button type="submit">Verify</button>
        <p *ngIf="errorMessage">{{ errorMessage }}</p>
    </form>
</div>
HTML
<!-- app.component.html -->

<main class="main">
    <div class="content">
        <div class="left-side">
            <h1>{{ title }}</h1>
            <div>
                <ul>
                    <li><a (click)="login()" *ngIf="!isLoggedIn">Login</a></li>
                    <li><a (click)="register()" *ngIf="!isLoggedIn">Register</a></li>
                    <li><a (click)="logout()" *ngIf="isLoggedIn">Logout</a></li>
                </ul>
            </div>
        </div>

    </div>
</main>
<router-outlet>
</router-outlet>
CSS
/* user.component.css */

.container {
    width: 50%;
    margin: 2rem auto;
    padding: 1.5vmax;
    padding-right: 2.5vmax;
    border: 1px solid #ccc;
    border-radius: 5px;
}

h2 {
    text-align: center;
    margin-bottom: 20px;
    font-size: 2rem;
}

.form-group {
    margin-bottom: 20px;
}

label {
    display: block;
    margin-bottom: 5px;
}

input[type="text"],
input[type="email"],
input[type="password"],
input[type="number"] {
    width: 97%;
    padding: 10px;
    border: 1px solid #ccc;
    border-radius: 5px;
}

button[type="submit"] {
    width: 20%;
    padding: 1.1vmax;
    background-color: #0056b3;
    color: #fff;
    border: none;
    border-radius: 5px;
    cursor: pointer;
    font-weight: bold;
    font-size: 1rem;
    align-self: center;
    margin-top: 1vmax;
}

.container {
    width: 50%;
    margin: 2rem auto;
    padding: 1.5vmax;
    padding-right: 3.5vmax;
    border: 1px solid #ccc;
    border-radius: 5px;
}

h2 {
    text-align: center;
    margin-bottom: 20px;
    font-size: 2rem;
}

.form-group {
    margin-bottom: 20px;
}

label {
    display: block;
    margin-bottom: 5px;
}

input[type="email"],
input[type="password"] {
    width: 99%;
    padding: 10px;
    border: 1px solid #ccc;
    border-radius: 5px;
}

button[type="submit"] {
    width: 20%;
    padding: 1.1vmax;
    background-color: #0056b3;
    color: #fff;
    border: none;
    border-radius: 5px;
    cursor: pointer;
    font-weight: bold;
    font-size: 1rem;
    align-self: center;
    margin-top: 1vmax;
}

.error-message {
    color: #FF0000;
    background-color: #FFEFEF;
    padding: 10px;
    border: 1px solid #FF0000;
    border-radius: 5px;
    margin-bottom: 10px;
    margin-top: 10px;
}

.success-message {
    color: green;
    background-color: rgb(186, 218, 186);
    padding: 10px;
    border: 1px solid green;
    border-radius: 5px;
    margin-bottom: 10px;
    margin-top: 10px;
}
CSS
/* verify.component.css */

.verify-container {
    width: 100%;
    max-width: 400px;
    margin: 50px auto;
    padding: 20px;
    background-color: #f8f9fa;
    border: 1px solid #ced4da;
    border-radius: 5px;
    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
}

.verify-container h3 {
    text-align: center;
    color: #333;
    margin-bottom: 20px;
}

.verify-container form {
    display: flex;
    flex-direction: column;
}

.verify-container label {
    font-size: 16px;
    color: #495057;
    margin-bottom: 5px;
}

.verify-container input[type="text"] {
    height: 38px;
    width: 63%;
    padding: 6px 12px;
    font-size: 16px;
    color: #495057;
    background-color: #fff;
    border: 1px solid #ced4da;
    border-radius: 4px;
    margin-bottom: 20px;
}

.verify-container button {
    background-color: #3b5775;
    color: white;
    border: none;
    padding: 10px 15px;
    font-size: 16px;
    border-radius: 4px;
    cursor: pointer;
    transition: background-color 0.3s;
}

.verify-container button:hover {
    background-color: #0056b3;
}

.verify-container p {
    color: #dc3545;
    font-size: 14px;
    margin-top: 0;
}
CSS
/* dashboard.component.css */

.dashboard-container {
    margin: 20px;
    padding: 20px;
    border: 1px solid green;
    border-radius: 5px;
    text-align: center;
    color: green;
    background-color: rgb(164, 220, 164);
}
CSS
/* app.component.css */

.main {
    display: flex;
    flex-direction: column;
    align-items: center;
    background-color: darkslategrey;
    color: white;
}

.content {
    width: 100%;
    max-width: 1200px;
    padding: 20px;
}

.left-side {
    display: flex;
    justify-content: space-between;
    align-items: center;
}

.left-side h1 {
    margin: 0;
    margin-left: 3%;
}

.left-side ul {
    list-style-type: none;
    padding: 0;
    margin: 0;
}

.left-side li {
    display: inline;
    margin-right: 20px;
}

.left-side li a {
    text-decoration: none;
    color: white;
    font-weight: bold;
    font-size: 1.5rem;
}

.left-side li a:hover {
    color: lightgray;
}

a {
    cursor: pointer;
}
JavaScript
// user.component.ts

import { Component, OnInit } from "@angular/core";
import { UserService } from "../../services/user.service";
import { Router } from "@angular/router";
import { SharedService } from "../../services/shared.service";
import { FormsModule } from "@angular/forms";
import { CommonModule } from "@angular/common";

@Component({
    selector: "app-user",
    standalone: true,
    imports: [FormsModule, CommonModule],
    templateUrl: "./user.component.html",
    styleUrl: "./user.component.css",
})
export class UserComponent implements OnInit {
    username!: string;
    email!: string;
    password!: string;
    phone: number = 0;
    credentials: any = {};
    successMessage: string = "";
    errorMessage: string = "";
    loginActive: boolean = true;
    registerActive: boolean = false;
    constructor(
        private userService: UserService,
        private router: Router,
        private sharedService: SharedService
    ) { }

    ngOnInit(): void {
        this.sharedService.loginEvent.subscribe(() => {
            this.loginActive = true;
            this.registerActive = false;
            this.username = "";
            this.email = "";
            this.password = "";
            this.successMessage = "";
            this.errorMessage = "";
        });
        this.sharedService.registerEvent.subscribe(() => {
            this.registerActive = true;
            this.loginActive = false;
            this.username = "";
            this.email = "";
            this.password = "";
            this.successMessage = "";
            this.errorMessage = "";
        });
    }

    login(): void {
        const credentials = {
            email: this.email,
            password: this.password,
        };
        this.userService.login(credentials).subscribe(
            (response: any) => {
                this.loginActive = false;
                this.registerActive = false;
                this.router.navigate([`/verify/${response.userId}`]);
                this.successMessage = response.message;
                this.errorMessage = "";
            },
            (error: any) => {
                console.error("Error logging in:", error);
                this.errorMessage =
                    "Login unsuccessfull ! Please reload or try in incognito tab";
                this.successMessage = "";
            }
        );
    }

    register(): void {
        const userData = {
            username: this.username,
            email: this.email,
            password: this.password,
            phone: this.phone,
        };

        this.userService.register(userData).subscribe(
            (response: any) => {
                this.successMessage = response.message;
                this.errorMessage = "";
                this.loginActive = true;
                this.registerActive = false;
            },
            (error: any) => {
                this.errorMessage = "User not registered successfully";
                this.successMessage = "";
            }
        );
    }
}
JavaScript
// verify.component.ts

import { Component, OnInit } from "@angular/core";
import { UserService } from "../../services/user.service";
import { ActivatedRoute, Router } from "@angular/router";
import { FormsModule } from "@angular/forms";
import { CommonModule } from "@angular/common";

@Component({
    selector: "app-verify",
    standalone: true,
    imports: [FormsModule, CommonModule],
    templateUrl: "./verify.component.html",
    styleUrls: ["./verify.component.css"],
})
export class VerifyComponent implements OnInit {
    userId: string = "";
    code: string = "";
    errorMessage: string = "";
    successMessage: string = "";
    constructor(
        private authService: UserService,
        private router: Router,
        private route: ActivatedRoute
    ) { }
    ngOnInit() {
        this.route.params.subscribe((params: any) => {
            this.userId = params["id"];
        });
    }

    onVerify(): void {
        this.authService.verifyCode(this.userId, this.code).subscribe({
            next: (response: any) => {
                console.log("Verification successful", response);
                this.errorMessage = "";
                const token = response.token;
                localStorage.setItem("token", token);
                this.authService.setAuthenticationStatus(true);
                this.authService.emitLoggedInEvent();
                this.successMessage = response.message;
                this.router.navigate(["/dashboard"]);
            },
            error: (error: any) => {
                this.errorMessage = "Verification failed";
                console.error("Error during verification", error);
            },
        });
    }
}
JavaScript
// dashboard.component.ts

import { Component, OnInit } from "@angular/core";

@Component({
    selector: "app-dashboard",
    standalone: true,
    imports: [],
    templateUrl: "./dashboard.component.html",
    styleUrl: "./dashboard.component.css",
})
export class DashboardComponent implements OnInit {
    message: string;

    constructor() {
        this.message = "You are logged in successfully!";
    }

    ngOnInit(): void { }
}
JavaScript
// user.service.ts

import { HttpClient } from "@angular/common/http";
import { EventEmitter, Injectable } from "@angular/core";
import { BehaviorSubject, Observable } from "rxjs";

@Injectable({
    providedIn: "root",
})
export class UserService {
    private baseUrl = "http://localhost:5000/api/auth";
    constructor(private httpClient: HttpClient) { }

    register(userData: any): Observable<any> {
        return this.httpClient.post(`${this.baseUrl}/register`, userData);
    }

    login(credentials: any): Observable<any> {
        return this.httpClient.post(`${this.baseUrl}/login`, credentials);
    }

    private isAuthenticatedSubject = new BehaviorSubject<boolean>(false);

    isAuthenticated(): Observable<boolean> {
        return this.isAuthenticatedSubject.asObservable();
    }

    setAuthenticationStatus(isAuthenticated: boolean): void {
        this.isAuthenticatedSubject.next(isAuthenticated);
    }

    loggedInEvent: EventEmitter<any> = new EventEmitter();
    emitLoggedInEvent() {
        this.loggedInEvent.emit();
    }

    verifyCode(userId: string, code: string): Observable<any> {
        return this.httpClient.post(`${this.baseUrl}/verify`, { userId, code });
    }
}
JavaScript
// shared.service.ts

import { EventEmitter, Injectable } from '@angular/core';

@Injectable({
    providedIn: 'root'
})
export class SharedService {
    loginEvent: EventEmitter<void> = new EventEmitter<void>();
    registerEvent: EventEmitter<void> = new EventEmitter<void>();
    constructor() { }

    triggerLoginEvent(): void {
        this.loginEvent.emit();
    }

    triggerRegisterEvent(): void {
        this.registerEvent.emit();
    }
}
JavaScript
// app.routes.ts

import { Routes } from "@angular/router";
import { UserComponent } from "./components/user/user.component";
import { VerifyComponent } from "./components/verify/verify.component";
import { DashboardComponent } from "./components/dashboard/dashboard.component";

export const routes: Routes = [
    { path: "", component: UserComponent },
    { path: "verify/:id", component: VerifyComponent },
    { path: "dashboard", component: DashboardComponent },
    { path: "**", redirectTo: "/" },
];
JavaScript
// app.module.ts

import { InjectionToken, NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
import { JwtHelperService, JWT_OPTIONS } from '@auth0/angular-jwt';
import { RouterModule } from '@angular/router';
import { routes } from './app.routes';
import { UserComponent } from './components/user/user.component';
import { VerifyComponent } from './components/verify/verify.component';
import { DashboardComponent } from './components/dashboard/dashboard.component';
@NgModule({
    declarations: [
        AppComponent,
        UserComponent,
        VerifyComponent,
        DashboardComponent
    ],
    imports: [
        BrowserModule,
        FormsModule,
        RouterModule.forRoot(routes),
    ],
    exports: [RouterModule],
    providers: [{ provide: JWT_OPTIONS, useValue: JWT_OPTIONS }, JwtHelperService],
    bootstrap: [AppComponent]
})
export class AppModule { }
JavaScript
// app.component.ts

import { Component } from "@angular/core";
import { Router, RouterOutlet } from "@angular/router";
import { SharedService } from "./services/shared.service";
import { UserService } from "./services/user.service";
import { FormsModule } from "@angular/forms";
import { CommonModule } from "@angular/common";

@Component({
    selector: "app-root",
    standalone: true,
    imports: [RouterOutlet, FormsModule, CommonModule],
    templateUrl: "./app.component.html",
    styleUrl: "./app.component.css",
})
export class AppComponent {
    title = "Multi Factor Authentication";
    isLoggedIn: boolean = false;
    constructor(
        private router: Router,
        private userService: UserService,
        private sharedService: SharedService
    ) { }
    ngOnInit(): void {
        this.userService.loggedInEvent.subscribe((data: any) => {
            this.isLoggedIn = true;
        });
        if (typeof localStorage !== "undefined" && localStorage.getItem("token")) {
            this.isLoggedIn = true;
        }
    }

    login(): void {
        this.sharedService.triggerLoginEvent();
        this.router.navigate(["/"]);
    }

    register(): void {
        this.sharedService.triggerRegisterEvent();
        this.router.navigate(["/"]);
    }

    logout(): void {
        this.userService.setAuthenticationStatus(false);
        this.isLoggedIn = false;
        localStorage.removeItem("token");
        this.router.navigate(["/"]);
    }
}

To start the application run the following command.

ng serve

Output:

MultiFactor Auth in angular

OUTPUT GIF



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads