6

I have build an interceptor for making HTTP requests to a PHP backend. This backend gives an JWT token to the app and I save this in the Ionic Storage. But I want to get that token from Storage and add it as an header to the HTTP request.

Below is my interceptor with and hardcoded token. This works and I get a response from the backend.

See update @ bottom of this post

http-interceptor.ts

import { HttpInterceptor, HttpRequest } from '@angular/common/http/';
import {HttpEvent, HttpHandler} from '@angular/common/http';
import { AuthProvider } from "../providers/auth/auth";
import {Injectable} from "@angular/core";
import {Observable} from "rxjs/Observable";
import {Storage} from "@ionic/storage";

@Injectable()
export class TokenInterceptor implements HttpInterceptor {

    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        const changedReq = req.clone({headers: req.headers.set('Authorization', 'Bearer MY TOKEN')});
        return next.handle(changedReq);
    }

}

But how do I get the token from storage into the header. I searched alot and most of the tutorials / examples are from the older HTTP module. If someone has an idea or has a up2date example ?

UPDATE

Oke below code send the token

intercept(req: HttpRequest<any>, next: HttpHandler) : Observable<HttpEvent<any>>{
        return fromPromise(this.Auth.getToken())
            .switchMap(token => {
                const changedReq = req.clone({headers: req.headers.set('Authorization', 'Bearer ' + token )});

                return next.handle(changedReq);
            });
    }

With 1 exception, namely the first time I access that page :)

PostMans
  • 338
  • 5
  • 18

3 Answers3

6

For anyone who comes across this like me and is using rxjs >=5.5.0 then you can just do:

auth-interceptor.ts

@Injectable()
export class AuthInterceptor implements HttpInterceptor {

constructor(private authService: AuthService) { }

intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {

    return from(this.authService.getToken()).pipe(mergeMap((token) => {
        const changedReq = req.clone({
            setHeaders: {
                Authorization: `Bearer ${token}`
            }
        });

        return next.handle(changedReq);
    }));
}

auth-service.ts

public async getToken() {
    return await this.storage.get('ACCESS_TOKEN');
}
MatthiasV
  • 71
  • 2
  • 4
4

You can save JWT token in, for example, localStorage

 localStorage.setItem('myToken', res.token);

and then access it with

localStorage.getItem('myToken');

In your case something like this:

import { HttpInterceptor, HttpRequest } from '@angular/common/http/';
import {HttpEvent, HttpHandler} from '@angular/common/http';
import { AuthProvider } from "../providers/auth/auth";
import {Injectable} from "@angular/core";
import {Observable} from "rxjs/Observable";
import {Storage} from "@ionic/storage";

@Injectable()
export class TokenInterceptor implements HttpInterceptor {

    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        const changedReq = req.clone({headers: req.headers.set('Authorization', localStorage.getItem('myToken'))});
        return next.handle(changedReq);
    }

}

If you want to use Ionic Storage

import { HttpInterceptor, HttpRequest } from '@angular/common/http/';
    import {HttpEvent, HttpHandler} from '@angular/common/http';
    import { AuthProvider } from "../providers/auth/auth";
    import {Injectable} from "@angular/core";
    import {Observable} from "rxjs/Observable";
    import {Storage} from "@ionic/storage";

   @Injectable()
    export class TokenInterceptor implements HttpInterceptor {

    constructor(public _storage: Storage) {
         _storage.get('myToken').then((val) => {
         console.log('Your age is', val);
         });
    }

       intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
            const changedReq = req.clone({headers: req.headers.set('Authorization', this.val)});
            return next.handle(changedReq);
        }

    }
Tomislav Stankovic
  • 3,080
  • 17
  • 35
  • 42
  • Thanks for your reply, but is the native ionic storage not better then localstorage ? (This is what I read from different source and that's the problem, there are a lot of sources and all tell them differently :) – PostMans Jan 04 '18 at 15:35
  • Matter of personal choice. In your case Ionic Storage and Local Storage are very similar, almost the same. I will add one more thing to the above answer. – Tomislav Stankovic Jan 04 '18 at 18:51
  • Thanks, I tried your code and it works, except the first time I access the page. Then the token is undefined. – PostMans Jan 05 '18 at 08:57
  • At which point you are doing `setItem`? Right after login/moment you get token or later? – Tomislav Stankovic Jan 05 '18 at 13:43
  • The token get set after the response of API. When I do a ionic serve (again), the token is the first time unidentifiied even its set in storage. However I must say that I didn't experienced this issue again. Maybe a caching issue with ionic serve ? – PostMans Jan 08 '18 at 08:48
  • If you didn't experienced this again then maybe there was some temporary caching issue. – Tomislav Stankovic Jan 10 '18 at 18:42
  • @TomislavStankovic, Can you please explain how the interceptors are invoked from the http call? Can you please share some code snippet? – Arj 1411 Jun 19 '18 at 11:49
  • @PostMans im having the same problem where the first time it is undefined. How did you end up fixing your issue? – Mans Jun 29 '18 at 19:19
  • @AnandRaj It's invoked inside component that uses that `TokenInterceptor` service. For example: `getData(){ this.TokenInterceptor.intercept().subscribe(res => { console.log(res); }); }` – Tomislav Stankovic Jun 29 '18 at 19:48
  • @AnandRaj, just noticed your reply but glad it has been solved ! – PostMans Jul 02 '18 at 07:54
4

Caching the token in the interceptor is a bad idea because if the token changes the interceptor will not be aware of those changes.

// Don't do this.
token: string;
constructor(private storage: Storage) {
  this.storage.get('token').then((res) => {
     this.token = res;
  })
}

If you want to use Ionic Storage and the interceptor together you can do so by using Observable.flatMap like so...

app.module.ts

providers: [
    { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true},
    SecurityStorageService
]

AuthInterceptor.ts

@Injectable()
export class AuthInterceptor implements HttpInterceptor {

constructor(
  private securityStorageService: SecurityStorageService
) {}

 intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    // This method gets a little tricky because the security token is behind a   
    // promise (which we convert to an observable). So we need to concat the 
    // observables.
    //  1. Get the token then use .map to return a request with the token populated in the header.
    //  2. Use .flatMap to concat the tokenObservable and next (httpHandler)
    //  3. .do will execute when the request returns
    const tokenObservable = this.securityStorageService.getSecurityTokenAsObservable().map(token => {
      return request = request.clone({
        setHeaders: {
          Authorization: `Bearer ${token}`
        }
      });
    });

    return tokenObservable.flatMap((req) => {
      return next.handle(req).do((event: HttpEvent<any>) => {
        if (event instanceof HttpResponse) {
          // do stuff to the response here
        }
      }, (err: any) => {
        if (err instanceof HttpErrorResponse) {
          if (err.status === 401) {
            // not authorized error .. do something
          }
        }
      });
    })
  }

security-storage-service.ts

You technically don't need this service, but you shouldn't have Ionic Storage logic in your interceptor.

@Injectable()
export class SecurityStorageService {

  constructor(private storage: Storage) {

  }

  getSecurityToken() {
    return this.storage.get(StorageKeys.SecurityToken)
      .then(
      data => { return data },
      error => console.error(error)
    );
  }
  getSecurityTokenAsObservable() {
    return Observable.fromPromise(this.getSecurityToken());
  }
}

storage-keys.ts

export class StorageKeys {  
  public static readonly SecurityToken: string = "SecurityToken";
}
Ryan E.
  • 977
  • 8
  • 16