What is HTTP Client in Angular?

This tutorial is adapted from Web Age course Comprehensive Angular 10 Programming Training.

1.1 The Angular HTTP Client

The Angular HTTP Client provides a simplified API for network communication. It is a wrapper over the JavaScript XMLHttpRequest API. The API is asynchronous. JavaScript is single-threaded. Doing a blocking synchronous HTTP call will otherwise freeze the UI. It supports making HTTP requests (GET, POST, etc.), working with request and response headers, asynchronous programming. It makes use of the rxjs async library Observable object.

1.2 Using The HTTP Client – Overview

The core client API is available from the HttpClient Angular service class. This is available from the HttpClientModule Angular module.

  • Import HttpClientModule from your application module.

A Data Service is created that makes network requests:

  • Inject the HttpClient service instance.
  • Use various methods of HttpClient to make network calls. They return an Observable object. Usually, this Observable is returned from the service method.

An Angular component is used to display the response data:

    • The data service is injected into the constructor
    • The component calls a method of the data service and obtains an Observable object.
    • The component then “subscribes” to the Observable to receive the response from the HTTP call.
    • The component’s template displays the data

1.3 Importing HttpClientModule

In order to use the Http client throughout the application, we need to import HttpClientModule in the application module. HttpClientModule is a collection of service providers from the Angular HTTP library

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

@NgModule({
    imports: [ BrowserModule, HttpClientModule ],
...})

Now the HttpClient service is injectable throughout your application

Importing HttpClientModule

Note that HttpClientModule configures HttpClient as a provider. You don’t have to do this again from your application module. As a result, HttpClient is now injectable anywhere in your application.

1.4 Service Using HttpClient

Sample Service Code (“people.service.ts”):

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';

export class Person {id:number, name:string ...}

@Injectable(...)
export class PeopleService{
  constructor(private http: HttpClient){}

  getPerson(id:number) : Observable<Person> {
   return this.http.get<Person>(`/app/person/${id}`)
  }
  getAllPersons() : Observable<Person[]> {
   return http.get<Person[]>(`/app/person`)
  }
}

1.5 Making a GET Request

  • Call the get() method of HttpClient service.
      All network call methods like get(), post(), put() return an Observable.
  • There are many overloaded versions of the get() call. Some are:
let obs:Observable<Object> = http.get(url) 
//Strong typing
let obs:Observable<Person> = http.get<Person>(url) 
  • For the get() calls it is recommended you use the strongly typed version (second one from above).
  • The get() method returns an Observable immediately. One needs to subscribe to the Observable to obtain the response data asynchronously.

Notes:

As they are used with the Angular HttpClient object Observable objects can be thought of as the glue that connects network requests with data consumers. There are several aspects to this relationship that our discussion will cover one at a time. From the current slide, we see that http requests return Observable objects that we can assign to a variable for later use. In the next slide, we will take a look at how Observable objects are used.

1.6 What does an Observable Object do?

Angular uses the Reactive Extensions for JavaScript (RxJS) implementation of the Observable object. Observable objects provide a convenient way for applications to consume asynchronous data/event streams. Observable objects are exposed to events which they then make available to consumers. Applications consume events by subscribing to the Observable object. The Observable object can be used to transform data before returning it to a consumer if needed.

  • Notes:

Reactive programming and Observable objects can be used anywhere an asynchronous programming model is required. For more information on these topics see:

http://reactivex.io/intro.html

1.7 Using the Service in a Component

  • Once the service has been created we can create an Angular component that uses it to retrieve and display data.
  • Our component will have to import the service.
  • In order to retrieve data, the code in our component will have to work with the Observable object that was created in the service.
  • The screenshot at the right shows how the data will look when displayed.

1.8 The PeopleService Client Component

import { Component, OnInit } from '@angular/core';
import { PeopleService, Person } from './people.service';

