I am a junior developer creating a angular library for use across all of my orginizations future angular applications. My issue is I am getting "uninitialized_public_client_application" when my handleRedirectPromise fires. I have been working on this issue for longer than I care to admit. I have seen some similar post but they haven't seemed to work and it being a library has added a bit of extra complexity.
This is my test applications 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 { }
This is the relevant part of my msal-auth.service.ts in my library, an error is triggered when calling handleRedirectPromise. The emitters here are for my auth-guard to make sure it does not try to start until the initialization of the msal service has completed. Please let me know if you need any farther information!
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);
})
}
Edit: It seems that I needed to await the instance like this :
const msalInstance = new PublicClientApplication(msalConfigSettings);
await msalInstance.initialize();
Unfortunately my guard now gets stuck awaiting for the msalservice to emit a value here:
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);
Edit 2:
This issue was due to an oversight on my part, the msal-service was using a const instance instead of a the class variable. Changing
const msalInstance = new PublicClientApplication(msalConfig);
to:
this.msalInstance = new PublicClientApplication(msalConfig);
Fixed the issue.
Thanks @Low-Signal for sharing the solution in Qs and comment. Posting it as an answer for the benefit of community:
The error message "uninitialized_public_client_application" indicates that the MSAL PublicClientApplication instance has not been initialized before calling the
handleRedirectPromise
method. This can happen if you callhandleRedirectPromise
before theinitialize
method has finished executing.Using the below code, I am able to use the Angular MSAL Library:
Output: