Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular HTTP Interceptor subscribing to observable and then returning next.handle but throwing TypeError: You provided 'undefined'

I have a HTTP interceptor and before every request I check if the access token is expired, if it is, I subscribe to a http.post call from my service and then subscribe to it and when I get a new access token I call next.handle(request) like this:

        this.auth.refreshAccessToken().subscribe((token: string) => {
          this.auth.newAccessToken = token;
          request = request.clone({
            setHeaders: {
              Authorization: `Bearer ${token}`
            }
          });
          return next.handle(request);
        });

The issue is then it is throwing TypeError: You provided 'undefined' where a stream was expected. You can provide an Observable, Promise, Array, or Iterable.

Which makes me think I'm making that http.post call wrong right there.

EDIT 1: I haven't had the chance to test this thoroughly but so far it seems that everything works. I had a console.log before returning the whole map but it didn't fire, however, everything else worked and I update the currentUser everywhere/permissions everytime I get a new access token and that DID happen, so for all intents and purposes it seems to work, here's the updated code:

          mergeMap(token => {
            this.auth.newAccessToken = token;
            request = request.clone({
              setHeaders: {
                Authorization: `Bearer ${token}`
              }
            });
            return next.handle(request);
          })
like image 417
SebastianG Avatar asked Apr 16 '19 15:04

SebastianG


1 Answers

You would not subscribe() inside the interceptor, instead you would return an Observable<HttpEvent<any>>. This can be done by utilizing RxJS pipeable operators ('rxjs/operators') such as tap (for side effects like setting newAccessToken) and switchMap or mergeMap in combination with pipe(), returning an observable of type Observable<HttpEvent<any>> to satisfy the HttpInterceptor interface:

import { Injectable } from '@angular/core';
import {
  HttpEvent, HttpInterceptor, HttpHandler, HttpRequest
} from '@angular/common/http';
import { Observable } from 'rxjs';
import { switchMap, tap } from 'rxjs/operators';
import { AuthService } from './auth.service';

@Injectable()
export class AuthInterceptor implements HttpInterceptor {
  constructor(private auth: AuthService) { }

  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return this.auth.refreshAccessToken().pipe(
      tap(token => this.auth.newAccessToken = token), // side effect to set token property on auth service
      switchMap(token => { // use transformation operator that maps to an Observable<T>
        const newRequest = request.clone({
          setHeaders: {
            Authorization: `Bearer ${token}`
          }
        });

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

Here is an example in action. Please check out the logs to see key information being logged out.

Hopefully that helps!

like image 139
Alexander Staroselsky Avatar answered Oct 13 '22 03:10

Alexander Staroselsky