Angular 2-5 JWT Authentication: Using Http Client, Http Interceptors and Guards

Application built with Angular 2-5 to see how to implement Authentication using JWT with the help of HttpClient, HttpInterceptors ,Guards and Spring Boot backend...

Welcome back for another article, this time we will focus on a frontend application built with Angular 5 to see how to implement Authentication using JWT with the help of HttpClient, Http Interceptors and Guards for protecting routes. So let’s get started.


What is an HTTP Interceptor?

Let’s see what are HTTP Interceptors and why are they useful?
As per a dictionary the word interceptor means anything that intercepts something.
Now from an Angular view, Interceptors allows us to pre-process and post-process a client request before sending and after getting the response from the server. Interceptors can be used in two different phases in a life cycle of an HTTP request to the server:
1.  Before sending the request: At this phase the configuration of the request can be modified, for example we can add an Access Token to be sent to the server with each HTTP request so it can be validated at server end.
2.  After receiving the response from server: This happens when we receive the response from the server. At this stage we can use it to alter the response coming from server before it is being passed to the code block from where the HTTP call was initiated. The most common use case for this is to handle Global Errors across the app.
According to Angular Team at Google “When your application makes a request, interceptors transform it before sending it to the server, and the interceptors can transform the response on its way back before your application sees it.”
In our case we will use the HTTP Interceptors to automatically attach authentication information to requests, specifically our JSON Web Token as an Authorization header with the Bearer scheme.

What is an Angular Route Guards?

Angular route guards are interfaces which allow or deny the access to a specific requested route. They make this decision based on the Boolean value returned by an overridden function defined in a given guard interface.
There are five different guard types so we can choose depending on what we want to do:

  • CanActivate
  • CanActivateChild
  • CanDeactivate
  • CanLoad
  • Resolve

Without going too much into details of each guard in this post, you can check the Angular docs for more clarification.

Now we are ready to start our journey for implementing the JWT Authentication using Angular’s HttpInterceptor to request resources from the server and Guards to protect our routes.


Project Folder Structure

Angular jwt authentication

Create an Authentication service

When integrating authentication in an angular app, the best approach is to create a dedicated service that holds everything you need. Every authentication service should have at least some basic methods for allowing users to log in and log out. It must also include a method that retrieves a JSON Web Token from it's dedicated storage on the client side and a way to check the validity of this token to tell if the user is authenticated or not.
One way we can check whether a JWT is expired is to use angular2-jwt to return a boolean after checking the exp claim.
Install it using: npm i --save @auth0/angular-jwt@1.2.0

When it finishes installing import it within your authentication class service and instantiate the JwtHelperService class.

First let’s create our Authentication service using:
ng g service security/services/auth --module=app


// src/app/security/services/auth.service.ts

import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders, HttpResponse, HttpErrorResponse } from '@angular/common/http';

import { Observable } from 'rxjs/Observable';
import { map, catchError } from 'rxjs/operators';
import { ErrorObservable } from 'rxjs/observable/ErrorObservable';

import { JwtHelperService } from '@auth0/angular-jwt';

const jwtHelper = new JwtHelperService();

@Injectable()
export class AuthService {

  private baseUrl: string = 'http://localhost:8080/api/';
  private loginUrl: string = this.baseUrl + 'open/login';
  private headers: HttpHeaders = new HttpHeaders();

  constructor(private http: HttpClient) {
    this.headers = this.headers.set("Content-Type", "application/json");
    this.headers = this.headers.set('Accept', 'application/json');
  }

  login(username: string, password: string): Observable<boolean> {

    return this.http.post(this.loginUrl, {username: username, password: password}, {headers: this.headers, observe: 'response'})
      .pipe(
        map((response: HttpResponse<any>) => {
            // login successful if there's a jwt token in the response
            let token = response.body.token;
            let refreshToken = response.body.refreshToken;

            if (token) {
                // store username and jwt token in local storage to keep user logged in between page refreshes
                localStorage.setItem('loggedUser', JSON.stringify({ username: username, token: token, refreshToken: refreshToken }));

                // return true to indicate successful login
                return true;
            } else {
                // return false to indicate failed login
                return false;
            }
        }),
        catchError(this.handleError)
      );
  }

