Angular 8 - Authentication and Authorization


Advertisements

Authentication is the process matching the visitor of a web application with the pre-defined set of user identity in the system. In other word, it is the process of recognizing the user’s identity. Authentication is very important process in the system with respect to security.

Authorization is the process of giving permission to the user to access certain resource in the system. Only the authenticated user can be authorised to access a resource.

Let us learn how to do Authentication and Authorization in Angular application in this chapter.

Guards in Routing

In a web application, a resource is referred by url. Every user in the system will be allowed access a set of urls. For example, an administrator may be assigned all the url coming under administration section.

As we know already, URLs are handled by Routing. Angular routing enables the urls to be guarded and restricted based on programming logic. So, a url may be denied for a normal user and allowed for an administrator.

Angular provides a concept called Router Guards which can be used to prevent unauthorised access to certain part of the application through routing. Angular provides multiple guards and they are as follows:

  • CanActivate − Used to stop the access to a route.

  • CanActivateChild − Used to stop the access to a child route.

  • CanDeactivate − Used to stop ongoing process getting feedback from user. For example, delete process can be stop if the user replies in negative.

  • Resolve − Used to pre-fetch the data before navigating to the route.

  • CanLoad − Used to load assets.

Working example

Let us try to add login functionality to our application and secure it using CanActivate guard.

Open command prompt and go to project root folder.

cd /go/to/expense-manager

Start the application.

ng serve

Create a new service, AuthService to authenticate the user.

ng generate service auth
CREATE src/app/auth.service.spec.ts (323 bytes)
CREATE src/app/auth.service.ts (133 bytes)

Open AuthService and include below code.

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

import { Observable, of } from 'rxjs';
import { tap, delay } from 'rxjs/operators';

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

   isUserLoggedIn: boolean = false;

   login(userName: string, password: string): Observable {
      console.log(userName);
      console.log(password);
      this.isUserLoggedIn = userName == 'admin' && password == 'admin';
      localStorage.setItem('isUserLoggedIn', this.isUserLoggedIn ? "true" : "false"); 

   return of(this.isUserLoggedIn).pipe(
      delay(1000),
      tap(val => { 
         console.log("Is User Authentication is successful: " + val); 
      })
   );
   }

   logout(): void {
   this.isUserLoggedIn = false;
      localStorage.removeItem('isUserLoggedIn'); 
   }

   constructor() { }
}

Here,

  • We have written two methods, login and logout.

  • The purpose of the login method is to validate the user and if the user successfully validated, it stores the information in localStorage and then returns true.

  • Authentication validation is that the user name and password should be admin.

  • We have not used any backend. Instead, we have simulated a delay of 1s using Observables.

  • The purpose of the logout method is to invalidate the user and removes the information stored in localStorage.

Create a login component using below command −

ng generate component login
CREATE src/app/login/login.component.html (20 bytes)
CREATE src/app/login/login.component.spec.ts (621 bytes)
CREATE src/app/login/login.component.ts (265 bytes)
CREATE src/app/login/login.component.css (0 bytes)
UPDATE src/app/app.module.ts (1207 bytes)

Open LoginComponent and include below code −

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

import { FormGroup, FormControl } from '@angular/forms';
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 {

   userName: string;
   password: string;
   formData: FormGroup;

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

   ngOnInit() {
      this.formData = new FormGroup({
         userName: new FormControl("admin"),
         password: new FormControl("admin"),
      });
   }

   onClickSubmit(data: any) {
      this.userName = data.userName;
      this.password = data.password;

      console.log("Login page: " + this.userName);
      console.log("Login page: " + this.password);

      this.authService.login(this.userName, this.password)
         .subscribe( data => { 
            console.log("Is Login Success: " + data); 
      
           if(data) this.router.navigate(['/expenses']); 
      });
   }
}

Here,

  • Used reactive forms.

  • Imported AuthService and Router and configured it in constructor.

  • Created an instance of FormGroup and included two instance of FormControl, one for user name and another for password.

  • Created a onClickSubmit to validate the user using authService and if successful, navigate to expense list.

Open LoginComponent template and include below template code.

<!-- Page Content -->
<div class="container">
   <div class="row">
      <div class="col-lg-12 text-center" style="padding-top: 20px;">
         <div class="container box" style="margin-top: 10px; padding-left: 0px; padding-right: 0px;">
            <div class="row">
               <div class="col-12" style="text-align: center;">
                                    <form [formGroup]="formData" (ngSubmit)="onClickSubmit(formData.value)" 
                                          class="form-signin">
                                    <h2 class="form-signin-heading">Please sign in</h2>
                                    <label for="inputEmail" class="sr-only">Email address</label>
                                    <input type="text" id="username" class="form-control" 
                                          formControlName="userName" placeholder="Username" required autofocus>
                                    <label for="inputPassword" class="sr-only">Password</label>
                                    <input type="password" id="inputPassword" class="form-control" 
                                          formControlName="password" placeholder="Password" required>
                                    <button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
                                    </form>
               </div>
            </div>
         </div>
      </div>
   </div>
</div>

Here,

Created a reactive form and designed a login form.

Attached the onClickSubmit method to the form submit action.

Open LoginComponent style and include below CSS Code.

