Skip to content

WebNinjaDeveloper.com

Programming Tutorials




Menu
  • Home
  • Youtube Channel
  • PDF Invoice Generator
Menu

Angular 14/15 JWT Login & Registration Auth System in Node.js & Express Using MongoDB in Browser

Posted on April 1, 2023

 

 

Welcome folks today in this blog post we will be building a jwt authentication system in angular and node.js and express using mongodb in browser. All the full source code of the application is shown below.

 

 

BUY FULL SOURCE CODE

 

 

Get Started

 

 

In order to get started you need to make a new directory called nodeangularauth and inside it we will be making the angular frontend and node.js backend as well

 

 

mkdir nodeangularauth

 

 

And after that we will be first of all creating the node.js backend api for the authentication system as shown below

 

 

Making the Backend

 

 

Now we need to make the backend directory as shown below

 

 

mkdir backend

 

 

npm init -y

 

 

npm i express

 

 

npm i cors

 

 

npm i mongoose

 

 

npm i cookie-parser

 

 

npm i bcryptjs

 

 

npm i jsonwebtoken

 

 

Express : This will be the web server which we will be using for our application

 

 

mongoose : It will be dependency which we will be using for interacting with the Mongodb database

 

 

bcryptjs : This will be used for hashing the passwords

 

 

jsonwebtoken This library will generate and verify the json web token and let users login into the protected routes.

 

 

cookie-parser : This dependency will allow users to create and read cookies inside the express application

 

 

Directory Structure

 

 

At the end of this app this will be the directory structure of the backend as shown below

 

 

 

 

 

Now we need to make the index.js file and copy paste the following code

 

 

index.js

 

 

JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
const express = require('express')
const mongoose = require('mongoose')
const cors = require('cors')
const cookieParser = require('cookie-parser')
 
mongoose.connect('mongodb://localhost/node_auth', {
    useNewUrlParser: true,
    useUnifiedTopology: true
}, () => {
    console.log('connected to the database')
})
 
const routes = require('./routes/routes')
 
app = express()
 
app.use(cookieParser())
app.use(cors({
    credentials: true,
    origin: ['http://localhost:4200']
}))
 
app.use(express.json())
 
app.use('/api', routes)
 
app.listen(8000)

 

 

As you can see we are starting the express app at the port 8000 and then we are also passing the middleware of the cookie parser so that we can allow to read and set the cookies. And then we are also importing the routes file and also passing it as the middleware. And then we are also passing the cors middleware and here we are passing the origin where we allow the requests from that particular origin only. And also we are importing the mongoose library at the top and then we are connecting it to the database

 

 

Making the Models

 

 

Now we need to make the models folder and there we need to make the user.js file and copy paste the following code

 

 

models/user.js

 

 

JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const mongoose = require('mongoose')
 
const userSchema = new mongoose.Schema({
    name: {
        type: String,
        required: true
    },
    email: {
        type: String,
        unique: true,
        required: true
    },
    password: {
        type: String,
        required: true
    }
})
 
module.exports = mongoose.model('User', userSchema)

 

 

As you can see we are importing the mongoose library at the top and then we are defining the schema of the collection and there we have three fields such as name email and password and then we are creating the model with the specified schema as shown above.

 

 

Making the Routes

 

 

Now we will be making the routes for defining all the auth operations such as the login, register and logout functions as shown below

 

 

routes/routes.js

 

 

JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
const router = require("express").Router();
const bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken");
const User = require("../models/user");
 
router.post("/register", async (req, res) => {
  const salt = await bcrypt.genSalt(10);
  const hashedPassword = await bcrypt.hash(req.body.password, salt);
  const record = await User.findOne({ email: req.body.email });
 
  if (record) {
    return res.status(400).send({
      message: "Email is already registered",
    });
  } else {
    const user = new User({
      name: req.body.name,
      email: req.body.email,
      password: hashedPassword,
    });
 
    const result = await user.save();
 
    const { _id } = await result.toJSON();
 
    const token = jwt.sign({ _id: _id }, "secret");
 
    res.cookie("jwt", token, {
      httpOnly: true,
      maxAge: 24 * 60 * 60 * 1000, // 1 day
    });
 
    res.send({
      message: "success",
    });
  }
});
 