@Component({
  selector: 'app-people',
  template: `
    <p *ngFor='let person of list' >{person.name}}</p>`
})
export class PeopleComponent implements OnInit {
  list: Person[]
  constructor(private peopleService: PeopleService ){}
  ngOnInit() {
    this.peopleService.getAllPersons().subscribe(
      (data: Person[]) => this.list = data
    )
  }
}}

1.9 Error Handling

  • Two types of errors can happen during a network call:
    • No network connection can be made to the server.
    • The server returns an invalid response code in 4XX and 5XX range.
  • A subscriber can handle these errors by supplying an error handler function to subscribe().
ngOnInit() {
  this.peopleService.getAllPersons().subscribe(
    (data: Person[]) => this.list = data,
    (error:HttpErrorResponse) => console.log(error)
    )
}

1.10 Customizing the Error Object

    • A service may decide to offer more meaningful errors in a custom error object instead of the default HttpErrorResponse. You can intercept an error with the

catchError()

    • operator and supply a custom error object using

throwError()

    • .

The following transforms the error object from HttpErrorResponse to a string.

import {Observable, throwError} from 'rxjs'
import {catchError} from 'rxjs/operators'

export class PeopleService {
  constructor(httpClient: HttpClient){}

  getAllPersons() : Observable<Person[]> {
   return httpClient
    .get<Person[]>(`/app/person`)
    .pipe(
      catchError(error => 
        throwError("There was a problem with the network"))
    )
  }
}

//The component
ngOnInit() {
  this.peopleService.getAllPersons().subscribe(
    (data: Person[]) => this.list = data,
    (errMsg:string) => alert(errMsg))
}

1.11 Making a POST Request

Supply the request body as the second argument to the post() method.

createPerson(person:Person) : Observable {
  return this.http.post("/app/person", person) 
}

If a POST request returns data in the response body, you can make a more type-safe call.

createPerson(person:Person) : Observable<AddPersonResult> {
  return this.http.post<AddPersonResult>("/app/person", 
    person) 
}
//The component
this.peopleService.createPerson(...)
  .subscribe((result: AddPersonResult) => {...})

1.12 Making a PUT Request

  • Supply the request body as the second argument to the put() method.
updatePerson(person:Person) : Observable {
  return this.http.put("/app/person", person) 
}
  • If a PUT request returns data in the response body, you can make a more type-safe call.
updatePerson(person:Person) : Observable<UpdateResult> {
  return this.http.put<UpdateResult>("/app/person", 
    person) 
}
//The component
this.peopleService.updatePerson(...)
  .subscribe((result: UpdateResult) => {...})

1.13 Making a DELETE Request

  • A DELETE request does not take any body

deletePerson(id:number) : Observable { return this.http.delete(`/app/person/${id}`) }

  • If a DELETE request returns data in the response body, you can make a more type safe call.
deletePerson(id:number) : Observable<DeleteStatus> {
  return this.http.delete<DeleteStatus>(`/app/person/${id}`) 
}
//The component
this.peopleService.deletePerson(12)
  .subscribe((result: DeleteStatus) => {...})

1.14 Summary

In this tutorial, we covered:

  • What is the Angular HTTP Client
  • Importing HttpClientModule
  • Making Get/ Post Calls
  • Working with Observables

How to Create a Basic Single Page Application Using Angular Router?

This tutorial is adapted from Web Age course Introduction to Angular 8 Programming.

In this tutorial, we will develop a very simple single page application (SPA) using the Angular Component Router module. The main goal will be to understand how routing works. We will keep the business logic very simple.

Part 1 – Get Started

1. Open a command prompt window.

2. Create  C:\LabWork folder.

3. Run this command to create a new project called route-test.

ng new route-test --routing --style css

Part 2 – The Business Logic

We will develop an SPA that has three pages (views):

  1. The home page. Shown by default.

  2. The news page. Mapped to the “/news” URL.

  3. The about page. Mapped to the “/about” URL.

After we develop this application we will be able to navigate between the pages without reloading the browser.

Part 3 – Create the Components

