Authenticating Angular 8 Client Application with JSON WEB Token (JWT) Based Authentication
In my previous article we have seen the Token based authentication using ASP.NET Core 3.1. In continuation of that post, in this post we will demonstrate the Authentication of the Angular application. Angular is one of the most preferred framework for modern front-end applications. Angular uses the http module from @angular/common package to make HTTP Calls to external REST APIs. Angular uses HttpInterceptor interface. This interface intercepts and handles Httprequest and HttpResponse. The interceptor transforms the outgoing request by adding addition information into the HTTP headers e.g. Token information.
To authenticate the client application using JSON Web Token we need to register new users using the authentication service and then authenticate the user to receive JSON Web Token from the server side application. The client application uses this token to make requests to the server and the server authenticate the requests from the client application based on this token.
The following figure explains the picture of the token based authentication from Angular application
Figure 1: The JSON Web Token Authentication from Angular Application
The following figure explains the picture of the token based authentication from Angular application
Figure 1: The JSON Web Token Authentication from Angular Application
The above figure shows the Angular Client application and ASP.NET Core 3.1 server side application. Following points explains the behavior of the application (Note that the following numbering applies to the numbers used on the figure.)
In my previous article the JSON Web Token authentication is already explained. You can download the server side application code and use it.
Implementing the Angular Client application.
The client application is implemented using Microsoft Visual Studio Code (VSCode). You can download VSCode from this link. You also need the Node.js. This can be downloaded from this link.
Step 1: Open the command prompt and run the following command to install Angular CLI.
npm install -g @angular/cli
Step 2: To create a new Angular application run the following command
ng new ngclient
This will create a new Angular project will all dependencies.
Step 3: Open the ngclient folder in the VS Code. In the src folder of the project add a new folder of name secure-call. In this folder add a new file and name it as secure-call.models.ts. In this file add classes as shown in the following listing
export class RegisterUser {
constructor(
public Email: string,
public Password: string,
public ConfirmPassword: string
) {}
}
export class LoginUser {
constructor(public UserName: string, public Password: string) {}
}
export class Product {
constructor(
public productRowId: number,
public productName: string,
public price
) {}
}
export class ResponseData {
constructor(public message: string) {}
}
Listing 1: The Model classes for Registering Users, LoginUser, ResponseData and Product
The RegisterUser class is used to define properties to register new user with the application. The LoginUser class will be used to define properties for user login. The ResponseData class define property to receive response message from the ASP.NET Core Web API. The Product class define properties for storing data from the Web API.
Step 4: In the secure-call folder add a new file and name it as secure-call.service.ts. In this file add the code as shown in the following listing
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import {
RegisterUser,
LoginUser,
Product,
ResponseData
} from './secure-call.models';
import { Observable } from 'rxjs';
@Injectable({ providedIn: 'root' })
export class SecureCallService {
url: string;
constructor(private httpClient: HttpClient) {
this.url = 'http://localhost:5000';
}
// method to register the user
registerUser(user: RegisterUser): Observable<ResponseData> {
let userRegistered: Observable<ResponseData>;
const options = {
headers: new HttpHeaders({
'Content-Type': 'application/json'
})
};
userRegistered = this.httpClient.post<ResponseData>(
`${this.url}/api/Auth/Register`,
user,
options
);
return userRegistered;
}
// method to login the user the user
authenticateUser(user: LoginUser): Observable<ResponseData> {
let userAuthenticated: Observable<ResponseData>;
const options = {
headers: new HttpHeaders({
'Content-Type': 'application/json'
})
};
userAuthenticated = this.httpClient.post<ResponseData>(
`${this.url}/api/Auth/Login`,
user,
options
);
return userAuthenticated;
}
// method to get products
getProducts(token: string): Observable<Product[]> {
let products: Observable<Product[]>;
const headerValues = new HttpHeaders();
products = this.httpClient.get<Product[]>(`${this.url}/api/Products`);
return products;
}
}
Listing 2: The Angular Service to accept ASP.NET Core Web API to perform HTTP calls
The code in the above listing contains methods to register and authenticate user and get products. These methods makes HTTP calls to ASP.NET Core Web API.
Step 5: In the secure-call folder add a new file and name it as app.interceptor.ts. We will add HttpInterceptor code in this file. Add the code in this file as shown in the following listing
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent} from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { SecureCallService } from './secure-call.service';
@Injectable()
export class SecurityTokenInterceptorService implements HttpInterceptor {
// 1
constructor(private serv: SecureCallService) {}
// 2
intercept(req: HttpRequest<any>, next: HttpHandler):
Observable<HttpEvent<any>> {
// 3 We retrieve the token, if any
const token = localStorage.getItem('token');
// 4
let newHeaders = req.headers;
// 5
if (token) {
console.log(token);
// 6 If we have a token, we append it to our new headers
newHeaders = newHeaders.append('Authorization', `Bearer ${token}`);
}
// 7 clone the request with new headers
const authReq = req.clone({headers: newHeaders});
// 8
return next.handle(authReq);
}
}
Listing 3: The HTTP Interceptor
The above code has following specifications (Note: Following numbers matches with comment numbers applied on the code)
Step 6: In the secure-call folder add a new file and name it as secure-call.component.ts. In the file add the code as shown in the following listing
import { Component, OnInit } from "@angular/core";
import {
RegisterUser,
LoginUser,
Product,
ResponseData
} from "./secure-call.models";
import { SecureCallService } from "./secure-call.service";
@Component({
selector: "app-secure-call-component",
templateUrl: "./secure-call.component.html"
})
export class SecureCallComponent implements OnInit {
createUser: RegisterUser;
loginUser: LoginUser;
products: Array<Product>;
response: ResponseData;
canLogin: boolean;
constructor(private serv: SecureCallService) {
this.createUser = new RegisterUser("", "", "");
this.loginUser = new LoginUser("", "");
this.products = Array<Product>();
this.response = new ResponseData("");
this.canLogin = false;
}
ngOnInit() { }
registerUser(): void {
this.serv.registerUser(this.createUser).subscribe(resp => {
this.response = resp;
if (this.response.message.length !== 0) {
this.canLogin = true;
}
});
}
authenticateUser(): void {
this.serv.authenticateUser(this.loginUser).subscribe(resp => {
this.response = resp;
console.log(resp);
localStorage.setItem('token',this.response.message);
});
}
getProducts(): void {
if (this.response.message.length !== 0) {
this.serv.getProducts(this.response.message).subscribe(resp => {
this.products = resp;
});
}
}
clearRegisterInfo(): void {
this.createUser = new RegisterUser("", "", "");
}
clearLoginInfo(): void {
this.loginUser = new LoginUser("", "");
}
}
Listing 4: The Component code
This component uses all models class defined in Step 3. The component is injected with SecureCallService. The component contains method for registeringUser, authenticateUser and getProducts. These methods invokes methods from the SecureCallServie class to further make HTTP calls. The authenticateUser() method receives JSON Web Token from the ASP.NET Core Web API and save the token in the localStorage.
Step 7: In the secure-call folder add a new file and name it as secure-call.component.html. Add the HTML markup in this file as shown in the following listing
<div class="container">
<table class="table table-bordered table-striped">
<tbody>
<tr>
<td>
<div class="container">
<h2>Register User</h2>
<div class="form-group">
<label for="Email">Email</label>
<input type="text" [(ngModel)]="createUser.Email" class="form-control">
</div>
<div class="form-group">
<label for="Password">Password</label>
<input type="password" [(ngModel)]="createUser.Password" class="form-control">
</div>
<div class="form-group">
<label for="ConfirmPassword">Confirm Password</label>
<input type="password" [(ngModel)]="createUser.ConfirmPassword" class="form-control">
</div>
<div class="form-group">
<input type="button" value="Clear" (click)="clearRegisterInfo()" class="btn btn-default">
<input type="button" value="Register User" (click)="registerUser()" class="btn btn-success">
</div>
</div>
</td>
<td>
<div class="container">
<h2>Login User</h2>
<div class="form-group">
<label for="Email">Email</label>
<input type="text" [(ngModel)]="loginUser.UserName" class="form-control">
</div>
<div class="form-group">
<label for="Password">Password</label>
<input type="password" [(ngModel)]="loginUser.Password" class="form-control">
</div>
<div class="form-group">
<input type="button" value="Clear" (click)="clearLoginInfo()" class="btn btn-default">
<input type="button" value="Login" (click)="authenticateUser()" class="btn btn-success">
</div>
</div>
</td>
</tr>
</tbody>
</table>
<hr>
<input type="button" value="Load Products" (click)="getProducts()">
<br />
<table class="table table-bordered table-striped">
<thead>
<tr>
<td>Product Row Id</td>
<td>Product Name>
<td>Price</td>
</tr>
</thead>
<tbody>
<tr *ngFor="let prd of products">
<td>{{prd.productRowId}}</td>
<td>{{prd.productName}}</td>
<td>{{prd.price}}</td>
</tr>
</tbody>
</table>
</div>
Listing 5: The HTML code for UI
The above markup will be used for bind with the methods and properties from SecureCallComponent class. This is the UI for registering new user, login the user and then get product data.
Step 8: Modify app.module.ts to import and declare component, import the HTTP_INTERCEPTORS, SecurityTokenInterceptor service as shown in the following listing
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { AppComponent } from './app.component';
import { SecureCallComponent } from './secure-call/secure-call.component';
import { SecurityTokenInterceptorService } from './secure-call/app.interceptor';
@NgModule({
declarations: [AppComponent, SecureCallComponent],
imports: [BrowserModule, FormsModule, HttpClientModule],
providers: [ {provide: HTTP_INTERCEPTORS, useClass: SecurityTokenInterceptorService, multi: true}],
bootstrap: [SecureCallComponent]
})
export class AppModule {}
Listing 6: AppModule class with necessary imports
The AppModule registers HTTP_INTERCEPTOR and configure the SecurityTokenInterceptorService for each HTTP calls interception form the Angular application to ASP.NET Core Web APIs.
Step 9: Modify index.html by adding the selector for SecureCallComponent in it as shown in the following listing
<body>
<app-secure-call-component></app-secure-call-component>
</body>
Listing 7: The index.html containing the selector for the component
Open the VSCode terminal window and run the following command
npm run start
This will start the project on port 4200. Open the browser and enter the following address
http://localhost:4200
The browser will show UI as shown in the following figure
Figure 2: The UI for registering, authenticating user and then get products
To register new, enter Email, Password and Confirm Password and click on Register User button. The user will be created as shown in the following figure
Figure 3: Registering New User
The console show the newly registered user.
Enter the UserName and Password for login the user and click on Login button. If the UserName and Password matches then the login will takes place and the Token will be responded as shown in the following figure
Figure 4: Login and Receive Token
The token is available in the localStorage. Now click on Load Products button, this will receive the Products data as shown in the following figure
Figure 4: The Products Details
The Code of this article can be downloaded from this link.
Conclusion: The Authentication and Authorization using JSON Web Tokens is one of the major requirements for the front-end applications. Angular application with HTTP Interceptor is the bast way of implementing JSON Token Based authentication.
- Angular Component execute a method to register a new user with the application. This method from the component calls the method for the Angular Service.
- The Angular service makes the HTTP post request to the Register() method from the AuthController from the ASP.NET Core REST API.
- HTTP Interceptor intercept the request and forward above POST request.
- The ASP.NET Core Web API accepts the Post request and map it with the Users Model. The AuthController connects to the Identity Database to create a new user into the database.
- Once the new user is successfully registered the response will be send back to the client application.
- Angular component executes a method that access method from the Angular service to authenticate the user.
- The Angular service make HTTP Post request to the Auth()/Login() method from the AuthController.
- The HTTP Interceptor intercepts above Post request.
- The Auth()/Login() method from the AuthController connects to the Identity Database to verify whether the user is present in the Identity tables.
- If the user is present then the Login will takes place.
- The Auth()/Login() method generates the JSON Web Token and respond it to the client.
- On the client application the JSON Web Token can be stored in the LocalStorage or SessonStorage. In this article we will be using LocalStorage.
- The Angular component executes a method to access application data e.g. Products from the ASP.NET Core Server application. The Angular component access method from the Angular Service.
- The Angular service reads the token from the LocalStorage and makes HTTP GET call to the ASP.NET Core Web API.
- The HTTP Interceptor intercepts the HTTP Get request and add the Token into the HTTP Request header.
- The Request is Accepted by the ASP.NET Core Web API. In this step the token is checked for verification. If the Token is verified the application controller will make call to the application database to read the application data e.g. Products information.
- The data from the Application database will be read and returns to the Application controller.
- The response will be send back to the Angular client.
In my previous article the JSON Web Token authentication is already explained. You can download the server side application code and use it.
Implementing the Angular Client application.
The client application is implemented using Microsoft Visual Studio Code (VSCode). You can download VSCode from this link. You also need the Node.js. This can be downloaded from this link.
Step 1: Open the command prompt and run the following command to install Angular CLI.
npm install -g @angular/cli
Step 2: To create a new Angular application run the following command
ng new ngclient
This will create a new Angular project will all dependencies.
Step 3: Open the ngclient folder in the VS Code. In the src folder of the project add a new folder of name secure-call. In this folder add a new file and name it as secure-call.models.ts. In this file add classes as shown in the following listing
export class RegisterUser {
constructor(
public Email: string,
public Password: string,
public ConfirmPassword: string
) {}
}
export class LoginUser {
constructor(public UserName: string, public Password: string) {}
}
export class Product {
constructor(
public productRowId: number,
public productName: string,
public price
) {}
}
export class ResponseData {
constructor(public message: string) {}
}
Listing 1: The Model classes for Registering Users, LoginUser, ResponseData and Product
The RegisterUser class is used to define properties to register new user with the application. The LoginUser class will be used to define properties for user login. The ResponseData class define property to receive response message from the ASP.NET Core Web API. The Product class define properties for storing data from the Web API.
Step 4: In the secure-call folder add a new file and name it as secure-call.service.ts. In this file add the code as shown in the following listing
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import {
RegisterUser,
LoginUser,
Product,
ResponseData
} from './secure-call.models';
import { Observable } from 'rxjs';
@Injectable({ providedIn: 'root' })
export class SecureCallService {
url: string;
constructor(private httpClient: HttpClient) {
this.url = 'http://localhost:5000';
}
// method to register the user
registerUser(user: RegisterUser): Observable<ResponseData> {
let userRegistered: Observable<ResponseData>;
const options = {
headers: new HttpHeaders({
'Content-Type': 'application/json'
})
};
userRegistered = this.httpClient.post<ResponseData>(
`${this.url}/api/Auth/Register`,
user,
options
);
return userRegistered;
}
// method to login the user the user
authenticateUser(user: LoginUser): Observable<ResponseData> {
let userAuthenticated: Observable<ResponseData>;
const options = {
headers: new HttpHeaders({
'Content-Type': 'application/json'
})
};
userAuthenticated = this.httpClient.post<ResponseData>(
`${this.url}/api/Auth/Login`,
user,
options
);
return userAuthenticated;
}
// method to get products
getProducts(token: string): Observable<Product[]> {
let products: Observable<Product[]>;
const headerValues = new HttpHeaders();
products = this.httpClient.get<Product[]>(`${this.url}/api/Products`);
return products;
}
}
The code in the above listing contains methods to register and authenticate user and get products. These methods makes HTTP calls to ASP.NET Core Web API.
Step 5: In the secure-call folder add a new file and name it as app.interceptor.ts. We will add HttpInterceptor code in this file. Add the code in this file as shown in the following listing
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent} from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { SecureCallService } from './secure-call.service';
@Injectable()
export class SecurityTokenInterceptorService implements HttpInterceptor {
// 1
constructor(private serv: SecureCallService) {}
// 2
intercept(req: HttpRequest<any>, next: HttpHandler):
Observable<HttpEvent<any>> {
// 3 We retrieve the token, if any
const token = localStorage.getItem('token');
// 4
let newHeaders = req.headers;
// 5
if (token) {
console.log(token);
// 6 If we have a token, we append it to our new headers
newHeaders = newHeaders.append('Authorization', `Bearer ${token}`);
}
// 7 clone the request with new headers
const authReq = req.clone({headers: newHeaders});
// 8
return next.handle(authReq);
}
}
The above code has following specifications (Note: Following numbers matches with comment numbers applied on the code)
- The SecurityTokenInterceptorService class implements an HttpInterceptor interface and its intercept method. The class is constructor injected with the SecureCallService. This means that all Http calls from SecureCallService will be intercepted using the SecurityTokenInterceptorService class.
- The intercept() method accepts HttpRequest, HttpHendler and Objervable<HttpEvent> objects as input parameters. The HttpRequest object represents the current outgoing HTTP request. The interceptor uses this object to modify the HTTP Headers. The HttpHandler object transform the HttpRequest into the a stream of HttpEvents. The HttpEvent can be the HttpResponse received for the HttpRequest. The HttpHandler can further handle the request from one interceptor to the next interceptor.
- Since the current interceptor is used to add Token to the outgoing request, we will read the token from localstorage.
- We need to read HTTP header information for the current outgoing request. This step is important because the interceptor will modify the HTTP Header.
- If the token is available then it will be used by the interceptor
- The token will be added in the HTTP request header.
- The request with modified header will be cloned in the HttpRequest object.
- Finally the request will be made available to the next interceptor using HttpHandler object.
Step 6: In the secure-call folder add a new file and name it as secure-call.component.ts. In the file add the code as shown in the following listing
import { Component, OnInit } from "@angular/core";
import {
RegisterUser,
LoginUser,
Product,
ResponseData
} from "./secure-call.models";
import { SecureCallService } from "./secure-call.service";
@Component({
selector: "app-secure-call-component",
templateUrl: "./secure-call.component.html"
})
export class SecureCallComponent implements OnInit {
createUser: RegisterUser;
loginUser: LoginUser;
products: Array<Product>;
response: ResponseData;
canLogin: boolean;
constructor(private serv: SecureCallService) {
this.createUser = new RegisterUser("", "", "");
this.loginUser = new LoginUser("", "");
this.products = Array<Product>();
this.response = new ResponseData("");
this.canLogin = false;
}
ngOnInit() { }
registerUser(): void {
this.serv.registerUser(this.createUser).subscribe(resp => {
this.response = resp;
if (this.response.message.length !== 0) {
this.canLogin = true;
}
});
}
authenticateUser(): void {
this.serv.authenticateUser(this.loginUser).subscribe(resp => {
this.response = resp;
console.log(resp);
localStorage.setItem('token',this.response.message);
});
}
getProducts(): void {
if (this.response.message.length !== 0) {
this.serv.getProducts(this.response.message).subscribe(resp => {
this.products = resp;
});
}
}
clearRegisterInfo(): void {
this.createUser = new RegisterUser("", "", "");
}
clearLoginInfo(): void {
this.loginUser = new LoginUser("", "");
}
}
This component uses all models class defined in Step 3. The component is injected with SecureCallService. The component contains method for registeringUser, authenticateUser and getProducts. These methods invokes methods from the SecureCallServie class to further make HTTP calls. The authenticateUser() method receives JSON Web Token from the ASP.NET Core Web API and save the token in the localStorage.
Step 7: In the secure-call folder add a new file and name it as secure-call.component.html. Add the HTML markup in this file as shown in the following listing
<div class="container">
<table class="table table-bordered table-striped">
<tbody>
<tr>
<td>
<div class="container">
<h2>Register User</h2>
<div class="form-group">
<label for="Email">Email</label>
<input type="text" [(ngModel)]="createUser.Email" class="form-control">
</div>
<div class="form-group">
<label for="Password">Password</label>
<input type="password" [(ngModel)]="createUser.Password" class="form-control">
</div>
<div class="form-group">
<label for="ConfirmPassword">Confirm Password</label>
<input type="password" [(ngModel)]="createUser.ConfirmPassword" class="form-control">
</div>
<div class="form-group">
<input type="button" value="Clear" (click)="clearRegisterInfo()" class="btn btn-default">
<input type="button" value="Register User" (click)="registerUser()" class="btn btn-success">
</div>
</div>
</td>
<td>
<div class="container">
<h2>Login User</h2>
<div class="form-group">
<label for="Email">Email</label>
<input type="text" [(ngModel)]="loginUser.UserName" class="form-control">
</div>
<div class="form-group">
<label for="Password">Password</label>
<input type="password" [(ngModel)]="loginUser.Password" class="form-control">
</div>
<div class="form-group">
<input type="button" value="Clear" (click)="clearLoginInfo()" class="btn btn-default">
<input type="button" value="Login" (click)="authenticateUser()" class="btn btn-success">
</div>
</div>
</td>
</tr>
</tbody>
</table>
<hr>
<input type="button" value="Load Products" (click)="getProducts()">
<br />
<table class="table table-bordered table-striped">
<thead>
<tr>
<td>Product Row Id</td>
<td>Product Name>
<td>Price</td>
</tr>
</thead>
<tbody>
<tr *ngFor="let prd of products">
<td>{{prd.productRowId}}</td>
<td>{{prd.productName}}</td>
<td>{{prd.price}}</td>
</tr>
</tbody>
</table>
</div>
Listing 5: The HTML code for UI
The above markup will be used for bind with the methods and properties from SecureCallComponent class. This is the UI for registering new user, login the user and then get product data.
Step 8: Modify app.module.ts to import and declare component, import the HTTP_INTERCEPTORS, SecurityTokenInterceptor service as shown in the following listing
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { AppComponent } from './app.component';
import { SecureCallComponent } from './secure-call/secure-call.component';
import { SecurityTokenInterceptorService } from './secure-call/app.interceptor';
@NgModule({
declarations: [AppComponent, SecureCallComponent],
imports: [BrowserModule, FormsModule, HttpClientModule],
providers: [ {provide: HTTP_INTERCEPTORS, useClass: SecurityTokenInterceptorService, multi: true}],
bootstrap: [SecureCallComponent]
})
export class AppModule {}
The AppModule registers HTTP_INTERCEPTOR and configure the SecurityTokenInterceptorService for each HTTP calls interception form the Angular application to ASP.NET Core Web APIs.
Step 9: Modify index.html by adding the selector for SecureCallComponent in it as shown in the following listing
<body>
<app-secure-call-component></app-secure-call-component>
</body>
Open the VSCode terminal window and run the following command
npm run start
This will start the project on port 4200. Open the browser and enter the following address
http://localhost:4200
The browser will show UI as shown in the following figure
Figure 2: The UI for registering, authenticating user and then get products
To register new, enter Email, Password and Confirm Password and click on Register User button. The user will be created as shown in the following figure
Figure 3: Registering New User
The console show the newly registered user.
Enter the UserName and Password for login the user and click on Login button. If the UserName and Password matches then the login will takes place and the Token will be responded as shown in the following figure
Figure 4: Login and Receive Token
The token is available in the localStorage. Now click on Load Products button, this will receive the Products data as shown in the following figure
Figure 4: The Products Details
The Code of this article can be downloaded from this link.
Conclusion: The Authentication and Authorization using JSON Web Tokens is one of the major requirements for the front-end applications. Angular application with HTTP Interceptor is the bast way of implementing JSON Token Based authentication.