How To Build Authentication in Angular Using Node and Passport

Jay - Nov 19 '19 - - Dev Community

In this tutorial, you'll learn how to build an authentication in your Node Angular app using Passport.js.

The tutorial assumes the reader to be familiar with creating a basic application using Angular and Node.js Express framework. You'll implement the Authentication using Passport on top of an Angular Sales Dashboard application covered in a previous tutorial.

The source code from this tutorial is available on GitHub.

Why Authentication?

Why do you need authentication? Well, adding some kind of authentication makes your application secure and prevents unauthorized access.

Authentication can be done with a username and password or it can be a One-Time Password (OTP) sent to your phone. Using existing user information from popular social networks such as Google, Facebook, etc. is another way to add authentication.

What is Passport.js?

Passport.js is a middleware that can be easily used in your Node.js application. It helps to authenticate using username and password, Facebook, GitHub, etc.

From the official documentation,

Passport is authentication middleware for Node.js. Extremely flexible and modular, Passport can be unobtrusively dropped into any Express-based web application. A comprehensive set of strategies support authentication using a username and password, Facebook, Twitter and more.

Getting Started

Let' start by cloning the source code of the Angular Sales Dashboard app from its GitHub repository.

git clone https://github.com/JscramblerBlog/angular_dashboard

Navigate to the project directory and install the required dependencies.

cd angular_dashboard
npm install

Once the dependencies have been installed, start the Angular application server.

npm start

You'll have the Angular application running on localhost:4200.

Setting Up Authentication API

To set up the authentication API, you need to create a Node project.

mkdir node-api
cd node-api
npm init

Enter the required details and you will have the Node project setup. Next, install the Express framework for creating the API.

npm install express --save

Once you have Express installed, create a file called app.js and add the following code to it:

const express = require('express');
const app = new express();

app.post('/authenticate', (req, res) => {
    res.status(200).json({"statusCode" : 200 ,"message" : "hello"});
});

app.listen(3000, () => {
    console.log('App running at 3000')
})

That's the basic Express server with an endpoint. You now need to add a custom middleware to the /authenticate route.

What is a Middleware?

A middleware is a function that can intercept a request. It has access to the request, response objects and a next function. This next function, when invoked, executes the next middleware.

Let's add a custom middleware to the route.

const auth = () => {
    return (req, res, next) => {
        next()
    }
}

app.post('/authenticate', auth() , (req, res) => {
    res.status(200).json({"statusCode" : 200 ,"message" : "hello"});
});

The auth custom middleware does nothing but invoke the next middleware by calling next. Inside the auth middleware, you'll authenticate the user using Passport.

To use Passport, you need to install both passport and passport-local.

npm install passport --save
npm install passport-local --save

You'll be using the passport-local strategy to authenticate the user login using a username and password.

Require both passport and passport-local in app.js.

const  passport  =  require('passport');
const  LocalStrategy  =  require('passport-local').Strategy;

Passport has a number of strategies when it comes to authentication. You'll be using the local strategy in this tutorial and so we need to define it as below.

passport.use(new LocalStrategy(
    function(username, password, done) {
        if(username === "admin" && password === "admin"){
            return done(null, username);
        } else {
            return done("unauthorized access", false);
        }
    }
));

The local strategy uses the username and password for authentication. For the sake of this tutorial, we have hardcoded the username and password check.

Before getting into the detailed code, let's have a look at how the Passport authentication executes.

  • A request is received at the authenticate route.
  • The custom middleware intercepts the request and makes the Passport authentication call.
  • On successful authentication, Passport stores the user data in the session.
  • On subsequent requests, Passport fetches the user data from the session for authentication.

Let's make the Passport authentication call inside the custom middleware auth.

const auth = () => {
    return (req, res, next) => {
        passport.authenticate('local', (error, user, info) => {
            if(error) res.status(400).json({"statusCode" : 200 ,"message" : error});
            req.login(user, function(error) {
                if (error) return next(error);
                next();
            });
        })(req, res, next);
    }
}

passport.authenticate invokes the passport-local strategy and, once authentication is done, the callback is invoked.

On successful authentication, Passport saves the user data in the session. For that to happen, you need to invoke req.login with the user object.

You also need to define the serialize and deserialize method to facilitate user data storage in the session and retrieving the data on subsequent requests.

passport.serializeUser(function(user, done) {
    if(user) done(null, user);
});

passport.deserializeUser(function(id, done) {
    done(null, id);
});

Let’s now install and use the body-parser middleware so that the app can parse the posted parameters.

npm install body-parser --save

To use Passport, you need to initialize it and use it as below.

app.use(passport.initialize());
app.use(passport.session());

To authenticate subsequent requests, you can define another middleware function. This function checks if the user data exists in the request. passport provides a method called req.isAuthenticaed which can be used to check if the user is authenticated.

Here is how the middleware function looks:

const isLoggedIn = (req, res, next) => {
    if(req.isAuthenticated()){
        return next()
    }
    return res.status(400).json({"statusCode" : 400, "message" : "not authenticated"})
}

You can use the above middleware on other routes as shown below:

app.get('/getData', isLoggedIn, (req, res) => {
    res.json("data")
})

Let's move on to adding the authentication to the Angular Dashboard app.

Authenticating the Angular App

To add authentication to the Angular dashboard app, you need to add routes to the Angular application. From the project directory, execute the following command:

ng generate module app-routing --flat --module=app

It should create the routing module. Open app-module.routing.ts and replace it with the following code:

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { AppComponent } from './app.component';
import { LoginComponent } from './login/login.component';

const routes: Routes = [
  { path: 'home', component: AppComponent },
  { path: 'login', component : LoginComponent}
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

You'll be using two additional components in the Angular app called the login and root component. The LoginComponent will handle the login functionality and RootComponent will serve as the container for rendering different views.

ng generate component login
ng generate component root

Adding Route Guard To The Home Route

To authenticate the Angular route from any unauthorized access, you'll be adding a route guard. The guard uses an authorization service to check if the route access is authenticated.
Let's create an authorization service to check is access is authenticated.

ng generate service auth

The above command creates a service called auth.service.ts. Add the following code to it:

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';


@Injectable({
  providedIn: 'root'
})
export class AuthService {

  constructor(private http : HttpClient) { }

  public isAuthenticated() : Boolean {
    let userData = localStorage.getItem('userInfo')
    if(userData && JSON.parse(userData)){
      return true;
    }
    return false;
  }

  public setUserInfo(user){
    localStorage.setItem('userInfo', JSON.stringify(user));
  }

  public validate(email, password) {
    return this.http.post('/api/authenticate', {'username' : email, 'password' : password}).toPromise()
  }
}

After validating the user login, AuthService saves the user information. It exposes a method called isAuthenticated which can be utilized by the AuthGuardService service to authenticate the route.

Let's create AuthGuardService.

ng generate service authGuard

Add the following code to the auth-guard.service.ts file.

import { Injectable } from '@angular/core';
import { CanActivate,Router } from '@angular/router';
import {AuthService} from './auth.service';

@Injectable({
  providedIn: 'root'
})
export class AuthGuardService implements CanActivate {

  constructor(private authService : AuthService, private route : Router) { }

  canActivate(){
    if(this.authService.isAuthenticated()){
      return true;
    }
    this.route.navigate(['login']);
    return false;
  }
}   

The above AuthGuardService implements the CanActivate route guard. It means that, if the guard returns true, the navigation will continue; otherwise, it navigates to login.

Import AuthGuardService in app-routing.module.ts.

import { 
  AuthGuardService as AuthGuard 
} from './auth-guard.service';

Add the route guard to the home route in app-routing.module.ts.

const routes: Routes = [
  { path: 'home', component: AppComponent, canActivate : [AuthGuard] },
  { path: 'login', component : LoginComponent}
];

Inside the Login component, on click you need to make the API call the Node authentication API. Here is how the login.component.ts file looks:

import { Component, OnInit } from '@angular/core';
import { AuthService } from '../auth.service';
import { Router } from '@angular/router';

@Component({
  selector: 'app-login',
  templateUrl: './login.component.html',
  styleUrls: ['./login.component.css']
})
export class LoginComponent implements OnInit {

  userEmail : String;
  userPassword : String;

  constructor(private authService : AuthService, private router : Router) { }

  ngOnInit() {

  }

  login(){
    this.authService.validate(this.userEmail, this.userPassword)
    .then((response) => {
      this.authService.setUserInfo({'user' : response['user']});
      this.router.navigate(['home']);

    })
  }

}

For the API call to work from Angular to Node, you need to add a proxy conf file in the Angular app. Create a file called proxy.conf.json and add the following code :

{
    "/api/*": {
      "target": "http://localhost:3000",
      "pathRewrite": {"^/api" : ""}
    }
  }

Save the above changes and run the Angular app along with the Node REST API. Open the localhost:4200/home route and you will be redirected to the login screen. Once there, enter the credentials as admin and admin and you will be redirected to the Angular dashboard screen.

Wrapping it Up

In this tutorial, you learned how to authenticate a REST API using Passport. You also learned how to authenticate the Angular routes using route guards.

You used the passport-local strategy to implement authentication. In addition to passport-local, there are a number of other Passport strategies for authentication.

Finally, don't forget to pay special attention if you're developing commercial Angular apps that contain sensitive logic. You can protect them against code theft, tampering, and reverse engineering by following our guide.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .