charliebrownie
charliebrownie

Reputation: 6097

HttpInterceptor provided in Angular library is not working from Angular app

I am developing an Angular library where there is an authentication module that provides an HttpInterceptor. The main idea is to have this interceptor working automatically in any app that imports this authentication module without having to do any extra setup at it.

What I have so far is the following:

AuthenticationModule

@NgModule({
  imports: [ConcreteAuthModule],
  providers: [
    {
      provide: HTTP_INTERCEPTORS,
      useClass: BearerInterceptor,
      multi: true
    }
  ]
})
export class AuthenticationModule {
  static forRoot(config: AuthConfig): ModuleWithProviders {
    return {
      ngModule: AuthenticationModule,
      providers: [
        {
          provide: AUTH_CONFIG,
          useValue: config
        }
      ]
    };
  }
}

ConcreteAuthModule

@NgModule({
  imports: [ThirdPartyLibModule],
  providers: [
    {
      provide: AuthenticationService,
      useClass: ConcreteAuthService
    }
  ]
})
export class ConcreteAuthModule { }

BearerInterceptor

@Injectable()
export class BearerInterceptor implements HttpInterceptor {
  constructor(private authService: AuthenticationService) { }

  intercept(
    req: HttpRequest<any>,
    next: HttpHandler
  ): Observable<HttpEvent<any>> {
    const headers: any = {};

    if (this.authService.isUserAuthenticated()) {
      headers.Authorization = `Bearer ${this.singleSignOnService.getUserToken()}`;
    }

    const authReq = req.clone({ setHeaders: headers });

    return next.handle(authReq);
  }
}

And from a test Angular app I am importing this module the following way at the AppModule:

@NgModule({
  declarations: [AppComponent],
  imports: [
    BrowserModule,
    HttpClientModule,
    AuthenticationModule.forRoot({ /* config stuff */ })
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule {}

I checked how some third party libraries did this, also came across a couple of Stack Overflow questions that discussed about this and they all suggested having an Angular module created (I already have it: AuthenticationModule), then provide the http-interceptor on it (already have it too) and finally importing this module from an Angular app (also did this).

But still, none of the http requests in my app are being intercepted.

Tried importing the BearerInterceptor directly from my test app and providing it on the AppModule like this:

import { BearerInterceptor } from 'my-lib':

@NgModule({
  declarations: [AppComponent],
  imports: [
    BrowserModule,
    HttpClientModule,
    AuthenticationModule.forRoot({ /* config stuff */ })
  ],
  providers: [
    {
      provide: HTTP_INTERCEPTORS,
      useClass: BearerInterceptor,
      multi: true
    }
  ],
  bootstrap: [AppComponent]
})
export class AppModule {}

And that works! But this workaround is not what I am looking for...

Upvotes: 5

Views: 3016

Answers (2)

charliebrownie
charliebrownie

Reputation: 6097

The problem was because I was installing the library locally for testing purposes like the following:

$ npm i --save my-lib_path/dist/my-lib

After I published it and installed it from the npm registry it worked fine:

$ npm i --save my-lib

Upvotes: 1

Reactgular
Reactgular

Reputation: 54741

You're very close to a working solution.

The key is to look at how the module is being imported by the AppModule.

  imports: [
    BrowserModule,
    HttpClientModule,
    AuthenticationModule.forRoot({ /* config stuff */ })
  ],

That is how the AuthenticationModule is imported by the AppModule, but that NgModule does not provide the HTTP_INTERCEPTORS.

You've provided the token in the @NgModule() decorator, but that module is not being used by your application. It's the module defined by the forRoot() function.

Move the declaration of the HTTP_INTERCEPTORS to the forRoot() function.

Try this instead:

@NgModule({
    imports: [ConcreteAuthModule]
})
export class AuthenticationModule {
    static forRoot(config: AuthConfig): ModuleWithProviders {
        return {
            ngModule: AuthenticationModule,
            providers: [
                {
                    provide: AUTH_CONFIG,
                    useValue: config
                }, {
                    provide: HTTP_INTERCEPTORS,
                    useClass: BearerInterceptor,
                    multi: true
                }
            ]
        };
    }
}

Upvotes: 4

Related Questions