========================= Part II - Token Storage ========================= In this second tutorial we will improve upon the previous tutorial. We will see more advanced method calling and responses. We will add services, routing and http interceptors. After logging in, the bexstream backend replies with a user token. This user token must be sent in all API calls to identify the user making the API call. In this tutorial we will use the browser’s local storage to save the token. 1 – Create WebStorageService ============================ Add the WebStorageService inside folder lib ``ng generate service lib/web-storage`` Import webstorage module: ``npm install  ngx-webstorage-service@4.1.0`` Add read and write features to *web-storage.service.ts*: .. highlight:: ts .. code-block:: :linenos: :caption: web-storage.service.ts import { Inject, Injectable, InjectionToken } from '@angular/core'; import { StorageService } from 'ngx-webstorage-service'; export const BEXSTREAM_SERVICE_STORAGE = new InjectionToken('BEXSTREAM_SERVICE_STORAGE'); const STORAGE_KEY = 'bexstream-token'; @Injectable({ providedIn: 'root' }) export class WebStorageService { constructor( @Inject(BEXSTREAM_SERVICE_STORAGE) private storage: StorageService ) { } public storageToken(token: string): void { this.storage.set(STORAGE_KEY, token); } public getStoredToken(): string { return this.storage.get(STORAGE_KEY); } public removeStoredToken(): void { this.storage.remove(STORAGE_KEY); } public clearLocalStorage(): void { this.storage.clear(); } } | Afterwards we need to import this new service into *app.module.ts* .. code-block:: :linenos: :caption: app.module.ts :emphasize-lines: 5, 10, 23-26 import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { ReactiveFormsModule } from '@angular/forms'; import { HttpClientModule } from '@angular/common/http'; import { LOCAL_STORAGE } from 'ngx-webstorage-service'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; import { LoginComponent } from './login/login.component'; import { BEXSTREAM_SERVICE_STORAGE, WebStorageService } from './lib/web-storage.service'; @NgModule({ declarations: [ AppComponent, LoginComponent ], imports: [ BrowserModule, AppRoutingModule, ReactiveFormsModule, HttpClientModule ], providers: [ {provide: BEXSTREAM_SERVICE_STORAGE, useExisting: LOCAL_STORAGE}, WebStorageService ], bootstrap: [AppComponent] }) export class AppModule { } | Now that the WebStorage service is created we need to use it on the LoginComponent. .. code-block:: :linenos: :caption: login.component.ts :emphasize-lines: 5, 22, 27, 42 import { Component, OnInit } from '@angular/core'; import { FormBuilder } from '@angular/forms'; import { HttpClient } from '@angular/common/http'; import { User } from './models/user'; import { WebStorageService } from '../lib/web-storage.service'; @Component({ selector: 'app-login', templateUrl: './login.component.html', styleUrls: ['./login.component.less'] }) export class LoginComponent implements OnInit { user = { username: '', password: '' }; // see body on OpenAPI definition for POST /api/v1/auth/user loginForm = this.formBuilder.group({ username: '', password: '' }); constructor(private formBuilder: FormBuilder, private webStorage: WebStorageService, private http: HttpClient) { } ngOnInit(): void { this.user = { username: '', password: '' }; this.webStorage.removeStoredToken(); // implicit logout on init } /** * User authentication method */ public authenticate(): void { const loginFormValues = this.loginForm.value this.user.username = loginFormValues.username; this.user.password = loginFormValues.password; this.http.post<{ token: string }>('https://bexstream-preprod.beyond-vision.com/api/v1/auth/user', this.user) .subscribe({ next: (result) => { if (result.token) { this.webStorage.storageToken(result.token); alert(`${this.user.username} has been successfully logged in!`); } else { alert(`Unexpected response from the server for /api/v1/auth/user. Check the network request/response for details!`); } }, error: (err) => { alert(`Error on login. Please check the username and the password!.`); } }); } } | Restart the application: ``ng serve`` | | Login using the following credentials: ``user: drone-pilot-tutorial`` ``pass: drone-pilot-tutorial`` And check on you browser developer tools if the token has been recorded: .. image:: ../../_static/images/bexstream/token.webp :align: center | | By this step we have already logged in and stored the user token on the browser’s local storage. For the last step we need to attach this token for each bexstream API invocations. So on each invocation we must add to the request’s header the following: ``Authorization: ‘Bearer {token}’`` This token is used for user acces control and to identify who is doing the invocation. We could attach the Authorization header on each request, but Angular has a built in feature called HTTP Interceptor that does this for us. We just need to configure it. Firstly let’s add ou interceptor: ``ng generate interceptor lib/interceptors/jwt`` We will use the WebStorageService to retrieve the user token and for each request we will add to each request's header the User Token. Open the file jwt.interceptors.ts and add the following code: .. code-block:: :linenos: :caption: jwt.interceptor.ts import { Injectable } from '@angular/core'; import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from '@angular/common/http'; import { Observable } from 'rxjs'; import { WebStorageService } from '../web-storage.service'; @Injectable() export class JwtInterceptor implements HttpInterceptor { constructor(private webStorage: WebStorageService) {} intercept(request: HttpRequest, next: HttpHandler): Observable> { // add authorization header with jwt token if available const apiToken = this.webStorage.getStoredToken(); if (apiToken) { request = request.clone({ setHeaders: { Authorization: `Bearer ${apiToken}` } }); } return next.handle(request); } } | Now let’s add our JwtInterceptor to the app.module.ts: .. code-block:: :linenos: :caption: app.module.ts :emphasize-lines: 6, 26 import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { ReactiveFormsModule } from '@angular/forms'; import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http'; import { LOCAL_STORAGE } from 'ngx-webstorage-service'; import { JwtInterceptor } from './lib/interceptors/jwt.interceptor'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; import { LoginComponent } from './login/login.component'; import { BEXSTREAM_SERVICE_STORAGE, WebStorageService } from './lib/web-storage.service'; @NgModule({ declarations: [ AppComponent, LoginComponent ], imports: [ BrowserModule, AppRoutingModule, ReactiveFormsModule, HttpClientModule ], providers: [ {provide: BEXSTREAM_SERVICE_STORAGE, useExisting: LOCAL_STORAGE}, {provide: HTTP_INTERCEPTORS, useClass: JwtInterceptor, multi: true}, WebStorageService ], bootstrap: [AppComponent] }) export class AppModule { }