Angular 6 Web API 2 Bearer Token Authentication add to header with HttpInterceptor

angular authentication and authorization http interceptor angular 6 angular 6 refresh token interceptor angular 6 http interceptor angular http interceptor add header Angular 6 HttpInterceptor and sending Authorization Token in header Angular 6 Auth token interceptor not adding headers Authorization bearer token Angular6 add multiple headers in Angular 6 HttpInterceptor angular 5 http interceptor example

Security is the main feature of any application, we will use in this article Web API 2 bearer token, created through Owin oAuth, which we created in our previous article. Pass Bearer token with every HttpRequest with the help of HttpInterceptor. In this article we will see only the authentication and guard the pages. We will implement refresh token in next article because might be you are here only to know how to refresh token and retry the failed request.

Create a new Angular CLI application, let it install all the dependencies and check whether it is working properly.

Add bootstrap 4 for designing the pages

npm install bootstrap@4.0.0

Open angular.json file and add the path for bootstrap css file:

"styles": [
  "./node_modules/bootstrap/dist/css/bootstrap.min.css",
  "src/styles.css"
],

Auth.Service.ts

Create a new folder "shared" inside the app folder to keep our services. Create new service in shared folder say auth.service.ts. Let's analyze what this auth service will have in it:

  1. a method login to call the service to validate credential and create token
  2. a method getUserDetail to get the user detail from sessionStorage
  3. a method logout to clean everything from sessionStorage and redirect user to the login page.
  4. a method to refresh the token when we will get 401 - unauthorized error
  5. a getter to check user is logged in or not isLoggedIn
  6. a getter to get the value of token getToken
  7. a getter to get the value of refresh token getRefreshToken
  8. a method manageSession to manage the token, refresh token and user detail on sessionStorage

We will also need some extra information say previous page Url so we can redirect to that page after successful login. A way to indicate other pages/components that user is logged in so time to refresh the menu and any other activity like start timer for idle timeout etc.

  • redirectUrl: string; to keep previous page url
  • loginStatus: EventEmitter<boolean> = new EventEmitter<boolean>(); so we can indicate login/logout status

So let's see everything one by one and then complete code together, first we see the constructor and login method

// Constructor
constructor(private httpClient: HttpClient,
    private router: Router) { }     

public login(params): Observable<LoggedinUser> {
    var data = `grant_type=password&username=${params.userName}&password=${params.password}`;
    return this.httpClient.post<LoggedinUser>(`${environment.webapiUrl}token`, data);
}

See, we are not subscribing to get the value, we will do it in login component. We use here environment.webapiUrl so you need to add the base path of your Web API Url in environment file.

Point 5, 6, 7 and 8, getters and manageSession method:

public get isLoggedIn() { return !!sessionStorage.getItem('token'); }  
public get getToken() { return sessionStorage.getItem('token'); }
public get getRefreshToken() { return sessionStorage.getItem('refresh'); }

public manageSession(data: LoggedinUser) {
    sessionStorage.setItem('token', data.access_token);
    sessionStorage.setItem('refresh', data.refresh_token);
    sessionStorage.setItem('user', JSON.stringify(data));
}

Nothing special which we need to describe, simple getter and setting value on sessionStorage for token, refresh token and user data. Here LoggedinUser is a an interface.

export interface LoggedinUser {
    access_token: string,
    userName: string,
    refresh_token: string
}

You can define it according to your need and data you are returning from your Web API.

Method to get the user detail:

public getUserDetail() {     
  if (sessionStorage.getItem('user')) {
    return JSON.parse(sessionStorage.getItem('user'));
  } else {
    return null;
  }
}

Log page to create bearer token

Logout method to remove the sessionStorage data. First we copy the current URL to the variable, so we can redirect to the page once user login again. remove some values from sessionStorage, redirect to the login page and emit (broadcast) that user is logged out, if you are listening it, do whatever you have to do like change menu etc. we will see latter with navbar component:

public logout(): void {
    this.redirectUrl = document.location.pathname;
    sessionStorage.removeItem('token');
    sessionStorage.removeItem('refresh');
    sessionStorage.removeItem('user');
    this.router.navigate(['/login']);
    this.loginStatus.emit(false);
}

