HI WELCOME TO Sirees

Angular 8 Login and Logout with Web API Using Token Based Authentication

Leave a Comment
In this tutorial, we will discuss Angular 8 Login and Logout with Web API Using Token Based Authentication. This is a continuation to the previous article – User Registration in Angular 8 with Web API.

Content discussed :

Design Login Form in Angular 8 application.
Web API Token Based Authentication using OWIN and ASP.Net Identity.
Send Token From Angular HTTP Interceptor.
Here is the complete article list.

User Registration.
Login & Logout using Token.
Role Based Authorization.
GitHub link for the project project  : https://goo.gl/zWCW7U.

Following tools and modules are used for this project :

– Angular CLI
– Angular 8
– Materialize CSS (front end framework)
– VS Code & Visual Studio Editor

We assume that you have installed required packages and software for Angular 8 development.

Introduction
In previous article we have created angular 8 user registration form and inserted new users into Web API Project using ASP.NET Identity. Here we will update the angular 8 and Web API projects for token based user authentication.

Structure of Updated Angular 8 Project
Here is the final structure of Angular Application.

● src
+---● app
|   +--● user
|      |--user.component.ts|.html|.css
|      +--● sign-in
|      |  |--sign-in.component.ts|.html|.css
|      |
|      +--● sign-up
|      |  |--sign-up.component.ts|.html|.css
|      |
|      +--● shared
|         |--user.service.ts
|         |--user.model.ts
|
+---● home
|   |--home.component.ts|.html|.css
|
|---● routes.ts
|
+---● auth
|   |--auth.guard.ts
|   |--auth.interceptor.ts

● src
+---● app
|   +--● user
|      |--user.component.ts|.html|.css
|      +--● sign-in
|      |  |--sign-in.component.ts|.html|.css
|      |
|      +--● sign-up
|      |  |--sign-up.component.ts|.html|.css
|      |
|      +--● shared
|         |--user.service.ts
|         |--user.model.ts
|
+---● home
|   |--home.component.ts|.html|.css
|
|---● routes.ts
|
+---● auth
|   |--auth.guard.ts
|   |--auth.interceptor.ts

sign-up component is added in previous article, In this project user component will be the parent component for sign-in and sign-up component. Login form can created using sign-in component. home component will display dashboard for the application. After successful authentication, we opens home component for the user. Inside routes.ts file, we will configure routing for the application. Remaining auth  folder can discussed later.

So here we need 3 additional components – home,user and sign-in component. In-order to create these components execute following Angular CLI commands.

ng g c home
ng g c user
//from user component folder - user
ng g c sign-in

sign-up component is created in previous tutorial, as per the project design, we need to move the sign-up folder inside user folder. These components should be added to app.module.ts file as follows.

