Angular库集成MSAL遇uninitialized_public_client_application错误排查
解决MSAL调用handleRedirectPromise时的"uninitialized_public_client_application"错误
我是一名初级开发者,正在为公司所有未来的Angular应用开发通用Angular库,目前遇到调用handleRedirectPromise时触发"uninitialized_public_client_application"错误,排查多时且参考过相关帖子仍未解决,库的封装特性增加了排查难度。
测试应用的app.module.ts代码
import { APP_INITIALIZER, NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; import { NgxSenateModule, MSAL_CONFIG_TOKEN, MsalAuthService } from 'ngx-senate'; import { HeaderComponent } from './header/header.component'; import { HomeComponent } from './home/home.component'; import { SecondBestLayoutComponent } from './second-best-layout/second-best-layout.component'; import { environment } from 'src/environments/environment'; export function msalInitializer(msalAuthService: MsalAuthService): Function { return () => msalAuthService.initialize(); } @NgModule({ declarations: [ AppComponent, HeaderComponent, HomeComponent, SecondBestLayoutComponent ], imports: [ BrowserModule, AppRoutingModule, NgxSenateModule ], providers: [ { provide: MSAL_CONFIG_TOKEN, useValue: environment.msalConfig }, { provide: APP_INITIALIZER, useFactory: msalInitializer, deps: [MsalAuthService], multi: true } ], bootstrap: [AppComponent] }) export class AppModule { }
库中msal-auth.service.ts相关代码
此处的EventEmitter用于确保路由守卫在MSAL服务初始化完成前不会执行,调用handleRedirectPromise时触发错误:
import { Injectable, Inject, EventEmitter } from '@angular/core'; import { MSAL_CONFIG_TOKEN, MsalConfig } from './msal-config.token'; import { PublicClientApplication, LogLevel, AuthenticationResult, InteractionRequiredAuthError, Configuration, } from '@azure/msal-browser'; @Injectable({ providedIn: 'root' }) export class MsalAuthService { private msalInstance?: PublicClientApplication; initialized = new EventEmitter<boolean>(); constructor(@Inject(MSAL_CONFIG_TOKEN) private msalConfig: MsalConfig) { } async initialize(): Promise<void> { console.log("Initializing MSAL..."); const msalConfig: Configuration= { auth: { clientId: this.msalConfig.clientId, authority: this.msalConfig.authority, redirectUri: this.msalConfig.redirectUri || window.location.origin, postLogoutRedirectUri: this.msalConfig.postLogoutRedirectUri || window.location.origin, }, cache: { cacheLocation: "localStorage", storeAuthStateInCookie: false, }, system: { loggerOptions: { loggerCallback: (level, message, containsPii) => { console.log(message); }, piiLoggingEnabled: false, logLevel: LogLevel.Verbose, }, }, }; const msalInstance = new PublicClientApplication(msalConfig); msalInstance .handleRedirectPromise() .then((tokenResponse) => { console.log("Msal Authenticated!"); this.initialized.emit(true); }) .catch((error) => { console.log("MSal authentication failed."); console.log(error); this.initialized.emit(false); }) }
排查过程
编辑1:尝试添加实例初始化等待
试着给MSAL实例添加await initialize():
const msalInstance = new PublicClientApplication(msalConfigSettings); await msalInstance.initialize();
但此时路由守卫卡在等待MsalAuthService发送initialized事件的环节,守卫代码如下:
export const canActivate: CanActivateFn = async (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => { let config: AuthGuardConfig | null = null; try { config = inject(AUTH_GUARD_CONFIG_TOKEN); } catch { console.log("No provider for AUTH_GUARD_CONFIG_TOKEN.") } const msalService: MsalAuthService = inject(MsalAuthService); // Ensure the MSAL service is initialized before proceeding. await firstValueFrom(msalService.initialized);
编辑2:最终解决方案
发现问题源于一个低级疏忽:MsalAuthService中创建的MSAL实例是局部const变量,而非类的成员变量this.msalInstance,导致实例创建后没有被持久化到服务类中,后续操作无法访问到正确初始化的实例。
只需修改一行代码:
将
const msalInstance = new PublicClientApplication(msalConfig);
替换为
this.msalInstance = new PublicClientApplication(msalConfig);
修改后,"uninitialized_public_client_application"错误消失,路由守卫也能正常接收到初始化完成的事件。
内容的提问来源于stack exchange,提问作者Low-Signal
相关产品推荐
相关产品推荐

