Search

Suggested keywords:
  • Java
  • Docker
  • Git
  • React
  • NextJs
  • Spring boot
  • Laravel

Angular Security - Authentication Service

  • Share this:
post-title

Angular is a framework for creating single page web application. To know more about Angular and how to get started, please refer our previous article Getting started on Augular 7. Angular facilitates the security feature and protection mechanism. It provides frameworks by verifying all the routing urls with security authguard interface to validate and verify the user and its permissions.

Authentication Implementation

Singleton service class can be implemented to invoke authentication url and get the user principal to the localstorage after user key in the username and password in the login page. Injectable with provided in root will create a singleton service across angular app and isLoggedIn class level variable maintains the status of logged in of the Angular app.

File: auth.service.ts

  import { Injectable } from '@angular/core';
  import { HttpClient } from '@angular/common/http';
  import { Observable } from 'rxjs';
  @Injectable({
     providedIn: 'root'
  })
  export class AuthService {

  isLoggedIN: boolean = false;

  constructor(private httpclient: HttpClient) { }

  setIsloggedIntrue(){
     this.isLoggedIN = true
  }

  login(credentials): Observable<Object> {
  console.log(credentials);
  const formData = new FormData();
  formData.set('username', credentials.username);
  formData.set('password', credentials.password);
  return this.httpclient.post('/api/authenticate', formData);
}

 setLoggedIn() {
     this.isLoggedIN = true;
   }
}


Login component file will invoke the above login service after the user clicks the login button. Once successful login, it stores user principal data like username, userprofile image to the local storage of the browser.

File: logincomponent.ts

 login(credentials) {
    console.log(credentials);
    this.loginRepository.submitLogin(credentials)
         .subscribe((val) => {
             console.log("POST call successful value returned in body", val);
            if (val && val['id']) {
                console.log("going to navigate to equipments");
                this.authService.setLoggedIn();
                localStorage.setItem("id", val['id'])
                localStorage.setItem("username", val['firstname'])
                localStorage.setItem('userimage', val['image'])
                this.router.navigate(['home-page']); //login success, then it will land on the home page.
            } else {
                this.loginError = true
                setTimeout(() => {
                      this.loginError = false
                }, 3000)
           }
      },
      (response) => {
         console.log("POST call in error", response);
         this.loginError = true //it will show the error message saying "username or password is invalid
         setTimeout(() => {
              this.loginError = false
         }, 3000)
      },
      () => {
          console.log("The POST observable is now completed.");
   });
}

Now we need to protect all the resources urls with authenticated access. To do that, we need to implement CanActivate interface which we will check the authorization service state and local storage whether user already authenticated.

Auth guard service:

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

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

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

      canActivate() {
         console.log(this.authService.isLoggedIN);
         if (this.authService.isLoggedIN) return true;
         if (localStorage.getItem("id")) return true;
         this.router.navigate(["/login"]);
         return false;
     }

     setLoggedIn() {
         this.authService.isLoggedIN = true;
     }
  }

Auth guard will be wired up in all the secured resources routing paths like home module by mentioning it as canActivate options. So when routing happens to that module, it will be checked whether the user logged in by invoking AuthGuard and then invoke the login page.

 import { NgModule } from "@angular/core";
 import { Routes, RouterModule } from "@angular/router";
 import { LoginComponent } from "./components/login/login.component";
 import { AuthGaurd } from './services/auth-gaurd.service';

 const approutes: Routes = [{
     path: "login",
     component: LoginComponent
 }, {
     path: "",
     loadChildren: () => import('src/app/components/home.module').then(m => m.HomeModule),
     canActivate: [AuthGaurd]
  }
];

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

  Routersnapshot takes the current url from the route snapshot url

  this.returnUrl = this.route.snapshot.queryParams['returnUrl'] || '/'; <!-- it fetches the current url -->

  This can be redirected once we do successful login.

  this.router.navigate([this.returnUrl]);

Http Interceptors - JWT Intereceptor/Error Interceptor:

Once user authenticated, every time the backend request should carry the authentication service bearer token so web server process the authorized request. We can create interceptor which get the token from authentication service and add it to the request headers. After header created, it can be chained to next intercept handler chain.


 @Injectable()
    export class JwtInterceptor implements HttpInterceptor {
    constructor(private authenticationService: AuthenticationService) {}

    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    <!-- add authorization header with jwt token if available -->
    let currentUser = this.authenticationService.currentUserValue;
    if (currentUser && currentUser.token) {
        request = request.clone({
        setHeaders: {
            Authorization: `Bearer ${currentUser.token}`
        }
    });
 }

   return next.handle(request);
}

Response to any request returns to 401 or 403 status code, it needs to be immedatiely redirected to the login page irrespective of the current user page. This can be handled in the interceptor response side by next.handle(request).pipe(//to check response status). It will check the status code and redirect direct to the logout.

  @Injectable()
  export class ErrorInterceptor implements HttpInterceptor {
  constructor(private authenticationService: AuthenticationService) { }

  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
      return next.handle(request).pipe(catchError(err => {
      if ([401, 403].indexOf(err.status) !== -1) {
          <!-- auto logout if 401 Unauthorized or 403 Forbidden response returned from api -->
         this.authenticationService.logout();
         location.reload(true);
     }

     const error = err.error.message || err.statusText;
     return throwError(error);
  }))
 }
}


Prevent Cross Site Scripting:

Cross-site scripting (XSS) enables attackers to inject malicious code into web pages. Angular treats all values as untrusted by default. When a value is inserted into the DOM from a template, via property, attribute, style, class binding, or interpolation, Angular sanitizes and escapes untrusted values. Angular trusts template code, so generating templates, in particular templates containing user data, circumvents Angular's built-in protections.

Custom Domsanitizer to be implemented for sanitize by implementing the angular DOMSanitizer class. It also provides SecurityContext to indicate the type like html, style, script, urls.

  abstract class DomSanitizer implements Sanitizer {
   abstract sanitize(context: SecurityContext, value: string | SafeValue): string | null

  }

Reference:
Angular Security https://angular.io/guide/security

HTTP interceptors https://angular.io/api/common/http/HttpInterceptor

Security Context https://angular.io/api/core/SecurityContext

 

DevGroves Technologies

About author
DevGroves Technologies is a IT consulting and services start-up company which is predominately to web technologies catering to static website, workflow based CRM websites, e-commerce websites and reporting websites tailoring to the customer needs. We also support open source community by writing blogs about how, why and where it need to be used for.