Refresh token part will be covered in more detail in next article.

Here is the complete code together:

import { Injectable, EventEmitter } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Router } from '@angular/router';
import { Observable } from 'rxjs';
import { environment } from '../../environments/environment';
import { LoggedinUser } from './models';

@Injectable()
export class AuthService {

  redirectUrl: string;
  loginStatus: EventEmitter<boolean> = new EventEmitter<boolean>();

  constructor(private httpClient: HttpClient,
    private router: Router) { }

  public login(params): Observable<LoggedinUser> {
    var data = `grant_type=password&username=${params.userName}&password=${params.password}`;
    return this.httpClient.post<LoggedinUser>(`${environment.webapiUrl}token`, data);
  }

  public refreshToken() {
    /* Leave it for next article because I want to cover 
     * it in more detail and different scenarios which 
     * we need for real project */
  }
  public getUserDetail() {     
    if (sessionStorage.getItem('user')) {
      return JSON.parse(sessionStorage.getItem('user'));
    } else {
      return null;
    }
  }

  public get isLoggedIn() { return !!sessionStorage.getItem('token'); }  
  public get getToken() { return sessionStorage.getItem('token'); }
  public get getRefreshToken() { return sessionStorage.getItem('refresh'); }

  public manageSession(data: LoggedinUser) {
    sessionStorage.setItem('token', data.access_token);
    sessionStorage.setItem('refresh', data.refresh_token);
    sessionStorage.setItem('user', JSON.stringify(data));
  }

  public logout(): void {
    this.redirectUrl = document.location.pathname;
    sessionStorage.removeItem('token');
    sessionStorage.removeItem('refresh');
    sessionStorage.removeItem('user');
    this.router.navigate(['/login']);
    this.loginStatus.emit(false);
  }
}

Auth.Guard.ts

We completed our authentication service, now let's create the simplest file call auth guard which will protect the route to access if user will not have proper authorization whether it's a role or authentication. Create a new file inside the shared folder where we created our Auth Service and give a name auth.guard.ts and add following code in it:

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

@Injectable()
export class AuthGuard implements CanActivate {

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

  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
    let url: string = state.url;
    return this.checkLogin(state.url);
  }

  checkLogin(url: string): boolean {
    if (this.authService.isLoggedIn) { return true; }
    // To redirect to the page user is after login
    this.authService.redirectUrl = url;
    // move to login page
    this.router.navigate(['/login']);
    return false;
  }
}

Note we implemented CanActivate interface which will return boolean, whether user is logged in or not.

We used a method to check the user is logged in or not named checkLogin, if user is logged in return true otherwise set current url for future and move to login page and return true.

App-Routing.Module.ts

We completed our authentication service and authentication guard, time to create the routing for entire application, we are going to use normal routing rather than lazy loading, if you are interested about lazy loading see my article Angular 6 Lazy Loading with demo. Create a new module named AppRouting.Module.ts and add following code to protect our secured pages except the login page:

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

import { AuthGuard } from './shared/auth.guard';
import { LoginComponent } from './components/login/login.component';
import { NavbarComponent } from './components/navbar/navbar.component';
import { HomeComponent } from './components/home/home.component';
import { CustomerComponent } from './components/customer/customer.component';
import { ProductComponent } from './components/product/product.component';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { AppComponent } from './app.component';


const routes: Routes = [
  { path: 'login', component: LoginComponent},
  { path: 'customer', component: CustomerComponent, canActivate: [AuthGuard] },
  { path: 'product', component: ProductComponent, canActivate: [AuthGuard]  },
  { path: 'home', component: HomeComponent, canActivate: [AuthGuard]  },
  { path: '',   redirectTo: '/home', pathMatch: 'full'  }
]

@NgModule({
  imports: [
    CommonModule,
    FormsModule,
    ReactiveFormsModule,
    RouterModule.forRoot(routes)
  ],
  exports: [RouterModule],
  declarations: [
    AppComponent,
    LoginComponent,
    HomeComponent,
    CustomerComponent,
    ProductComponent,
    NavbarComponent
  ],
  providers: []
})
export class AppRoutingModule { }

Customer page and menu after login