  logout(): void {
    // clear token remove user from local storage to log user out
    localStorage.removeItem('loggedUser');
  }

  getToken(): string {
    let loggedUser = JSON.parse(localStorage.getItem('loggedUser'));
    let token = loggedUser && loggedUser.token;
    return token ? token : "";
  }

  isAuthenticated(): boolean {
    const token = this.getToken();
    // Check whether the token is expired or not
    // return true or false
    return token != "" ? !jwtHelper.isTokenExpired(token) : false;
  }

  private handleError(error: HttpErrorResponse) {
    if (error.error instanceof ErrorEvent) {
      // A client-side or network error occurred. Handle it accordingly.
      console.error('An error occurred:', error.error.message);
    } else {
      // The backend returned an unsuccessful response code.
      // The response body may contain clues as to what went wrong,
      console.error(
        `Backend returned code ${error.status}, ` +
        `body was: ${error.error}`);
    }
    // return an ErrorObservable with a user-facing error message
    return new ErrorObservable(error);
  }
}

All Methods created inside our service are self-explanatory. The getToken method retrieves the JWT from the localStorage and isAuthenticated method allow us to check if the token is still valid or not based on expiration date.

It is important to note here that we make use of the new Angular’s HttpClient from @angular/common/http to submit requests to the server and not the old Http class from @angular/http. Otherwise, Angular’s Interceptors simply won’t work and we will not be able to attach our JWT to each outgoing request.

Create an Interceptor

To create an Interceptor we need to create a service class which implements HttpInterceptor.


//src/app/security/interceptors/token.interceptor.ts
import { Injectable, Injector } from '@angular/core';
import { Router} from '@angular/router'
import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest, HttpErrorResponse } from '@angular/common/http';

import { Observable } from 'rxjs/Observable';
import { ErrorObservable } from 'rxjs/observable/ErrorObservable';
import { catchError } from 'rxjs/operators';

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

@Injectable()
export class TokenInterceptor implements HttpInterceptor {

  constructor(private inj: Injector) { }

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {

    let authService: AuthService = this.inj.get(AuthService); //authservice is an angular service

    console.log("intercepted request ... ");
    
    const authToken: string = authService.getToken();

    // cloned headers, updated with the authorization header.
    const authReq = req.clone({ setHeaders: {'Authorization': `Bearer ${authToken}`}  });

    // send cloned request with header to the next handler.
    return next.handle(authReq)
    .pipe(
        catchError((error: HttpErrorResponse) => {
            let router = this.inj.get(Router);
            console.log("Interceptor error ... "+ JSON.stringify(error));
            if (error.status === 401) {
                console.log("Interceptor code 401 ... ");
                //logout users, redirect to login page
                authService.logout();
                //redirect to the signin page or show login modal here
                router.navigate(['account/login']); 
                return new ErrorObservable(error);
            }
    
            return new ErrorObservable(error);
                
        })
    );
  }
}

Angular HttpInterceptor provides intercept() method with HttpRequest and HttpHandler parameters to intercept outgoing requests. As you can see, we can’t tamper with the original request. To be able to modify it we need to clone it first then add the headers we want, in our case we added an Autorization header with Bearer authentication scheme followed by the JSON Web Token (JWT) retrieved from local storage using the getToken method belonging to the AuthService.

Calling next.handle means that we are dispaching the request to the next interceptor in the chain, if there is one.

Configure Interceptor with Provider

To maintain the order between interceptors if there is many, it is better to create a const array in the index.ts file that will hold the configuration for adding the Interceptor to the Interceptors array as following:


// src/app/security/interceptors/index.ts

import { HTTP_INTERCEPTORS } from '@angular/common/http';

import { TokenInterceptor } from './token.interceptor';

export const httpInterceptorProviders = [
  { provide: HTTP_INTERCEPTORS, useClass: TokenInterceptor, multi: true },
];

Now add it to the providers attribute in the application’s module.


// src/app/app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { RouterModule } from '@angular/router';
import { NgModule } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';

import { AppRoutingModule } from './app-routing.module';
import { AccountModule } from './account/account.module';

import { AppComponent } from './app.component';
import { AuthService } from './security/services/auth.service';
import { httpInterceptorProviders } from './security/interceptors';
import { AuthGuard } from './security/guards/auth.guard';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    RouterModule,
    AccountModule,
    AppRoutingModule,
    HttpClientModule
  ],
  providers: [AuthService, httpInterceptorProviders, AuthGuard],
  bootstrap: [AppComponent]
})
export class AppModule { }

Now everytime we send an HTTP request, the user’s token will be added by the interceptor automatically.

Let's Test it!

To test this let’s create our account module with nested login and profile components using the following commands:


  • ng g module account –routing
  • ng g c account/account --module=account/account --flat
  • ng g c account/login --module=account/account
  • ng g c account/profile --module=account/account

The login component will create our login form using Angular's Reactive forms to authenticate the user by providing his credentials.


import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, FormControl, Validators } from '@angular/forms';
import { Router } from '@angular/router';

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

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

  loginForm: FormGroup;

  constructor(private router: Router, private fb: FormBuilder, private authService: AuthService) { 
    this.createLoginForm();
  }

  ngOnInit() {
    
  }

  createLoginForm() {
    this.loginForm = this.fb.group({
      'username': [' ', Validators.required],
      'password': [' ', Validators.required]
    })
  }

  login() {
    let credentials = this.loginForm.value;
    console.log(credentials);
    
    this.authService.login(credentials.username, credentials.password)
    .subscribe(() => {
      this.router.navigate(['account/profile']);
    },
    err => console.log("Error while Authenticating"));
    
  }

}

The HTML template:


<div class="container">
    <div class="row">
        <div class="col-md-6 offset-md-3">
            <form [formGroup]="loginForm" (ngSubmit)="login()">
                <div class="form-group">
                    <label for="username">Username</label>
                    <input formControlName="username" 
                        type="text" class="form-control form-control-lg" 
                        id="username" placeholder="Enter Username"
                        required>
                    <div *ngIf="loginForm.controls['username'].errors && !loginForm.controls['username'].pristine" class="error-msg">
                        <div [hidden]="!loginForm.controls['password'].errors.required">Username is required.</div>
                    </div>
                </div>
                <div class="form-group">
                    <label for="password">Password</label>
                    <input formControlName="password" type="password" 
                        class="form-control form-control-lg" 
                        id="password" placeholder="Enter Password"
                        required>
                    <div *ngIf="loginForm.controls['password'].errors && !loginForm.controls['password'].pristine" class="error-msg">
                        <div [hidden]="!loginForm.controls['password'].errors.required">Password is required.</div>
                    </div>
                </div>
        
                <button type="submit" class="btn btn-lg btn-primary pull-xs-right" [disabled]="loginForm.invalid">Submit</button>
            </form>
        </div>
    </div>
</div>

The profile component will be protected by an Angular guard that will allow the access if the user is authenticated, otherwise he will be redirected to the login form.


// src/app/account/profile/profile.component.ts
import { Component, OnInit } from '@angular/core';

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

  constructor() { }

  ngOnInit() {
  }

}

The HTML template:


<!-- src/app/account/profile/profile.component.html-->
<p>
  I'm profile Component, only Authenticated users can see me.
</p>

Now, let’s modify the AccountRoutingModule as bellow:


import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