/src/app/app.module.ts
...
import { HomeComponent } from './home/home.component';
import { UserComponent } from './user/user.component';
import { SignInComponent } from './user/sign-in/sign-in.component';
import { SignUpComponent } from './user/sign-up/sign-up.component';
@NgModule({
   ...
   imports: [
   ...
   HomeComponent,
   UserComponent,
   SignInComponent,
   SignUpComponent
   ],
...

...
import { HomeComponent } from './home/home.component';
import { UserComponent } from './user/user.component';
import { SignInComponent } from './user/sign-in/sign-in.component';
import { SignUpComponent } from './user/sign-up/sign-up.component';
@NgModule({
   ...
   imports: [
   ...
   HomeComponent,
   UserComponent,
   SignInComponent,
   SignUpComponent
   ],
...
Because of sign-up component location change, we need to fix some broken import statement with User and UserService classes as follows.

/src/app/user/sign-up/sign-up.component.ts
import { User } from '../../shared/user.model';
import { UserService } from '../../shared/user.service';
...

Now let me update the global style sheet – styles.css with complete CSS rules for this application.

/src/styles.cssCSS
button.btn-submit{
    background-color: #38547b;
    color: #fff;
    width: 100%;
}
button.btn-submit:focus,button.btn-submit:hover{
    background-color: #38547b;
}

input.ng-invalid.ng-dirty{
    border-bottom-color : #e91e63 !important;
    box-shadow: 0 1px 0 0 #e91e63 !important;
}
/*for error div i*/
div.error-message i{
    vertical-align: middle !important;
}

/*for tab control*/
.tabs{
    height: 65px;
}
.tabs .tab{
    text-transform : none !important;
    line-height: 65px;
    height: 65px;
}
.tabs .tab a{
    font-size: 25px;
    color: grey !important;
}

.tabs .tab a.active{
    background-color: #fff !important;
}

button.btn-submit{
    background-color: #38547b;
    color: #fff;
    width: 100%;
}
button.btn-submit:focus,button.btn-submit:hover{
    background-color: #38547b;
}

input.ng-invalid.ng-dirty{
    border-bottom-color : #e91e63 !important;
    box-shadow: 0 1px 0 0 #e91e63 !important;
}
/*for error div i*/
div.error-message i{
    vertical-align: middle !important;
}

/*for tab control*/
.tabs{
    height: 65px;
}
.tabs .tab{
    text-transform : none !important;
    line-height: 65px;
    height: 65px;
}
.tabs .tab a{
    font-size: 25px;
    color: grey !important;
}

.tabs .tab a.active{
    background-color: #fff !important;
}
For this project, I will use Material Icons instead Font Awesome Icons. For that update index.html with icon style sheet reference.

/src/index.html
<head>
...
  <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
</head>
<body class="deep-purple lighten-5">
...
</body>

<head>
...
  <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
</head>
<body class="deep-purple lighten-5">
...
</body>
Inside the body tag we have classes from materialize framework to add some background color. Update app.component.html  as follows.

/src/app/app.component.html
<router-outlet></router-outlet>

This router-outlet tag will be replaced with current component html as per router configuration.

In sign-up component, user registration form is enclosed inside card div from Materialize CSS(designed in previous article). Now let’s remove the card div and we will just keep the user registration form inside the html as follows.

/src/app/user/sign-up/sign-up.component.html

<form class="col s12 white" #userRegistrationForm="ngForm" (ngSubmit)="OnSubmit(userRegistrationForm)">
  <div class="row">
    <div class="input-field col s6">
      <input class="validate" type="text" name="UserName" #UserName="ngModel" [(ngModel)]="user.UserName" required>
      <label data-error="Required field!">UserName</label>
    </div>
    <div class="input-field col s6">
      <input class="validate" type="password" name="Password" #Password="ngModel" [(ngModel)]="user.Password" required minlength="3">
      <label [attr.data-error]="Password.errors!=null?(Password.errors.required?'Required field!':'Minimum 3 characters needed'):''">Password</label>
    </div>
  </div>
  <div class="row">
    <div class="input-field col s12">
      <input class="validate" type="text" name="Email" #Email="ngModel" [(ngModel)]="user.Email" [pattern]="emailPattern">
      <label data-error="Invalid email!">Email</label>
    </div>
  </div>
  <div class="row">
    <div class="input-field col s6">
      <input type="text" name="FirstName" #FirstName="ngModel" [(ngModel)]="user.FirstName">
      <label>First Name</label>
    </div>
    <div class="input-field col s6">
      <input type="text" name="LastName" #LastName="ngModel" [(ngModel)]="user.LastName">
      <label>Last Name</label>
    </div>
  </div>
  <div class="row">
    <div class="input-field col s12">
      <button [disabled]="!userRegistrationForm.valid" class="btn-large btn-submit" type="submit">Submit</button>
    </div>
  </div>
</form>

<form class="col s12 white" #userRegistrationForm="ngForm" (ngSubmit)="OnSubmit(userRegistrationForm)">
  <div class="row">
    <div class="input-field col s6">
      <input class="validate" type="text" name="UserName" #UserName="ngModel" [(ngModel)]="user.UserName" required>
      <label data-error="Required field!">UserName</label>
    </div>
    <div class="input-field col s6">
      <input class="validate" type="password" name="Password" #Password="ngModel" [(ngModel)]="user.Password" required minlength="3">
      <label [attr.data-error]="Password.errors!=null?(Password.errors.required?'Required field!':'Minimum 3 characters needed'):''">Password</label>
    </div>
  </div>
  <div class="row">
    <div class="input-field col s12">
      <input class="validate" type="text" name="Email" #Email="ngModel" [(ngModel)]="user.Email" [pattern]="emailPattern">
      <label data-error="Invalid email!">Email</label>
    </div>
  </div>
  <div class="row">
    <div class="input-field col s6">
      <input type="text" name="FirstName" #FirstName="ngModel" [(ngModel)]="user.FirstName">
      <label>First Name</label>
    </div>
    <div class="input-field col s6">
      <input type="text" name="LastName" #LastName="ngModel" [(ngModel)]="user.LastName">
      <label>Last Name</label>
    </div>
  </div>
  <div class="row">
    <div class="input-field col s12">
      <button [disabled]="!userRegistrationForm.valid" class="btn-large btn-submit" type="submit">Submit</button>
    </div>
  </div>
</form>
Design Tab Control
First of all we have to design user component. Inside that we’ll add an outer div for both sign-in and sign-up components. For that I’ll add Materialize CSS card control with two tabs.

/src/app/user/user.component.html
<div class="container">
  <div class="row">
    <div class="col s8 offset-s2">
      <div class="card grey lighten-2">
        <div class="card-tabs">
          <ul class="tabs tabs-fixed-width tabs-transparent">
            <li class="tab">
              <a>Sign In</a>
            </li>
            <li class="tab">
              <a>Sign Up</a>
            </li>
          </ul>
        </div>
        <div class="card-content white">
          <div class="row">
            <router-outlet></router-outlet>
          </div>
        </div>
      </div>
    </div>
  </div>
</div>

<div class="container">
  <div class="row">
    <div class="col s8 offset-s2">
      <div class="card grey lighten-2">
        <div class="card-tabs">
          <ul class="tabs tabs-fixed-width tabs-transparent">
            <li class="tab">
              <a>Sign In</a>
            </li>
            <li class="tab">
              <a>Sign Up</a>
            </li>
          </ul>
        </div>
        <div class="card-content white">
          <div class="row">
            <router-outlet></router-outlet>
          </div>
        </div>
      </div>
    </div>
  </div>
</div>

router-outlet inside this component will be replaced by either sign-in or sign-up component.

Configure Application Route
First of all create new file – routes.ts. Then use following rout configurations.

/src/app/routes.ts

import { Routes } from '@angular/router'
import { HomeComponent } from './home/home.component';
import { UserComponent } from './user/user.component';
import { SignUpComponent } from './user/sign-up/sign-up.component';
import { SignInComponent } from './user/sign-in/sign-in.component';

export const appRoutes: Routes = [
    { path: 'home', component: HomeComponent },
    {
        path: 'signup', component: UserComponent,
        children: [{ path: '', component: SignUpComponent }]
    },
    {
        path: 'login', component: UserComponent,
        children: [{ path: '', component: SignInComponent }]
    },
    { path : '', redirectTo:'/login', pathMatch : 'full'}
 
];

import { Routes } from '@angular/router'
import { HomeComponent } from './home/home.component';
import { UserComponent } from './user/user.component';
import { SignUpComponent } from './user/sign-up/sign-up.component';
import { SignInComponent } from './user/sign-in/sign-in.component';

export const appRoutes: Routes = [
    { path: 'home', component: HomeComponent },
    {
        path: 'signup', component: UserComponent,
        children: [{ path: '', component: SignUpComponent }]
    },
    {
        path: 'login', component: UserComponent,
        children: [{ path: '', component: SignInComponent }]
    },
    { path : '', redirectTo:'/login', pathMatch : 'full'}
 
];

For sign-in and login path we have used one parent component (user component – for outer div) and inside the children we have Sign In and Sign Up component. With this file we have configured application routing, now we need to tell the application to follow these routes. Update app.module.ts file.

/src/app/app.module.ts
...
import { RouterModule } from '@angular/router'
import { appRoutes } from './routes';
@NgModule({
   ...
   imports: [
   ...
   RouterModule.forRoot(appRoutes)
   ],
...

Now we can use this router path in user component tab control.

/src/app/user/user.component.html
...

<ul class="tabs tabs-fixed-width tabs-transparent">
   <li class="tab">
     <a routerLink='/login' routerLinkActive='active'>Sign In</a>
   </li>
   <li class="tab">
     <a  routerLink='/signup' routerLinkActive='active'>Sign Up</a>
   </li>
</ul>

...

...
<ul class="tabs tabs-fixed-width tabs-transparent">
   <li class="tab">
     <a routerLink='/login' routerLinkActive='active'>Sign In</a>
   </li>
   <li class="tab">
     <a  routerLink='/signup' routerLinkActive='active'>Sign Up</a>
   </li>
</ul>

...

Here routerLinkActive is used to apply css class ‘active’, when we click on these anchor links.

Design Login Form in Angular 8
Now let’s design the login form, Open and update the sign-in component html file as follows.

/src/app/user/sign-in/sign-in.component.html
<form #loginForm="ngForm" class="col s12 white">
   <div class="row">
     <div class="input-field col s12">
       <i class="material-icons prefix">account_circle</i>
       <input type="text" #UserName ngModel name="UserName" placeholder="Username" required>
     </div>
   </div>
   <div class="row">
      <div class="input-field col s12">
        <i class="material-icons prefix">vpn_key</i>
        <input type="password" #Password ngModel name="Password" placeholder="Password" required>
      </div>
    </div>
    <div class="row">
        <div class="input-field col s12">
          <button [disabled]="!loginForm.valid" class="btn-large btn-submit" type="submit">Login</button>
        </div>
      </div>
</form>

<form #loginForm="ngForm" class="col s12 white">   <div class="row">
     <div class="input-field col s12">
       <i class="material-icons prefix">account_circle</i>
       <input type="text" #UserName ngModel name="UserName" placeholder="Username" required>
     </div>
   </div>
   <div class="row">
      <div class="input-field col s12">
        <i class="material-icons prefix">vpn_key</i>
        <input type="password" #Password ngModel name="Password" placeholder="Password" required>
      </div>
    </div>
    <div class="row">
        <div class="input-field col s12">
          <button [disabled]="!loginForm.valid" class="btn-large btn-submit" type="submit">Login</button>
        </div>
      </div>
</form>

Here we have a template driven form. In-order to work this form, make sure that FormsModule is added to app.module.ts file. Both username and password text boxes are mandatory fields and hence required attribute is added to the inputs fields. Now we have a login form like this.

Screen Shot Showing of Login Form Design



Token Based User Authentication in Web API
Now let’s update the Web API Project for Token Based Authentication. In-order to implement user authentication we need OWIN(Open Web Interface For .Net Applications). It act as a middle-ware between Asp.Net Application and IIS Server. First of all install required NuGet Packages.

In Solution explorer, Right Click On References. then click on Manage NuGet Packages. then do an online search for following packages and install them.

Microsoft ASP.NET Identity Owin
Microsoft.Owin.Host.SystemWeb
Microsoft.Owin.Cors
In-order to work with OWIN, we have to create OWIN Startup class. For that right click on Project > New Item,  then Search for OWIN Startup class. and I’ll name the file as Startup.cs. Now you can update the class as follows.

Startup.cs
public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        app.UseCors(CorsOptions.AllowAll);

        OAuthAuthorizationServerOptions option = new OAuthAuthorizationServerOptions
        {
            TokenEndpointPath = new PathString("/token"),
            Provider = new ApplicationOAuthProvider(),
            AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(60),
            AllowInsecureHttp = true
        };
        app.UseOAuthAuthorizationServer(option);
        app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
    }
}

public class Startup{
    public void Configuration(IAppBuilder app)
    {
        app.UseCors(CorsOptions.AllowAll);

        OAuthAuthorizationServerOptions option = new OAuthAuthorizationServerOptions
        {
            TokenEndpointPath = new PathString("/token"),
            Provider = new ApplicationOAuthProvider(),
            AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(60),
            AllowInsecureHttp = true
        };
        app.UseOAuthAuthorizationServer(option);
        app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
    }
}

Let’s discuss line by line.

app.UseCors  function will enable CORS(Cross Origin Resource Sharing) inside the application. In previous article, we already enabled CORS in WebApiConfig.cs file. Now you can comment that line.

After Enabling CORS, we have an object of OAuthAuthorizationServerOptions.  inside that we have following properties

TokenEndpointPath  – Specifies URL to Authenticate a user. In this Case we have to make HttpPost Request with username and password into http:localhost:portnumber/token URL. If given user credentials are valid, it will return an access token.
Provider – we set provider as ApplicationOAuthProvider class object. inside that we define how do we authenticate a user from database.
AccessTokenExpireTimeSpan  – access token from token request response will have an expire time. In this case it is 60 minutes/1 Hour.
AllowInsecureHttp – flag to secure token request from unsecured HTTP.
Let’s create the ApplicationOAuthProvider provider class to  validate username and password.

ApplicationOAuthProvider.cs
 public class ApplicationOAuthProvider : OAuthAuthorizationServerProvider
    {
        public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
        {
            context.Validated();
        }

        public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
        {
            var userStore = new UserStore<ApplicationUser>(new ApplicationDbContext());
            var manager = new UserManager<ApplicationUser>(userStore);
            var user = await manager.FindAsync(context.UserName,context.Password);
            if (user != null) {
                var identity = new ClaimsIdentity(context.Options.AuthenticationType);
                identity.AddClaim(new Claim("Username", user.UserName));
                identity.AddClaim(new Claim("Email", user.Email));
                identity.AddClaim(new Claim("FirstName", user.FirstName));
                identity.AddClaim(new Claim("LastName", user.LastName));
                identity.AddClaim(new Claim("LoggedOn", DateTime.Now.ToString()));
                context.Validated(identity);
            }
            else
                return;
        }
    }

 public class ApplicationOAuthProvider : OAuthAuthorizationServerProvider    {
        public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
        {
            context.Validated();
        }

        public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
        {
            var userStore = new UserStore<ApplicationUser>(new ApplicationDbContext());
            var manager = new UserManager<ApplicationUser>(userStore);
            var user = await manager.FindAsync(context.UserName,context.Password);
            if (user != null) {
                var identity = new ClaimsIdentity(context.Options.AuthenticationType);
                identity.AddClaim(new Claim("Username", user.UserName));
                identity.AddClaim(new Claim("Email", user.Email));
                identity.AddClaim(new Claim("FirstName", user.FirstName));
                identity.AddClaim(new Claim("LastName", user.LastName));
                identity.AddClaim(new Claim("LoggedOn", DateTime.Now.ToString()));
                context.Validated(identity);
            }
            else
                return;
        }
    }

ApplicationOAuthProvider is inherited from OAuthAuthorizationServerProvider class. Inside that we have overridden two functions.

ValidateClientAuthentication() – used to validate client device based on clientID and secret code. but in this project we’ll not authenticate client device.
GrantResourceOwnerCredentials() – authenticate a user with given username and password. If authentication is successful then we save user details as Claims Identity. It can be used to retrieve user details without DB interaction.
Now back to VS Code Editor. Let’s make token POST request on login form submission.

Token Request From Angular 5 Project
First of all create a function inside UserService class to make a token request as follows.

/src/app/shared/user.service.ts
...
userAuthentication(userName, password) {
    var data = "username=" + userName + "&password=" + password + "&grant_type=password";
    var reqHeader = new HttpHeaders({ 'Content-Type': 'application/x-www-urlencoded','No-Auth':'True' });
    return this.http.post(this.rootUrl + '/token', data, { headers: reqHeader });
  }


Along with userName and password you have to pass grant_type as password. With HttpHeader, we have set Content-Type as application/x-www-urlencoded. Additional No-Auth property is set to True. we can discuss need of this No-Auth property later.
Now open sign-in component html and then wire up a submit event

/src/app/user/sign-in/sign-in.component.html
<form #loginForm="ngForm" class="col s12 white"
  (ngSubmit)="OnSubmit(UserName.value,Password.value)" >
   ...
</form>

No let’s define the function inside the component typescript file.

/src/app/user/sign-in/sign-in.component.ts

import { Component, OnInit } from '@angular/core';
import { UserService } from '../../shared/user.service';
import { HttpErrorResponse } from '@angular/common/http';
import { Router } from '@angular/router';

@Component({
  selector: 'app-sign-in',
  templateUrl: './sign-in.component.html',
  styleUrls: ['./sign-in.component.css']
})
export class SignInComponent implements OnInit {
  isLoginError : boolean = false;
  constructor(private userService : UserService,private router : Router) { }

  ngOnInit() {
  }

  OnSubmit(userName,password){
     this.userService.userAuthentication(userName,password).subscribe((data : any)=>{
      localStorage.setItem('userToken',data.access_token);
      this.router.navigate(['/home']);
    },
    (err : HttpErrorResponse)=>{
      this.isLoginError = true;
    });
  }

}

import { Component, OnInit } from '@angular/core';import { UserService } from '../../shared/user.service';
import { HttpErrorResponse } from '@angular/common/http';
import { Router } from '@angular/router';

@Component({
  selector: 'app-sign-in',
  templateUrl: './sign-in.component.html',
  styleUrls: ['./sign-in.component.css']
})
export class SignInComponent implements OnInit {
  isLoginError : boolean = false;
  constructor(private userService : UserService,private router : Router) { }

  ngOnInit() {
  }

  OnSubmit(userName,password){
     this.userService.userAuthentication(userName,password).subscribe((data : any)=>{
      localStorage.setItem('userToken',data.access_token);
      this.router.navigate(['/home']);
    },
    (err : HttpErrorResponse)=>{
      this.isLoginError = true;
    });
  }

}
Inside the OnSubmit() function, you have two arrow function, for success and error response. if user authentication is successful, we save the accessToken in localStorage (in web browser). because we need this token to authenticate the user in this angular 5. Finally we will navigate the user to home component.

If authentication is fails, isLogin is set to true, based on this property we will show an error message just above the login form.

/src/app/user/sign-in/sign-in.component.html
<div *ngIf="isLoginError" class="red-text center error-message">
    <i class="material-icons">error</i> Incorrect username or password
</div>
<form #loginForm="ngForm" class="col s12 white"
  (ngSubmit)="OnSubmit(UserName.value,Password.value)" >
   ...
</form>

Angular 5 User Authentication Using Token
Now let’s implement user authentication in this angular 5 project. First of all, I will create a new folder auth inside the app folder. Now lets create guard class for user authentication.

//from auth folder
ng g g auth

So it will create auth.guard.ts file, inside that we’ll modify the canActivate() function as follows.

/src/app/auth/auth.guard.ts

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

@Injectable()
export class AuthGuard implements CanActivate {
  constructor(private router : Router){}
  canActivate(
    next: ActivatedRouteSnapshot,
    state: RouterStateSnapshot):  boolean {
      if (localStorage.getItem('userToken') != null)
      return true;
      this.router.navigate(['/login']);
      return false;
  }
}

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

@Injectable()
export class AuthGuard implements CanActivate {
  constructor(private router : Router){}
  canActivate(
    next: ActivatedRouteSnapshot,
    state: RouterStateSnapshot):  boolean {
      if (localStorage.getItem('userToken') != null)
      return true;
      this.router.navigate(['/login']);
      return false;
  }
}

with canActivate() function we will check whether an user is authenticated or not using localStorage – userToken.
if not not authenticate we will send him to login form. in-order to work this guard we have to do some additional work.

open app.module.ts file, then add AuthGuard class inside providers array.

/src/app/app.module.ts
...
import { AuthGuard } from './auth/auth.guard';
...
providers: [..AuthGuard],
....

Now update routes.ts file by adding guard class to routes which is to be authenticated.
/src/app/routes.ts
...
import { AuthGuard } from './auth/auth.guard';
...
export const appRoutes: Routes = [
    { path: 'home', component: HomeComponent,canActivate:[AuthGuard] },
....

In this case, home route must authenticated so we have added canActivate array with AuthGuard class.
Consume Web API Methods with Authorization
To consume Web API method with Authorize Attribute, we need to send access token along with the request. To demonstrate this process, let me add a Web API method with Authorize attribute.

Inside the Web API project, I’ll add a new Web API method inside AccountController as follows.

AccountController
...

[HttpGet]
[Authorize]
[Route("api/GetUserClaims")]
public AccountModel GetUserClaims()
{
    var identityClaims = (ClaimsIdentity)User.Identity;
    IEnumerable<Claim> claims = identityClaims.Claims;
    AccountModel model = new AccountModel()
    {
        UserName = identityClaims.FindFirst("Username").Value,
        Email = identityClaims.FindFirst("Email").Value,
        FirstName = identityClaims.FindFirst("FirstName").Value,
        LastName = identityClaims.FindFirst("LastName").Value,
        LoggedOn = identityClaims.FindFirst("LoggedOn").Value
    };
    return model;
}

This method returns few user claims saved during user authentication. Now let’s call this Web API from Home Component. First of all update the home.component.html as follows.

/src/app/home/home.component.html

<nav>
  <div class="nav-wrapper">
    <a href="#" class="brand-logo center">
      <i class="material-icons">cloud</i>Dotnet Mob App</a>
    <ul id="nav-mobile" class="right hide-on-med-and-down">
      <li>
        <a (click)="Logout()">Logout</a>
      </li>
    </ul>
  </div>
</nav>
<div class="row" *ngIf="userClaims">
  <div class="col s12 m7">
    <div class="card">
      <div class="card-content">
        <span>Username :{{userClaims.UserName}}</span>
        <br>
        <span>Email : {{userClaims.Email}}</span>
        <br>
        <span>Full Name : {{userClaims.FirstName}} {{userClaims.LastName}}</span>
        <br>
        <span>Logged On : {{userClaims.LoggedOn}}</span>
      </div>
    </div>
  </div>
</div>


Update the component typescript file.

/src/app/home/home.component.ts

import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { UserService } from '../shared/user.service';

@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.css']
})
export class HomeComponent implements OnInit {
  userClaims: any;

  constructor(private router: Router, private userService: UserService) { }

  ngOnInit() {
    this.userService.getUserClaims().subscribe((data: any) => {
      this.userClaims = data;

    });
  }

  Logout() {
    localStorage.removeItem('userToken');
    this.router.navigate(['/login']);
  }
}


import { Component, OnInit } from '@angular/core';import { Router } from '@angular/router';
import { UserService } from '../shared/user.service';

@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.css']
})
export class HomeComponent implements OnInit {
  userClaims: any;

  constructor(private router: Router, private userService: UserService) { }

  ngOnInit() {
    this.userService.getUserClaims().subscribe((data: any) => {
      this.userClaims = data;

    });
  }

  Logout() {
    localStorage.removeItem('userToken');
    this.router.navigate(['/login']);
  }
}
Now let’s define the getUserClaims() function inside the UserService class.

/src/app/shared/user.service.ts
...
getUserClaims(){
   return  this.http.get(this.rootUrl+'/api/GetUserClaims');
  }

So inside the home component ngOnInit Lifecyle-Hook, we consumed the GetUserClaims method and we have shown the result in home component html.
Here we didn’t append access token in the request, we can discuss that in a bit. We have a logout button in home component top Navbar. Logout() function is wired up to the logout button click event.

HTTP Interceptor in Angular 5
In UserService class function getUserClaims(), To consume the Web API method we have not appended the accessToken. It would be difficult to append accessToken to every Web API call and we have to handle the 401 UnAuthorized Response in each of them( if access token expires).

To do this task, we have a common place –  HTTP Interceptor. For that I will create auth.interceptor.ts file in auth folder as follows.

/src/app/auth/auth.interceptor.ts
import { HttpInterceptor, HttpRequest, HttpHandler, HttpUserEvent, HttpEvent } from "@angular/common/http";
import { Observable } from "rxjs/Observable";
import { UserService } from "../shared/user.service";
import 'rxjs/add/operator/do';
import { Injectable } from "@angular/core";
import { Router } from "@angular/router";

@Injectable()
export class AuthInterceptor implements HttpInterceptor {

    constructor(private router: Router) { }

    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        if (req.headers.get('No-Auth') == "True")
            return next.handle(req.clone());

        if (localStorage.getItem('userToken') != null) {
            const clonedreq = req.clone({
                headers: req.headers.set("Authorization", "Bearer " + localStorage.getItem('userToken'))
            });
            return next.handle(clonedreq)
                .do(
                succ => { },
                err => {
                    if (err.status === 401)
                        this.router.navigateByUrl('/login');
                }
                );
        }
        else {
            this.router.navigateByUrl('/login');
        }
    }
}