router.post("/login", async (req, res) => {
  const user = await User.findOne({ email: req.body.email });
 
  if (!user) {
    return res.status(404).send({
      message: "User not Found",
    });
  }
 
  if (!(await bcrypt.compare(req.body.password, user.password))) {
    return res.status(400).send({
      message: "Password is Incorrect",
    });
  }
 
  const token = jwt.sign({ _id: user._id }, "secret");
 
  res.cookie("jwt", token, {
    httpOnly: true,
    maxAge: 24 * 60 * 60 * 1000, // 1 day
  });
 
  res.send({
    message: "success",
  });
});
 
router.get("/user", async (req, res) => {
  try {
    const cookie = req.cookies["jwt"];
 
    const claims = jwt.verify(cookie, "secret");
 
    if (!claims) {
      return res.status(401).send({
        message: "unauthenticated",
      });
    }
 
    const user = await User.findOne({ _id: claims._id });
 
    const { password, ...data } = await user.toJSON();
 
    res.send(data);
  } catch (e) {
    return res.status(401).send({
      message: "unauthenticated",
    });
  }
});
 
router.post("/logout", (req, res) => {
  res.cookie("jwt", "", { maxAge: 0 });
 
  res.send({
    message: "success",
  });
});
 
module.exports = router;

 

 

As you can see we are importing all the dependencies at the top and the inside the register and login functions we are taking the email and password and then inserting it inside the database and also we are checking if already the email exists or not. And also if password match or not in case of login and then we are sending the error messages back to the client as well. And also we are setting the jwt token inside the cookie using the sign() method with the secret key and also we are verifying the jwt token using the verify() method and lastly for the logout method we are clearing out the cookie data.

 

 

Making the Angular Frontend

 

 

Now we need to make the frontend directory and inside it we need to make a new angular project using the below command as shown below

 

 

mkdir frontend

 

 

ng new jwtfrontend

 

 

cd jwtfrontend

 

 

Now we will be installing the dependencies which will be needed for this angular project as shown below

 

 

npm i sweetalert2

 

 

This is the only library we need to in order to display popup messages to the user when they enter invalid data

 

 

Now you will see the below directory structure at the end of the angular project as shown below

 

 

 

 

 

Creating the Components

 

 

As you can see in the above directory screenshot we need to create the four components for our angular frontend namely the home component where we will be displaying the user info and then we have the login and register forms component and lastly we will be having the navigation component where we will displaying all the buttons which will be common inside every component.

 

 

ng generate component home

 

 

ng generate component nav

 

 

ng generate component login

 

 

ng component generate register

 

 

 

Making the Routes

 

 

Now guys we need to edit the app-routing.module.ts file and copy paste the following code

 

 

app-routing.module.ts

 

 

TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import {NgModule} from '@angular/core';
import {Routes, RouterModule} from '@angular/router';
import {HomeComponent} from './home/home.component';
import {LoginComponent} from './login/login.component';
import {RegisterComponent} from './register/register.component';
 
const routes: Routes = [
  {path: '', component: HomeComponent},
  {path: 'login', component: LoginComponent},
  {path: 'register', component: RegisterComponent},
];
 
@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule {
}

 

 

As you can see we are registering all the components that we created earlier with their respective paths inside the routes variable. Now the routing of our angular project is properly configured for example if user goes to the / path then he or she will be redirected to the HomeComponent it will be rendered.

 

 

 

Adding the Forms & HttpClient Module

 

 

Now for this angular frontend we will be using the html5 forms and also we will be making the http requests to the backend. For both these tasks we need to include the formsModule and the httpClientModule inside the app.module.ts file of your angular project.

 

 

 

app.module.ts

 

 

 

TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import {BrowserModule} from '@angular/platform-browser';
import {NgModule} from '@angular/core';
 
import {AppRoutingModule} from './app-routing.module';
import {AppComponent} from './app.component';
import {RegisterComponent} from './register/register.component';
import {LoginComponent} from './login/login.component';
import {HomeComponent} from './home/home.component';
import {NavComponent} from './nav/nav.component';
import {FormsModule, ReactiveFormsModule} from '@angular/forms';
import {HttpClientModule} from '@angular/common/http';
 