.form-signin {
   max-width: 330px;

   padding: 15px;
   margin: 0 auto;
}

input {
   margin-bottom: 20px;
}

Here, some styles are added to design the login form.

Create a logout component using below command −

ng generate component logout
CREATE src/app/logout/logout.component.html (21 bytes)
CREATE src/app/logout/logout.component.spec.ts (628 bytes)
CREATE src/app/logout/logout.component.ts (269 bytes)
CREATE src/app/logout/logout.component.css (0 bytes)
UPDATE src/app/app.module.ts (1368 bytes)

Open LogoutComponent and include below code.

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

import { AuthService } from '../auth.service';
import { Router } from '@angular/router';

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

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

   ngOnInit() {
      this.authService.logout();
      this.router.navigate(['/']);
   }

}

Here,

  • Used logout method of AuthService.
  • Once the user is logged out, the page will redirect to home page (/).

Create a guard using below command −

ng generate guard expense
CREATE src/app/expense.guard.spec.ts (364 bytes)
CREATE src/app/expense.guard.ts (459 bytes)

Open ExpenseGuard and include below code −

import { Injectable } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router, UrlTree } from '@angular/router';
import { Observable } from 'rxjs';

import { AuthService } from './auth.service';

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

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

   canActivate(
   next: ActivatedRouteSnapshot,
   state: RouterStateSnapshot): boolean | UrlTree {
      let url: string = state.url;

          return this.checkLogin(url);
      }

      checkLogin(url: string): true | UrlTree {
         console.log("Url: " + url)
         let val: string = localStorage.getItem('isUserLoggedIn');

         if(val != null && val == "true"){
            if(url == "/login")
               this.router.parseUrl('/expenses');
            else 
               return true;
         } else {
            return this.router.parseUrl('/login');
         }
      }
}

Here,

  • checkLogin will check whether the localStorage has the user information and if it is available, then it returns true.
  • If the user is logged in and goes to login page, it will redirect the user to expenses page
  • If the user is not logged in, then the user will be redirected to login page.

Open AppRoutingModule (src/app/app-routing.module.ts) and update below code −

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { ExpenseEntryComponent } from './expense-entry/expense-entry.component';
import { ExpenseEntryListComponent } from './expense-entry-list/expense-entry-list.component';
import { LoginComponent } from './login/login.component';
import { LogoutComponent } from './logout/logout.component';

import { ExpenseGuard } from './expense.guard';

const routes: Routes = [
   { path: 'login', component: LoginComponent },
   { path: 'logout', component: LogoutComponent },
   { path: 'expenses', component: ExpenseEntryListComponent, canActivate: [ExpenseGuard]},
   { path: 'expenses/detail/:id', component: ExpenseEntryComponent, canActivate: [ExpenseGuard]},
   { path: '', redirectTo: 'expenses', pathMatch: 'full' }
];

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

Here,

  • Imported LoginComponent and LogoutComponent.
  • Imported ExpenseGuard.
  • Created two new routes, login and logout to access LoginComponent and LogoutComponent respectively.
  • Add new option canActivate for ExpenseEntryComponent and ExpenseEntryListComponent.

Open AppComponent template and add two login and logout link.

<div class="collapse navbar-collapse" id="navbarResponsive">
   <ul class="navbar-nav ml-auto">
      <li class="nav-item active">
         <a class="nav-link" href="#">Home
            <span class="sr-only" routerLink="/">(current)</span>

         </a>
      </li>
      <li class="nav-item">
         <a class="nav-link" routerLink="/expenses">Report</a>
      </li>
      <li class="nav-item">
         <a class="nav-link" href="#">Add Expense</a>
      </li>
      <li class="nav-item">

         <a class="nav-link" href="#">About</a>
      </li>
      <li class="nav-item">
                  <div *ngIf="isUserLoggedIn; else isLogOut">
                        <a class="nav-link" routerLink="/logout">Logout</a>
                  </div>

                  <ng-template #isLogOut>
                              <a class="nav-link" routerLink="/login">Login</a>
                  </ng-template>
      </li>
   </ul>
</div>

Open AppComponent and update below code −

import { Component } from '@angular/core';

import { AuthService } from './auth.service';

@Component({
   selector: 'app-root',
   templateUrl: './app.component.html',
   styleUrls: ['./app.component.css']
})
export class AppComponent {

   title = 'Expense Manager';
   isUserLoggedIn = false;

   constructor(private authService: AuthService) {}

   ngOnInit() {
      let storeData = localStorage.getItem("isUserLoggedIn");
      console.log("StoreData: " + storeData);

      if( storeData != null && storeData == "true")
         this.isUserLoggedIn = true;
      else


         this.isUserLoggedIn = false;
   }
}

Here, we have added the logic to identify the user status so that we can show login / logout functionality.

Open AppModule (src/app/app.module.ts) and configure ReactiveFormsModule

import { ReactiveFormsModule } from '@angular/forms'; 
imports: [ 
   ReactiveFormsModule 
]

Now, run the application and the application opens the login page.

ReactiveFormsModule

Enter admin and admin as username and password and then, click submit. The application process the login and redirects the user to expense list page as shown below −

FormsModule

Finally, your can click logout and exit the application.

Advertisements