Note we move all the common modules like CommonModule, FormsModule, ReactiveFormsModule etc. to the routing module.

We need to use this routing module to our app.module, let's update this file as well and use our above created app-routing module:

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

import { AuthService } from './shared/auth.service';
import { AuthGuard } from './shared/auth.guard';
import { AppComponent } from './app.component';
import { AppRoutingModule } from './app-routing.module';

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

Note we are importing our app-routing and both auth service and auth guard in providers.

Tokenized-Interceptor

To add authentication token with every HTTP request after login we will use an interceptor which is introduced after Angular 4.3.1, luckily we are using Angular 6 so we can benefit from this feature. Create a new file called Tokenized-Interceptor.ts on the root. We will implement HttpInterceptor in this class. Le't see how we can add a header with any request, create function in it with name addHeaders like this:

constructor(private authService: AuthService) { }
private setHeaders(request: HttpRequest<any>) {
    const token = this.authService.getToken;
    if (token) {
        request = request.clone({
            setHeaders: {
                'content-type': 'application/json', 
                 Authorization: `Bearer ${token}`
            }
         });
    }
    return request;
}

Note we are first getting the token with the help of Auth Service and check if user have any token only then add the header to the request.

Now we will implement the intercept method

intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    // Add authorization header if already logged in
    request = this.setHeaders(request);
    return next.handle(request);
}

After adding header, we call the next to execute the request. In next article we will add error handling to handle the 401-Unauthorized error to refresh the token and retry the request with new token.

I think it's better to add the error handling here which will reduce the code in components, change the above intercept method, let's see the complete interceptor code:

import { Injectable } from '@angular/core';
import {
  HttpRequest,
  HttpHandler,
  HttpEvent,
  HttpInterceptor,
  HttpErrorResponse
} from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError, mergeMap, finalize } from 'rxjs/operators';
import { AuthService } from './shared/auth.service';
import { AppResponse } from './shared/models';

@Injectable()
export class TokenizedInterceptor implements HttpInterceptor {

    constructor(private authService: AuthService) { }

    // Add authozization token to the request
    private setHeaders(request: HttpRequest<any>) {
        const token = this.authService.getToken;
        if (token) {
            request = request.clone({
                setHeaders: {
                    'content-type': 'application/json', 
                     Authorization: `Bearer ${token}`
                }
             });
        }
        return request;
    }

    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        request = this.setHeaders(request);
        return next.handle(request)
            .pipe(
            catchError((error: any) => {
                 if (error instanceof HttpErrorResponse) {
                    switch (error.status) {    
                        case 0: 
                            return throwError( {status: error.status, message: "Unable to connect to server, please contact admin" } as AppResponse );

                        case 400:
                        case 401:
                        case 403:
                        case 404:
                        case 500:
                        default:
                            return throwError({status: error.status, message: error.message } as AppResponse );
                    }
                } else if (error.error instanceof ErrorEvent) { // Client Side Error
                    return throwError({status: error.status, message: error.error.message } as AppResponse);
                } else {  // Server Side Error
                    return throwError({status: error.status, message: error.error.message } as AppResponse);
                }

             }),
             finalize(() => {
                // do something at the end
             })
          );
    }
}

If you can note, we are throwing AppResponse which is a custom interface to make easy to handle the errors, here is the interface:

export interface AppResponse {
    status: number;
    message: string;
    data: object;
}

Don't be panic, bear with me, I will provide the complete code for this error handling in next article.

Let's create some components, so we can use the routing, gaurd, navbar etc. We are creating a project which is entirely protected and will only be allowed to authenticated users, so let's create

  • Login
  • Home
  • Customer
  • Product
  • Navbar

You can add any content on home, customer and product page and call a service from Web API so we can test by passing the token and try to get the values, even you can return a string from the Web API, and add Authorize attribute to them.

Login.Component.ts

Create a new component named login. What features we are going to achieve, let's see as a list:

  • Textbox for user id - Required
  • Textbox for password - Required
  • Login Button to login
  • Show required error on submit if value not provided
  • Show error for invalid id/password
  • remove error once user focused to any textbox

We are going to use the reactive form, it is not very complicated so I am going to copy the complete TypeScript code of LoginComponent:

import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { AuthService } from '../shared/auth.service';
import { Router } from '@angular/router';
import { LoggedinUser } from '../shared/models';

@Component({
  selector: 'app-login',
  templateUrl: './login.component.html'
})
export class LoginComponent implements OnInit {
  frmLogin: FormGroup;
  isSubmitted = false;
  error: string;

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

  ngOnInit() {
    this.frmLogin = this.frmBuilder.group({
      userName: ['', Validators.required ],
      password: ['', Validators.required ]
    })
  }

  get frm() { return this.frmLogin.controls; }

  login() {
    this.isSubmitted = true;
    this.error = null;
    if (!this.frmLogin.valid) return;

    this.authService.login(this.frmLogin.value)    
      .subscribe((data: LoggedinUser) => {
          this.authService.manageSession(data);
          this.authService.loginStatus.emit(true);
          if (this.authService.redirectUrl) {
            this.router.navigate([this.authService.redirectUrl]);
          } else {
            this.router.navigate(['/']);
          }        
        },   (error: AppResponse) => {
             if(error.status === 400)
                   this.error = "Either user name or password is incorrect!";
              else
                   this.error = error.message;
       });
  }

  focused() { this.error = ''; }
}

Here we have frm getter to show the error, which you can see in HTML, so let's see the complete HTML for login page:

<form [formGroup]="frmLogin" (ngSubmit)="login()"
 class="form"  autocomplete="off"  novalidate="">
  <div class="form-group">
      <label for="uname1">Username</label>
      <span class="error float-right" 
            *ngIf="frm.userName.errors && (isSubmitted || frm.userName.dirty)">Required</span>
      <input type="text" id='userName'
        formControlName="userName" 
        (focus) = 'focused()'
        class="form-control form-control-lg rounded-0">
      <div class="invalid-feedback">Oops, you missed this one.</div>
  </div>
  <div class="form-group">
      <label>Password</label>
      <span class="error float-right" 
            *ngIf="frm.password.errors && (isSubmitted || frm.password.dirty)">Required</span>
      <input type="password" id='password'              
        formControlName="password"
        (focus) = 'focused()'
        class="form-control form-control-lg rounded-0" autocomplete="new-password">
      <div class="invalid-feedback">Enter your password too!</div>
  </div>
  <div class="row">
    <div class="col-12">
      <button type="submit"  id='btnLogin'
        class="btn btn-success btn-lg float-right">Login</button>
    </div>
  </div>
  <div class="row">
    <div class="col-12 error">{{error}}</div>
  </div>
</form>

I am not going to add the code for customer and product page, you can add the code in them and try to call any authorized method from Web API service and will see every time it will add a header to the request.

CORS error need to be fixed in Web API otherwise we will get error, so add following in you Web API Config file:

<httpProtocol>
  <customHeaders>
    <add name="Access-Control-Allow-Origin" value="*" />
    <add name="Access-Control-Allow-Headers" value="Content-Type,Authorization" />
    <add name="Access-Control-Allow-Methods" value="GET,POST,PUT,DELETE,OPTIONS" />
    <add name="Access-Control-Allow-Credentials" value="true" />
  </customHeaders>
</httpProtocol>

And comment following two lines:

<handlers>
  <remove name="ExtensionlessUrlHandler-Integrated-4.0" />
  <remove name="TRACEVerbHandler" />
  <!--<remove name="OPTIONSVerbHandler" />
  <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
  -->
</handlers>

These errors will occur only on development machine so you can remove them before moving to production server.

I don't thing you need the navbar code but let me add that might be it can help someone. Because we need to change the menu and consume the logout and emitter used in Authentication Service to indicate whether use is logged in or logged out. Create a new component called navbar and copy following code in it:

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

@Component({
  selector: 'app-navbar',
  templateUrl: './navbar.component.html'
})
export class NavbarComponent implements OnInit {
  user: any;
  constructor(private authService: AuthService) { }
  ngOnInit() {
    this.user = this.authService.getUserDetail();
    // Subscribe to listen changes of login status
    this.authService.loginStatus.subscribe(
      status => {
        if (status)
          this.user = this.authService.getUserDetail();
        else 
          this.user = null;
      }
    );
  }
  logout() {
    this.authService.logout();
  }
}

HTML Part of navbar:

<nav class="navbar navbar-expand-sm navbar-dark bg-dark">
  <div class="container">
      <a class="navbar-brand" [routerLink]="['/home']">Auth-Token</a>
      <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
        <span class="navbar-toggler-icon"></span>
      </button>

      <div class="collapse navbar-collapse" id="navbarSupportedContent">
        <ul class="navbar-nav mr-auto">
          <li class="nav-item" routerLinkActive="active" routerLinkActiveOptions="{exact: true}">
            <a class="nav-link" [routerLink]="['/home']">Home</a>
          </li>
          <li class="nav-item" routerLinkActive="active"  routerLinkActiveOptions="{exact: true}">
            <a class="nav-link" [routerLink]="['/customer']">Customer</a>
          </li>
        </ul>

        <ul class="navbar-nav float-right" *ngIf="!user">
            <li class="nav-item" routerLinkActive="active"  routerLinkActiveOptions="{exact: true}">
              <a class="nav-link" [routerLink]="['/register']">Register</a>
            </li>
            <li class="nav-item" routerLinkActive="active"  routerLinkActiveOptions="{exact: true}">
              <a class="nav-link" [routerLink]="['/login']">Login</a>
            </li>
        </ul>


        <ul class="navbar-nav float-right" *ngIf="user">
            <li class="nav-item" routerLinkActive="active"  routerLinkActiveOptions="{exact: true}">
              <a class="nav-link">Welcome {{user.userName}}</a>
            </li>
            <li class="nav-item" routerLinkActive="active"  routerLinkActiveOptions="{exact: true}">
              <a class="nav-link" (click)="logout()">Logout</a>
            </li>
        </ul>
      </div>
    </div>
</nav>

App.Component Complete code:

// HTML of App Component 
<app-navbar></app-navbar>
<div class="container">
    <router-outlet></router-outlet>
</div>  
// TypeScript of App Component
import { Component } from '@angular/core';
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html'
})
export class AppComponent {
  title = 'app';
}

That's it for the Authentication, Authorization and passing token to the Web API with the help of HttpInterceptor and handling basic errors.

Soon we will see the code for Refresh Token and how to handle the failed request after refreshing the token.

If you are looking for the code for this article, sure I will upload on Github after next article, adding the refresh token and retry pending request.

Ali Adravi Having 13+ years of experience in Microsoft Technologies (C#, ASP.Net, MVC and SQL Server). Worked with Metaoption LLC, for more than 9 years and still with the same company. Always ready to learn new technologies and tricks.
  • angular-6
  • HttpInterceptor
  • Bearer-token
  • authentication
  • Authorization
By Ali Adravi On 14 Oct, 18  Viewed: 300

Other blogs you may like

Global error handling in angular 6 with HttpClient & interceptor

Angular 4.3.1 introduced HttpInterceptor, we will use HttpClient and new HttpInterceptor feature to handle error globally for entire application. When calling the service method, we might get some kind of server error. It can be any kind, like poor network connection to reach to the server,... By Ali Adravi   On 10 Aug 2018  Viewed: 2,282

ASP.NET Web API 2, Owin, OAuth, Bearer Token, Refresh Token with custom database

Token base authentication expires over a fixed time, to overcome on it we need to use the refresh token. We will try to create the token as well as the refresh token after successful login, refresh token will be used to generate a new token if current token is already expired and it is not too... By Ali Adravi   On 08 Oct 2018  Viewed: 169

OAuth Web API 2 Bearer Token Role base authentication with custom database

Create Token with user credential & roles and authorize action methods based on role in Web API is the topic we will cover in this article. We would need to pass token in every request and decorate action methods with [Authorize(Roles = "Admin, Manager") etc. that's only the code we will need to... By Ali Adravi   On 04 Oct 2018  Viewed: 237

Integrate Facebook Authentication in ASP.Net5 Web App

Have you recently created a web application using Visual Studio 2015RC? Looking to integrate Facebook authentication to this web application built on ASP.Net5? Then you have hit the right keys and reached the right place. Here, you will use easy steps to integrate Facebook authentication. Now,... By Deepa Ranganathan   On 19 Jun 2015  Viewed: 318