Configuration must be specified either through AuthModule.forRoot or through AuthClientConfig.set
Asked Answered
H

1

0

I'm using NX to create an Angular security library so that I don't have all my security configured in one location. I'm using NX 16, Angular 16, and Auth0. I'm trying to pass in the Autho0 configurations via forRoot, but, I get the error described in the title. Below is a snippet of my code.

export const OPTIONS = new InjectionToken<string>('OPTIONS');

export interface SecurityModuleOptions {
  production: boolean,
  auth0Config: {
    domain: string,
    clientId: string,
    audience: string,
    useRefreshTokens: boolean,
    scope: string
  },
  httpInterceptor: {
    allowedList: string[]
  },
  redirectUri: string
}

export const initialize = (
  options: SecurityModuleOptions,
  apollo: Apollo,
  httpLink: HttpLink,
  authService: AuthService,
  authClientConfig: AuthClientConfig
) => {
  authClientConfig.set(options.auth0Config);
  return authService.isAuthenticated$
    .pipe(
      filter<boolean>((isAuthenticated) => isAuthenticated),
      mergeMap(() => authService.getAccessTokenSilently()
        .pipe(
          map((token: string) => {
            if (!token) {
              return;
            }

            const context = {
              headers: {
                Authorization: `Bearer ${ token }`
              }
            };
            const http = httpLink.create({ uri: '/graphql' });
            const ws = new WebSocketLink({
              uri: '/sockets',
              options: { reconnect: true }
            });
            const link = split(
              // split based on operation type
              ({ query }) => {
                const { kind, operation } = getMainDefinition(query) as OperationDefinitionNode;
                return kind === 'OperationDefinition' && operation === 'subscription';
              },
              ws,
              http
            );
            apollo.create({
              link: ApolloLink.from([setContext(() => context), link]),
              cache: new InMemoryCache({ addTypename: false }),
              connectToDevTools: !options.production
            });
          })
        )
      )
    );
};

@NgModule({
  imports: [
    CommonModule,
    AuthModule.forRoot()
  ],
  exports: [AuthModule]
})
export class SecurityModule {
  public static forRoot(options: SecurityModuleOptions): ModuleWithProviders<SecurityModule> {
    return {
      ngModule: SecurityModule,
      providers: [
        {
          provide: OPTIONS,
          useValue: options
        },
        {
          provide: APP_INITIALIZER,
          useFactory: initialize,
          deps: [OPTIONS, Apollo, HttpLink, AuthService, AuthClientConfig],
          multi: true
        },
        {
          provide: HTTP_INTERCEPTORS,
          useClass: AuthHttpInterceptor,
          multi: true
        },
        {
          provide: Window,
          useValue: window
        }
      ]
    };
  }
}

I'm calling the security library in one of the NX apps.

@NgModule({
  declarations: [AppComponent],
  imports: [
    SecurityModule.forRoot(environment),
  ],
  bootstrap: [AppComponent]
})
export class AppModule {}

Is there any other way of doing this? Below is a screenshot of the error I'm getting. Thanks in advance for any help.

enter image description here

Hotbox answered 23/6, 2023 at 2:19 Comment(0)
P
2

The error you are seeing is caused by the fact that AuthService is being instantiated before it received a configuration through either:

  • AuthModule.forRoot()
  • AuthClientConfig.set(options)

As you can see AuthService takes a dependency on Auth0ClientService, which runs the factory which on its turn injects AuthClientConfig and checks if there is a config.

You are trying to use AuthClientConfig.set, but your Initializer has a dependency on AuthService, so it already needs the config to be able to instantiate AuthService.

Typically, I would say anything that relies on the authentication state should move out of the app initializer, which would include the pretty much your entire initializer.

Another way would be to use Injector as a dependency on your initializer, and have it create the instance of AuthService after you called AuthClientConfig.set. However, I wouldn't recommend doing this, but instead move the majority outside of the initializer.

Process answered 27/6, 2023 at 15:36 Comment(1)
This got me figured out my issue. It was one of our Effects that has a dependency on AuthService. Turns out EffectsModule.forRoot is being triggered first before APP_INITIALIZER, but it only happens after I upgrade the following: angular 13.x to 14.x @auth0/angular 1.7.0 to 1.11.1 @ngrx/effects 12.x to 14.x The detailed issue was explained here: github.com/ngrx/platform/issues/931#issuecomment-376133976 The sample solution was provided here: github.com/brandonroberts/effects-issue-exampleMistassini

© 2022 - 2024 — McMap. All rights reserved.