angular

HTTP_INTERCEPTORS and getting data from Youtube conflicting


My environment & features of my application/project: Angular 19 and .Net Backend, getting videos data from Youtube and login and register feature with http_interceptors.

My problem or my issue: my query api to Youtube is not working if i am using http_interceptors in main.ts. This is how I test it: I comment the http_interceptors then my query api is working as it supposed to.

my http_interceptor in main.ts:

{
  provide: HTTP_INTERCEPTORS,
  useClass: JwtInterceptor,
  multi: true
},

my error from get HTTP and subscription from Youtube:

{
    "headers": {
        "normalizedNames": {
        },
        "lazyUpdate": null
    },
    "status": 401,
    "statusText": "OK",
    "url": "https://www.googleapis.com/youtube/v3/playlistItems?key=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&part=snippet&maxResults=12&playlistId=PLPvjlyTckx7OhGYE9JZw9Q1MYP_n-u-8V",
    "ok": false,
    "name": "HttpErrorResponse",
    "message": "Http failure response for https://www.googleapis.com/youtube/v3/playlistItems?key=xxxxxxxxxxxxxxxxxxxxxxxx&part=snippet&maxResults=12&playlistId=PLPvjlyTckx7OhGYE9JZw9Q1MYP_n-u-8V: 401 OK",
    "error": {
        "error": {
            "code": 401,
            "message": "Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.",
            "errors": [
                {
                    "message": "Invalid Credentials",
                    "domain": "global",
                    "reason": "authError",
                    "location": "Authorization",
                    "locationType": "header"
                }
            ],
            "status": "UNAUTHENTICATED"
        }
    }
}

my JwtInterceptor code in component:

export class JwtInterceptor implements HttpInterceptor {
  private accountService = inject(AccountService);


  intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
    this.accountService.user$.pipe(take(1)).subscribe({
      next: user => {
        if (user) {
          // Clone from the coming request and add Authorization header to that
          request = request.clone({
            setHeaders: {
              Authorization: `Bearer ${user.jwt}`
            }
          });
        }
      }
    })
    return next.handle(request);
  }
}

My question is: How I am to going disable the interceptor when I try to access or get data from Youtube?

Thanks.


Solution

  • You can modify your interceptor to return early if the request isn't headed for a domain that needs your JWT.

    Here I've used a simple regex to check against a single domain, but any test to confirm whether or not you know the target URL requires the JWT would work:

    export class JwtInterceptor implements HttpInterceptor {
      private accountService = inject(AccountService);
      private readonly myDomainPattern = /^http(s)?:[\/\\]{2}example.com[\/\\].*/i;
    
      intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
    
        // If this request isn't going to our API, we don't need
        // to append our JWT. Third party services won't need it.
        if (!myDomainPattern.test(req.url)) {
          return next.handle(request);
        }
    
        // Otherwise, continue adding our JWT as usual.
        this.accountService.user$.pipe(take(1)).subscribe({
          next: user => {
            if (user) {
              // Clone from the coming request and add Authorization header to that
              request = request.clone({
                setHeaders: {
                  Authorization: `Bearer ${user.jwt}`
                }
              });
            }
          }
        })
        return next.handle(request);
      }
    }
    

    Side note, you might run into timing issues with your interceptor because you're returning after subscribing, not after the user$ observable emits.

    You can return a composed observable that will delay executing next.handle() until user$ has emitted:

    // Otherwise, continue adding our JWT as usual.
    return this.accountService.user$.pipe(
      take(1),
      switchMap(user => {
        if (user) {
          request = request.clone({
            setHeaders: {
              Authorization: `Bearer ${user.jwt}`,
            }
          });
        }
        return next.handle(request);
      }));