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.
Top comments (2)
ERROR in src/app/app-routing.module.ts(7,59): error TS2304: Cannot find name 'AuthGuard'.
Hello, it seems you missed to import the
AuthGuard
and I missed to reference that in the tutorial also. My bad :)Simply do,
in
app-routing.module.ts
. And you should be good to go. Please refer the source code attached in the tutorial for further reference. Thanks for reading.