Each view in SPA is implemented by a separate component. These components can employ child components if needed. We will now create the components for our applications.

1. Change to the root project.

cd C:\LabWork\route-test

2. Run these commands to create the components.

ng g c home
ng g c about
ng g c news

The shortcut g stands for generate and c for component.

3. Open src/app/home/home.component.html.

4. Change the template like this:

  <h2>Home</h2>
  <p>This is the Home page</p>

5. Save the file.

6. Open src/app/about/about.component.html.

7. Change the template like this:

  <h2>About</h2>
  <p>This is the About page</p>

8. Save the file.

9. Open src/app/news/news.component.html.

10. Change the template like this:

  <h2>News</h2>
  <p>This is the News page</p>

11. Save the file.

The selectors for these components do not really play any role. We never manually add these components to any template. The router system inserts these components for us based on the URL.

Part 4 – Define the Route Table

1. Open route-test/src/app/app-routing.module.ts

2. Add these import statements for the component classes.

import { HomeComponent } from './home/home.component';
import { NewsComponent } from './news/news.component';
import { AboutComponent } from './about/about.component';

3. Set up the route table as shown in bold face below.

const routes: Routes = [
  { path: '', component: HomeComponent },
  { path: 'news', component: NewsComponent },
  { path: 'about', component: AboutComponent }
];

4. Save changes.

Part 5 – Setup the Component Host

1. Edit \src\app\app.component.html and replace all of its contents with the code below:

  <h1>Simple SPA!</h1>
  <a [routerLink]="['/']">Home</a>&nbsp;
  <a [routerLink]="['/news']">News</a>&nbsp;
  <a [routerLink]="['/about']">About</a>

  <div id=main>
    <router-outlet></router-outlet>
  </div>

The App component will act as the root of the application. It will render HTML content that is shared by all pages in the app.

Note these key aspects of the code:

  • We use the routerLink attribute directive to define navigational links. Here we are using links like “/news” and “/about”. We will later map these to the corresponding components in a route table.

  • Use <router-outlet> tag to define where the component for the pages should be inserted.

2. Save changes.

3. Edit \src\styles.css

4. Add the following content and save the file:

h2 {
    margin:0px;
}
#main{
    border: 1px solid grey;
    width: 250px;
    background-color: lightgray;
    margin-top: 5px;
    padding: 5px;
}

5. Save changes.

Part 6 – Test

1. Open a command prompt and go to the root folder of the project:

C:\LabWork\route-test

2. Run the following command to compile TypeScript and start the server:

npm start

3. Open a browser to http://localhost:4200

4. Verify that the home page is shown by default.

5. Click the News and About links. Make sure that the corresponding pages are shown. Verify that the URL in the browser’s address bar changes as you navigate.

6. Verify that the back button navigation works as expected.

7. Navigate to the About page.

8. Now refresh the browser. You should still see the About page.

Note: This behavior is primarily because the project uses the embedded server and all requests get routed to Angular. If you were using a web server that might first look for a local HTML file, refreshing the URL from the Router may not work and you may get a 404 error.

9. In the command prompt, hit ‘<CTRL>-C‘ to terminate the embedded server.

10. Close all open files.

Part 7 – Review

In this tutorial, we created a single page application with routing.

Security in Angular Applications

This tutorial is adapted from Web Age course Advanced Angular 8 Programming.

1.1 What is Authentication and Authorization?

Authentication is the process of reliably identifying a user. Authorization is the process of giving a user access to only the resources she is allowed to access. Some common programming tasks that need authenication and authorization are: Provide a login page where user enters her credentials, upon a successful login the server issues a token identifying the user-the browser side saves that token in memory or local storage, Limit access to application functions based on user privilege-this is usually done using role based security, With every HTTP call to the backend include the user identity token;depending on the technology used this is usually done as a cookie or HTTP header, If the user accesses a protected resource (such as an Angular route) and the identity token does not exist or has expired then redirect to the login page, If a HTTP call returns 403 or similar then redirect to the login page.

1.2 What is Identity Token?

Two most common ways to create an identity token are SAML2 and JWT. In the end they both achieve the same goals. An identity token issued by the server contains user’s unique ID, name, e-mail address, various security roles and any other kind of business specific payload. The token is signed by the server. It is very difficult to generate a fake token by a third party. When a client sends a token with an HTTP request the server can reliably ensure that the token was issued by it and has not been tampered with. A token is however vulnerable to stealing via man in the middle attack. The server can not discern if a token was stolen. To prevent against that always transfer tokens over HTTPS and set a token to expire every few hours. SAML2 is widely used in the enterprise. But JWT is a more modern standard and much easier to use. In this tutorial we will use JWT in the examples but the basic approach will be same for SAML2.

1.3 How to exchange the Identity Token?

Upon a successful login the server will issue the identity token. The client needs to send this back in future with every HTTP request. Exactly how the server and client do this depends on various things. Here are two common scenarios:

  • Cookie based – The server expects the token to be in a cookie. In which case the server sets the cookie and the browser automatically sends it back with every request.

  • Header based – In JWT the client usually sends the token back in the “Authorization” request header. In that case the server issues the token in the response body of the login request. The client needs to write code to save that token and set the Authorization header for all future requests.

//Example response from server containing a JWT token
{
  token: "abc-123"
}

//Example Authorization request header
Authorization: Bearer abc-123

1.4 Example Login Code in Angular Service

Saves identity token in session storage

@Injectable(...)
export class AuthenticationService {
  constructor(private http:HttpClient) { }

  login(credentials:Object) : Observable<Object> {
    return this.http.post<Object>("/login", credentials)
      .pipe(tap(response => 
          window.sessionStorage.setItem("auth-token", 
          response["token"])))
  }
  getAuthToken() : string {
    return window.sessionStorage.getItem("auth-token")
  }
}

Notes

The example above assumes that the identity token is available from the response of the “/login” call as follows:

{ token: “eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.e30.0gyt4I1ZFGzzDZBrK9jwOIH4NSWvs0k4snp-ZcwKLVU” }

1.5 What are the Options for Saving the Identity Token?

You can save the token in a service class member variable. Because Angular apps are single page the service instance is preserved as the user navigates between views. In session storage: This is per server domain and per tab. The storage is cleared when the tab is closed. In local storage. This is per domain and survives a browser restart.

1.6 How to send the Token to Server?

You can develop an interceptor to set the Authorization header for every backend call.

@Injectable()
export class AuthInterceptorService implements HttpInterceptor {
  constructor(private authSvc: AuthenticationService) { }

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    if (this.authSvc.getAuthToken() === null) {
      return next.handle(req)
    }

    req = req.clone({
      setHeaders: { "Authorization": `Bearer ${this.authSvc.getAuthToken()}` }
    })

    return next.handle(req)
  }
}

1.7 How to Obtain User Information?

JWT allows the server to encode user information such as full name, profile photo URL and user roles in the identity token. This information is called payload of the token. You can decode the token to obtain the payload. Use the jwt-decode package to do this.

npm install jwt-decode --save

npm install @types/jwt-decode –save-dev
  • Example code.

import * as decode from 'jwt-decode'

export class UserProfile {
  photoURL:string
  name:string
  roles:string[]
}

let jwtToken = "ABC1728...2xcv"
let userProfile = decode<UserProfile>(jwtToken)

1.8 What is Cross Site Scripting (XSS) Attack?

This attack is mounted by a user by somehow embedding a <script> tag in a page. This can be done for example by submitting a comment or writing a user profile that contains the <script> tag. When another user visits the infected page, the script can then collect private information about that user from the DOM such as cookie, names and emails. This information then can be passed to a third party site using a mechanism that bypasses the same origin policy. Such as setting the src attribute of an image or adding a <script> tag with source from another site. XSS is one of most common forms of security breaches on the web. To prevent XSS reject any HTML content from untrusted sources. For example, you can severely limit or completely eliminate any HTML in user comments. But allow a company staff to allow entering HTML in product catalog content.

1.9 Angular and XSS

By default Angular considers values stored in any component variable as untrusted. But any HTML directly entered in the template is trusted. In this example, the <div> and <b> tags in the template are trusted. But the <h1> tag in the comment variable is not trusted. Angular will sanitize variables (escape the HTML) before setting DOM element properties.

@Component({...
  template: `<div><b>Comment:</b> {{comment}}</div>`
})
export class AppComponent {
  comment = '<h1>Hello World</h1>'
}

//Generates this DOM element
<div><b>Comment:</b> &lt;h1&gt;Hello World&lt;/h1&gt;</div>

1.10 How to Sanitize Content?

Angular uses different strategies to sanitize the value in a variable depending on where in the template the variable is used:

  • HTML – When the variable is used to set the innerHTML of an element. We see this in the example in the previous slide.

  • Style – When you bind the variable to the “style” property of an element.

  • URL – When you bind the variable to the “href” property.

  • Resource URL – When you bind the variable to the “src” attribute of an image or script.

A different strategy is used in these cases because an XSS attack is mounted differently in each of these situations.

1.11 How to Trust HTML Content?

To bypass Angular’s sanitization for trusted content take these steps:

  • Convert a string to SafeHtml by calling the bypassSecurityTrustHtml() method of DomSanitizer service.

  • Bind the SafeHtml object to the innerHTML property of an element.

  • The same example as before converted to bypass sanitization.

import { DomSanitizer, SafeHtml } from '@angular/platform-browser';

@Component({...,
  template: `<div><b>Comment:</b> <span  [innerHTML]="getSafeComment()"></span></div>`
})
export class AppComponent {
  comment = '<h1>Hello World</h1>'
  constructor(private sanitizer: DomSanitizer){}
  getSafeComment() : SafeHtml {
    return this
      .sanitizer
      .bypassSecurityTrustHtml(this.comment)
  }
}

1.12 What is Cross-Site Request Forgery (XSRF)?

In this attack a page from a malicious site (evil.com) somehow sends a request to a legitimate site (good.com). Such a request needs to bypass the same origin policy which is quite easy. For example, request an image from good.com or submit a form using JavaScript. If the user is currently logged into the legitimate site then such a request can cause all kinds of unintended consequence. The damage level increases with the level of privilege the user has with the legitimate site. XSRF attack is technically very easy to mount. The only challenge is in how to lure users into the malicious site. This is usually done through phishing attack. Details of XSRF and strategies to mitigate it are beyond the scope of this class. Read about it in www.owasp.org.

1.13 Preventing XSRF Using Angular

One of the strategies used to prevent XSRF is to set a custom request header from the client and verify its existence in the server side. Currently there is no way for a malicious web page to set custom headers prior to requesting an image or submitting a form to the legitimate web site.

Angular’s HttpClient service has some support for this. To use this, take these steps:

  • The server must first issue a unique token using the XSRF-TOKEN cookie. set-cookie: XSRF-TOKEN=example-xsrf-token;Path=/

  • For all future POST, PUT, DELETE requests HttpClient will set the X-XSRF-TOKEN custom header. X-XSRF-TOKEN: example-xsrf-token

  • From the server side verify that X-XSRF-TOKEN custom header is set.

  • In addition, take all other mitigation steps outlined in OWASP web site. Like verify the Referer request header to make sure request is coming from a page served by the legitimate web site.

1.14 Summary

In this tutorial we covered:

  • Upon a successful authentication the server should issue an identity token. Angular app should save the token in memory, local or session storage depending on the business requirement.

  • To make any secure backend call the application needs to send that token back. Server validates the token to make sure that it was not made up by a third party or has not expired.

  • You can obtain user information such as name and email by decoding the token.

  • Angular provides ways to mitigate Cross Site Scripting and Cross Site Request Forgery attacks.