@NgModule({
  declarations: [
    AppComponent,
    RegisterComponent,
    LoginComponent,
    HomeComponent,
    NavComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    FormsModule,
    ReactiveFormsModule,
    HttpClientModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule {
}

 

 

Styling the App

 

 

Now guys for styling the app we need to add the bootstrap cdn inside the index.html file of your angular project

 

 

index.html

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Angular14frontend</title>
  <base href="/">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" type="image/x-icon" href="favicon.ico">
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet"
        integrity="sha384-giJF6kkoqNQ00vy+HMDP7azOuL0xtbfIcaT9wjKHr8RbDVddVHyTfAAsrekwKmP1" crossorigin="anonymous">
</head>
<body>
  <app-root></app-root>
</body>
</html>

 

 

And then inside the app.component.css file we need to copy paste the below custom css as shown below

 

 

app.component.css

 

 

CSS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
.form-signin {
  width: 100%;
  max-width: 330px;
  padding: 15px;
  margin: auto;
}
 
.form-signin .checkbox {
  font-weight: 400;
}
 
.form-signin .form-control {
  position: relative;
  box-sizing: border-box;
  height: auto;
  padding: 10px;
  font-size: 16px;
}
 
.form-signin .form-control:focus {
  z-index: 2;
}
 
.form-signin input[type="email"] {
  margin-bottom: -1px;
  border-bottom-right-radius: 0;
  border-bottom-left-radius: 0;
}
 
.form-signin input[type="password"] {
  margin-bottom: 10px;
  border-top-left-radius: 0;
  border-top-right-radius: 0;
}

 

 

And now inside the app.component.html file we need to include the below html code as shown below

 

 

app.component.html

 

 

1
2
3
4
5
<app-nav></app-nav>
 
<main class="form-signin">
  <router-outlet></router-outlet>
</main>

 

 

As you can see the nav component we included at the top is common in all the pages and after that we have embedded the router and based upon which path is hit different components will be loaded.

 

 

And lastly guys you need to add the below line inside the tsconfig.json file to make this property false inside the compiler options as shown below

 

 

tsconfig.json

 

 

 

1
2
3
4
5
"compilerOptions": {
 
"strictPropertyInitialization": false
 
}

 

 

 

 

 

 

Making the Navbar

 

 

Now guys we will be making the navbar component in which we will be displaying the login and register buttons for now as shown below

 

 

nav.component.html

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<nav class="navbar navbar-expand-md navbar-dark bg-dark mb-4">
  <div class="container-fluid">
    <a routerLink="/" class="navbar-brand">Home</a>
 
    <div>
      <ul class="navbar-nav me-auto mb-2 mb-md-0">
        <li class="nav-item">
          <a routerLink="/login" class="nav-link">Login</a>
        </li>
        <li class="nav-item">
          <a routerLink="/register" class="nav-link">Register</a>
        </li>
      </ul>
 
    </div>
  </div>
</nav>

 

 

 

 

 

Registering Users

 

 

Now guys we will be making the register form and allowing the user to enter the name email and the password as shown below

 

 

register.component.html

 

 

1
2
3
4
5
6
7
8
9
10
11
<form [formGroup]="form" (submit)="submit()">
  <h1 class="h3 mb-3 fw-normal">Please register</h1>
 
  <input formControlName="name" class="form-control" placeholder="Name" required>
 
  <input formControlName="email" type="email" class="form-control" placeholder="Email" required>
 
  <input formControlName="password" type="password" class="form-control" placeholder="Password" required>
 
  <button class="w-100 btn btn-lg btn-primary" type="submit">Submit</button>
</form>

 

 

As you can see we have binded the submit directive to the form and we are executing the submit() method once we click the register button in the form. And now we need to define this method inside the register.component.ts file and copy paste the following code

 

 

register.component.ts

 

 

TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';
import { HttpClient } from '@angular/common/http';
import { Router } from '@angular/router';
import Swal from 'sweetalert2';
 
@Component({
  selector: 'app-register',
  templateUrl: './register.component.html',
  styleUrls: ['./register.component.css'],
})
export class RegisterComponent implements OnInit {
  form: FormGroup;
 
  constructor(
    private formBuilder: FormBuilder,
    private http: HttpClient,
    private router: Router
  ) {}
 
  ngOnInit(): void {
    this.form = this.formBuilder.group({
      name: '',
      email: '',
      password: '',
    });
  }
 
   ValidateEmail = (email: any) => {
 
    var validRegex = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/;
  
    if (email.match(validRegex)) {  
  
      return true;
  
    } else {
  
      return false;
  
    }
  
  }
 
  submit(): void {
 
    let user = this.form.getRawValue();
 
    if (user.name == "" || user.email == '' || user.password == '') {
      Swal.fire('Error', 'Please enter all the fields', 'error');
 
    }else if(!this.ValidateEmail(user.email)){
 
      Swal.fire('Error', 'Please enter a valid email address', 'error');
 
    } else {
 
    this.http
      .post('http://localhost:8000/api/register', this.form.getRawValue(), {
        withCredentials: true,
      })
      .subscribe(() => this.router.navigate(['/']),(err) => {
        Swal.fire("Error",err.error.message,'error')
      })
 
    }
  }
}

 

 

As you can see we are getting the values submitted by the user and first of all checking if the values are not empty and also the email is valid or not and we are showing error messages with the help of sweetAlert2 library as well. And lastly if details are correct we are making the post request with the help of httpClient to the backend and their we are passing the data and then we are also passing the withCredentials option which automatically saves the jwt token to the cookies and pass it to the backend. And then inside the subscribe() callback we get the data and here we are redirecting the user to the home page. And if any error takes place we are showing the error message to the user.

 

 

 

 

 

 

 

 

If details are correct then the user will be saved inside the mongodb database as shown below

 

 

 

 

 

 

Showing User Details

 

 

Now guys we need to write the code inside the home component where we will be redirected once we are successfully registered or logged in. And for this you need to copy paste the following code in home.component.ts file

 

 

home.component.ts

 

 

TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import { Component, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Emitters } from '../emitters/emitters';
 
@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.css'],
})
export class HomeComponent implements OnInit {
  message = '';
 
  constructor(private http: HttpClient) {}
 
  ngOnInit(): void {
    this.http
      .get('http://localhost:8000/api/user', { withCredentials: true })
      .subscribe(
        (res: any) => {
          this.message = `Hi ${res.name}`;
          Emitters.authEmitter.emit(true);
        },
        (err) => {
          this.message = 'You are not logged in';
          Emitters.authEmitter.emit(false);
        }
      );
  }
}

 

 

As you can see we are using the ngOnInit() lifecycle method and inside it we are making the get request to the backend to get the user and we are passing the jwt token which is generated in the cookies with the help of withCredentials option set to true in the options. And now inside the callback function we are creating the dynamic message hi followed by the name of the user. And for this we need to communicate to the nav component. For this we need to use the emitters class of the angular core library. For this you need to create a folder called emitters and inside it we need to define the emitter.ts file and copy paste the below code

 

 

emitters/emitter.ts

 

 

TypeScript
1
2
3
4
5
import {EventEmitter} from '@angular/core';
 
export class Emitters {
  static authEmitter = new EventEmitter<boolean>();
}

 

 

Now we are using this emitter in the above code to push the value of the authenticated user it will be boolean either true or false depending on the state of the user. Now any component can subscribe to this emitter and get the value. Now we will be going to the nav.component.ts file and copy paste the following code

 

 

nav.component.ts

 

 

TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import {Component, OnInit} from '@angular/core';
import {Emitters} from '../emitters/emitters';
import {HttpClient} from '@angular/common/http';
 
@Component({
  selector: 'app-nav',
  templateUrl: './nav.component.html',
  styleUrls: ['./nav.component.css']
})
export class NavComponent implements OnInit {
  authenticated = false;
 
  constructor(private http: HttpClient) {
  }
 
  ngOnInit(): void {
    Emitters.authEmitter.subscribe(
      (auth: boolean) => {
        this.authenticated = auth;
      }
    );
  }
 
  logout(): void {
    this.http.post('http://localhost:8000/api/logout', {}, {withCredentials: true})
      .subscribe(() => this.authenticated = false);
  }
 
}

 

 

As you can see we are importing the emitter created by the home component and then we are getting the value of it by subscribing to it and depending on it’s value we are showing the logout button only if the user is authenticated in the html. And also we have defined the logout method where we are simply making the authenticated value to false and hiding the logout button and showing the login and register button.

 

And now inside the nav.component.html file we need make slight modifications as shown below

 

 

nav.component.html

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<nav class="navbar navbar-expand-md navbar-dark bg-dark mb-4">
  <div class="container-fluid">
    <a routerLink="/" class="navbar-brand">Home</a>
 
    <div>
      <ul class="navbar-nav me-auto mb-2 mb-md-0" *ngIf="!authenticated">
        <li class="nav-item">
          <a routerLink="/login" class="nav-link">Login</a>
        </li>
        <li class="nav-item">
          <a routerLink="/register" class="nav-link">Register</a>
        </li>
      </ul>
 
      <ul class="navbar-nav me-auto mb-2 mb-md-0" *ngIf="authenticated">
        <li class="nav-item">
          <a routerLink="/login" class="nav-link" (click)="logout()">Logout</a>
        </li>
      </ul>
    </div>
  </div>
</nav>

 

 

We have added a slight ngIf condition where we have passed the value of the authenticated boolean variable.

 

 

 

 

 

Adding the Login Form

 

 

Now guys we will be adding the code for displaying the login form so go to login.component.html file and copy paste the below html code

 

 

login.component.html

 

 

1
2
3
4
5
6
7
8
9
<form [formGroup]="form" (submit)="submit()">
  <h1 class="h3 mb-3 fw-normal">Please sign in</h1>
 
  <input formControlName="email" type="email" class="form-control" placeholder="Email" required>
 
  <input formControlName="password" type="password" class="form-control" placeholder="Password" required>
 
  <button class="w-100 btn btn-lg btn-primary" type="submit">Sign in</button>
</form>

 

 

As you can see we have two fields to enter the email and password and we have attached the event handler when we submit the form. Now we need to define this function inside the login.component.ts file as shown below

 

 

login.component.ts

 

 

TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';
import { HttpClient } from '@angular/common/http';
import { Router } from '@angular/router';
import Swal from 'sweetalert2';
 
@Component({
  selector: 'app-login',
  templateUrl: './login.component.html',
  styleUrls: ['./login.component.css'],
})
export class LoginComponent implements OnInit {
  form: FormGroup;
 
  constructor(
    private formBuilder: FormBuilder,
    private http: HttpClient,
    private router: Router
  ) {}
 
  ngOnInit(): void {
    this.form = this.formBuilder.group({
      email: '',
      password: '',
    });
  }
 
  ValidateEmail = (email: any) => {
 
    var validRegex = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/;
  
    if (email.match(validRegex)) {  
  
      return true;
  
    } else {
  
      return false;
  
    }
  
  }
 
  submit(): void {
 
    let user = this.form.getRawValue();
 
    if (user.email == '' || user.password == '') {
      Swal.fire('Error', 'Please enter all the fields', 'error');
 
    }else if(!this.ValidateEmail(user.email)){
 
      Swal.fire('Error', 'Please enter a valid email address', 'error');
 
    } else {
 
      this.http
        .post('http://localhost:8000/api/login', user, {
          withCredentials: true,
        })
        .subscribe(
          (res) => this.router.navigate(['/']),
          (err) => {
            Swal.fire('Error', err.error.message, 'error');
          }
        );
    }
  }
}

 

 

As you can see we are once again getting the values submitted by the user validating it and showing the error messages using the sweetlaert2 library and then making a post request to the backend api passing the user info and then we are redirecting the user to the home page.

 

 

 

 

 

 

 

If you see your browser cookies by inspect element you will see the jwt token created as shown below

 

 

 

 

BUY FULL SOURCE CODE

 

 

Recent Posts

  • Node.js Express Project to Remove Background of Images Using Rembg & Formidable Library in Browser
  • Node.js Tutorial to Remove Background From Image Using Rembg & Sharp Library in Command Line
  • Python 3 Flask Project to Remove Background of Multiple Images Using Rembg Library in Browser
  • Python 3 Rembg Library Script to Bulk Process Multiple Images and Remove Background in Command Line
  • Python 3 Rembg Library Script to Remove Background From Image in Command Line
  • Angular
  • Bunjs
  • C#
  • Deno
  • django
  • Electronjs
  • java
  • javascript
  • Koajs
  • kotlin
  • Laravel
  • meteorjs
  • Nestjs
  • Nextjs
  • Nodejs
  • PHP
  • Python
  • React
  • ReactNative
  • Svelte
  • Tutorials
  • Vuejs




©2023 WebNinjaDeveloper.com | Design: Newspaperly WordPress Theme