import { APP_INITIALIZER, ModuleWithProviders, NgModule } from '@angular/core'; import { KeycloakAngularModule, KeycloakEventType, KeycloakOptions, KeycloakService } from 'keycloak-angular'; import { DefaultUserService } from './services/default-user.service'; import { IIqserUser } from './types/user.response'; import { IqserUserService } from './services/iqser-user.service'; import { BASE_HREF, ModuleOptions } from '../utils'; import { IqserUsersModuleOptions } from './types/iqser-users-module-options'; import { IqserUser } from './iqser-user.model'; import { IqserRoleGuard } from './guards/iqser-role-guard.service'; import { IqserAuthGuard } from './guards/iqser-auth-guard.service'; import { IqserConfigService } from '../services'; import { NamePipe } from './name.pipe'; import { InitialsAvatarComponent } from './components/initials-avatar/initials-avatar.component'; import { MatTooltipModule } from '@angular/material/tooltip'; import { CommonModule } from '@angular/common'; import { UserButtonComponent } from './components/user-button/user-button.component'; import { MatIconModule } from '@angular/material/icon'; import { MatLegacyButtonModule as MatButtonModule } from '@angular/material/legacy-button'; import { TranslateModule } from '@ngx-translate/core'; import { TenantContext, TenantContextHolder } from '../tenants'; import { filter, firstValueFrom, of, switchMap } from 'rxjs'; import { KeycloakStatus, KeycloakStatusService } from './services/keycloak-status.service'; import { map, tap } from 'rxjs/operators'; function getKeycloakOptions(baseUrl: string, tenantContextHolder: TenantContextHolder, configService: IqserConfigService): KeycloakOptions { console.log('keycloak config for: ', tenantContextHolder.currentTenant); return { config: { url: configService.values.OAUTH_URL, realm: tenantContextHolder.currentTenant, clientId: configService.values.OAUTH_CLIENT_ID, }, initOptions: { checkLoginIframe: false, onLoad: 'check-sso', silentCheckSsoRedirectUri: window.location.origin + baseUrl + '/assets/oauth/silent-refresh.html', flow: 'standard', }, enableBearerInterceptor: true, }; } function configureAutomaticRedirectToLoginScreen(keyCloakService: KeycloakService, keycloakStatusService: KeycloakStatusService) { keyCloakService.getKeycloakInstance().onAuthRefreshError = () => { keycloakStatusService.createLoginUrlAndExecute(); }; } export function keycloakInitializer( keycloakService: KeycloakService, configService: IqserConfigService, baseUrl: string, keycloakStatusService: KeycloakStatusService, tenantContext: TenantContext, tenantContextHolder: TenantContextHolder, ): () => Promise { const tenantsReady = tenantContext.tenantsReady$.pipe( filter(t => t), switchMap(() => { if (tenantContextHolder.currentTenant) { const x = keycloakService.init(getKeycloakOptions(baseUrl, tenantContextHolder, configService)); configureAutomaticRedirectToLoginScreen(keycloakService, keycloakStatusService); keycloakStatusService.updateStatus(KeycloakStatus.PENDING); return x; } else { keycloakStatusService.updateStatus(KeycloakStatus.NOT_ACTIVE); return of(true); } }), ); return () => firstValueFrom(tenantsReady); } export function keycloakStatusInitializer(keycloakService: KeycloakService, keycloakStatusService: KeycloakStatusService) { const pipe = keycloakStatusService.keycloakStatus$.pipe( filter(status => status === KeycloakStatus.PENDING || status === KeycloakStatus.NOT_ACTIVE), switchMap(status => { if (status === KeycloakStatus.NOT_ACTIVE) { return of(true); } else { return keycloakService.keycloakEvents$.pipe( filter(event => event.type === KeycloakEventType.OnReady), tap(() => keycloakStatusService.updateStatus(KeycloakStatus.READY)), map(() => true), ); } }), ); return () => firstValueFrom(pipe); } const components = [NamePipe, InitialsAvatarComponent, UserButtonComponent]; @NgModule({ imports: [KeycloakAngularModule, MatTooltipModule, CommonModule, MatIconModule, MatButtonModule, TranslateModule], declarations: [...components], exports: [...components], }) export class IqserUsersModule { static forRoot< Interface extends IIqserUser, Class extends IqserUser & Interface, UserService extends IqserUserService, RolesGuard extends IqserRoleGuard = IqserRoleGuard, >(options: IqserUsersModuleOptions): ModuleWithProviders { const userService = ModuleOptions.getService(IqserUserService, DefaultUserService, options.existingUserService); const roleGuard = ModuleOptions.getService(IqserRoleGuard, IqserRoleGuard, options.existingRoleGuard); return { ngModule: IqserUsersModule, providers: [ userService, roleGuard, IqserAuthGuard, { provide: APP_INITIALIZER, useFactory: keycloakStatusInitializer, multi: true, deps: [KeycloakService, KeycloakStatusService], }, { provide: APP_INITIALIZER, useFactory: keycloakInitializer, multi: true, deps: [KeycloakService, IqserConfigService, BASE_HREF, KeycloakStatusService, TenantContext, TenantContextHolder], }, ], }; } }