import { HttpInterceptor, HttpRequest, HttpHandler, HttpUserEvent, HttpEvent } from "@angular/common/http";import { Observable } from "rxjs/Observable";
import { UserService } from "../shared/user.service";
import 'rxjs/add/operator/do';
import { Injectable } from "@angular/core";
import { Router } from "@angular/router";

@Injectable()
export class AuthInterceptor implements HttpInterceptor {

    constructor(private router: Router) { }

    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        if (req.headers.get('No-Auth') == "True")
            return next.handle(req.clone());

        if (localStorage.getItem('userToken') != null) {
            const clonedreq = req.clone({
                headers: req.headers.set("Authorization", "Bearer " + localStorage.getItem('userToken'))
            });
            return next.handle(clonedreq)
                .do(
                succ => { },
                err => {
                    if (err.status === 401)
                        this.router.navigateByUrl('/login');
                }
                );
        }
        else {
            this.router.navigateByUrl('/login');
        }
    }
}
Here AuthInterceptor class is inherited from HttpInterceptor class.

With the first IF statement, we will check the condition No-Auth is True/False. if it is true we don’t need to append the access token. So you can set No-Auth as true for web api calls which does not need authorization.

Then we check whether the user is authenticated or not. if authenticated then we will append the access token in request header. we must use the prefix Bearer while passing Authorization Access Token in request header. Inside the error function, we handle 401 Unauthorized Status Code – most often it can appear due to token expiration.

That’s it. Let me know your doubts and feedback in the comment box below. In the next part, we will discuss Role Based Authorization in Angular 5 with Web API.

0 comments:

Post a Comment

Note: only a member of this blog may post a comment.