Search code examples
angularangular7angular-http-interceptors

Using a promise to return a request object from an HTTP interceptor


I have been scratching my head at this for hours and I am hoping someone can help me and guide me. So, I am developing an Angular 7 application authentication module. One of the requirements is to develop an HTTP interceptor to add an Authorization (JWT) token and also to handle all error messages.

I am using an NPM package to handle localstorage of the tokens. This package uses set and get methods to store and returns a promise rather than the actual value of the token.

Now, my problem is in my interceptor function as we can see below. I have tried to comment where I am getting stuck.

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

    // Trying to get the token here but this returns a promise
    // this.token is a service for managing storage and retrieving of tokens
    const token = this.token.getToken();

    // If token is got, set it in the header
    // But when i console log, i see [object promise] other than the token
    if (token) {
        request = request.clone({
            headers: request.headers.set('Authorization', 'Bearer ' + token)
        });
    }

    return next.handle(request).pipe(catchError(err => {
        // Logs out the user if 401 error
        if (err.status === 401) {
            this.token.remove()
                .then(() => {
                    this.auth.changeAuthStatus(false);
                    this.router.navigateByUrl('/login');
                });
        }

        // Returns the error message for the user to see
        // for example in an alert
        const error = err.error.message || err.statusText;
        return throwError(error);
    }));
}

I hope I have well explained this problem. I have tried using async before the interceptor function but I get a red nasty error saying TS1055: Type 'typeof Observable' is not a valid async function return type in ES5/ES3 because it does not refer to a Promise-compatible constructor value.   Types of parameters 'subscribe' and 'executor' are incompatible..

I will appreciate any help towards fixing this issue.

Thank you!


Solution

  • to incorporate asynchronous processing into your interceptor, you want to promote your promise to an observable, and switchMap your observables together, returning the proper request:

    import { from as observableFrom } from "rxjs";
    import { switchMap } from "rxjs/operators";
    
    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        return observableFrom(this.token.getToken()).pipe(
            switchMap(token => {
    
                // do something with your promise-returned token here
    
                return next.handle(request).pipe(catchError(err => {
                    // Logs out the user if 401 error
                    if (err.status === 401) {
                        this.token.remove()
                            .then(() => {
                                this.auth.changeAuthStatus(false);
                                this.router.navigateByUrl('/login');
                            });
                    }
    
                    // Returns the error message for the user to see
                    // for example in an alert
                    const error = err.error.message || err.statusText;
                    return throwError(error);
                }));
            })
        );
    }
    

    have not tested this code directly, so i apologize for any typos, but it should get you where you want to go.

    1) promote your promise to an observable with from

    2) chain your observables with switchMap

    i notice you're not actually using the token returned in your example, you would do that in the function within switchMap that receives the result of the promise