import { AccountComponent } from './account.component';
import { LoginComponent } from './login/login.component';
import { ProfileComponent } from './profile/profile.component';

import { AuthGuard } from '../security/guards/auth.guard';

const routes: Routes = [{
  path: 'account', component: AccountComponent, children: [
    {
      path: '',
      redirectTo: 'login',
      pathMatch: 'full'
    },
    {
      path: 'login',
      component: LoginComponent
    }, {
      path: 'profile',
      component: ProfileComponent,
      canActivate: [AuthGuard]
    }
  ],
}];

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

As you can see here we use the AuthGuard to protect the route of Profile component.

Create the AuthGuard

Use the command ng g guard security/auth --module=app to generate the AuthGard and replace it's content with:


// src/app/security/guards/auth.guard.ts

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

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

@Injectable()
export class AuthGuard implements CanActivate {

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

  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
    if (this.authService.isAuthenticated()) {
        // logged in so return true
        return true;
    }

    // not logged in so redirect to login page with the return url and return false
    this.router.navigate(['account/login']);
    return false;
  }
}

For the Spring Boot part you will find it here to test with a backend: JWT with Spring Boot

Conclusion

To conclude, The HttpInterceptor Interface was one of the most useful improvements the Angular Team has introduced recently, which allows us to modify outgoing requests and incoming responses easily.

That’s all for this tutorial, I hope you have learned something today and stay tuned for other tutorials. If you have any questions or suggestions feel free to comment below. The full implementation of this tutorial can be found on GitHub

Name

Angular,7,Angular 8,1,Best Practices,1,Design,1,Firebase,1,Ionic,1,Java,5,Nodejs,2,Python,1,Restful API,1,Software Development,1,Spring,3,Spring Batch,1,Spring Boot 2,1,Web Development,1,
ltr
item
Programming Tutorials, News and Reviews: Angular 2-5 JWT Authentication: Using Http Client, Http Interceptors and Guards
Angular 2-5 JWT Authentication: Using Http Client, Http Interceptors and Guards
Application built with Angular 2-5 to see how to implement Authentication using JWT with the help of HttpClient, HttpInterceptors ,Guards and Spring Boot backend...
https://2.bp.blogspot.com/-ffcTzHEI220/W6FcplzuEdI/AAAAAAAAAMU/7CDjFDMpdEQ1U8Sle9f1uySbWGqjyw7bACLcBGAs/s320/folder%2Bstructure.png
https://2.bp.blogspot.com/-ffcTzHEI220/W6FcplzuEdI/AAAAAAAAAMU/7CDjFDMpdEQ1U8Sle9f1uySbWGqjyw7bACLcBGAs/s72-c/folder%2Bstructure.png
Programming Tutorials, News and Reviews
https://www.ninjadevcorner.com/2018/09/angular2-5-jwt-authentication-using-http-client-http-interceptors-and-guards.html
https://www.ninjadevcorner.com/
https://www.ninjadevcorner.com/
https://www.ninjadevcorner.com/2018/09/angular2-5-jwt-authentication-using-http-client-http-interceptors-and-guards.html
true
493653397416713395
UTF-8
Loaded All Posts Not found any posts VIEW ALL Readmore Reply Cancel reply Delete By Home PAGES POSTS View All RECOMMENDED FOR YOU LABEL ARCHIVE SEARCH ALL POSTS Not found any post match with your request Back Home Sunday Monday Tuesday Wednesday Thursday Friday Saturday Sun Mon Tue Wed Thu Fri Sat January February March April May June July August September October November December Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec just now 1 minute ago $$1$$ minutes ago 1 hour ago $$1$$ hours ago Yesterday $$1$$ days ago $$1$$ weeks ago more than 5 weeks ago Followers Follow THIS CONTENT IS PREMIUM Please share to unlock Copy All Code Select All Code All codes were copied to your clipboard Can not copy the codes / texts, please press [CTRL]+[C] (or CMD+C with Mac) to copy