I went through several questions like 1 , 2 but I don't know how to make my app work.
Problem: When I sign in 1st time, I do not get Bearer token and hence my SettingConfigService fails with 401, if I refresh the page, I get the token from this.oauth.getAccessToken() because now the token is in localstorage.
I am using oauth lib for login. Here is the modules and libs I have created.
App.module
export function AppConfigurationFactory(config: SettingConfigService) { return async () => await config.ensureInit(APP_NAME); } export class AppConfig { baseUrl: string; production: boolean; } export const appConfig: AppConfig = { baseUrl: environment.baseUrl, production: environment.production, }; @NgModule({ exports: [], declarations: [ ], imports: [ .... CustomAuthModule.forRoot(environment.keycloak), CustomInfrastructureModule.forRoot({ appConfig }), SharedModule, ], providers: [ { provide: AppConfig, useValue: appConfig }, ... { provide: APP_INITIALIZER, useFactory: AppConfigurationFactory, deps: [ SettingConfigService, HttpClient, TranslateService, OAuthService], multi: true, }, ], }) export class AppModule {} CustomAuthModule.ts
import { NgModule, APP_INITIALIZER, Optional, SkipSelf, ModuleWithProviders, InjectionToken } from '@angular/core'; import { HttpClientModule } from '@angular/common/http'; import { OAuthModule, AuthConfig } from 'angular-oauth2-oidc'; import { OAuthModuleConfig,CustomAuthConfigParams } from './auth.config'; import { AuthConfigService } from './auth.service'; export function init_app(authConfigService: AuthConfigService) { return () => authConfigService.initAuth(); } @NgModule({ imports: [HttpClientModule, OAuthModule.forRoot()] }) export classCustomAuthModule { constructor(@Optional() @SkipSelf() parentModule:CustomAuthModule){ if(parentModule){ throw new Error('QontrolAuthModule is already loaded.'); } } static forRoot(keycloakParams): ModuleWithProviders<QontrolAuthModule> { return { ngModule:CustomAuthModule, providers: [ AuthConfigService, OAuthModuleConfig, { provide: AuthConfig, useValue: keycloakParams }, { provide: APP_INITIALIZER, useFactory: init_app, deps: [AuthConfigService], multi: true, }, ] } } } CustomInfrastrucutureModule.ts
@NgModule({ declarations: [], imports: [CommonModule], exports: [], providers: [], }) export class CustomInfrastructureModule { static forRoot(conf?: { appConfig: SharedInfrastructureAppConfig; }): ModuleWithProviders<CustomInfrastructureModule> { return { ngModule: CustomInfrastructureModule, providers: [ { provide: APP_CONFIG, useValue: conf.appConfig }, { provide: LOCALE_ID, deps: [SettingConfigService], // some service handling global settings useFactory: (config: SettingConfigService) => config.culture }, ], }; } } SettingConfigService
@Injectable({providedIn: 'root'}) export class SettingConfigService { culture: string; config: any; constructor( private httpClient: HttpClient, @Inject(APP_CONFIG) protected appConfig: SharedInfrastructureAppConfig, private oauth: OAuthService ) { } async ensureInit(clientPrefix: string): Promise<void>{ console.log(this.oauth.getAccessToken()); //<-- comes as null when 1st login // putting a wait time of 1 sec as per https://stackoverflow.com/questions/951021/what-is-the-javascript-version-of-sleep makes it work, // because by that time, we have the token in localStorage const response = await this.httpClient.get<any>(`${this.appConfig.baseUrl}/configs`).toPromise(); this.config = response; } } Here is my code which fetched the token using oauth2-oidc which is called from
AuthConfigService
async initAuth(): Promise<any> { return new Promise((resolveFn, rejectFn) => { this.oauthService.configure(this.authConfig); // Redirect to path, if there is one if (window && window.location && window.location.pathname) { this.oauthService.redirectUri = window.location.origin + window.location.pathname; } this.oauthService.setStorage(localStorage); this.oauthService.tokenValidationHandler = new NullValidationHandler(); this.oauthService.events .pipe( filter((e: any) => { return e.type === 'token_received'; }) ) .subscribe(() => this.handleNewToken() // <-- this takes time to get triggered and meanwhile // the call to SettingConfigService is made ); this.oauthService.loadDiscoveryDocumentAndLogin().then((isLoggedIn) => { if (isLoggedIn) { this.oauthService.setupAutomaticSilentRefresh(); resolveFn(() => {}); } else { console.log(this.oauthService.getAccessToken()) this.oauthService.initImplicitFlow(); console.log(this.oauthService.getAccessToken()) rejectFn(); } }); }); } In short,
I need to synchronize APP_INITIALIZER of app.module.ts to wait for token of APP_INITIALIZER of CustomAuthModule, and then it'll have a bearer token (which gets added by interceptor). Is my understanding correct? Please help