microsoft-authentication-library-for-js: Angular 16: BrowserAuthError: uninitialized_public_client_application: You must call and await the initialize function before attempting to call any other MSAL API.

Core Library

MSAL.js (@azure/msal-browser)

Core Library Version

3.0.8

Wrapper Library

MSAL Angular (@azure/msal-angular)

Wrapper Library Version

None

Public or Confidential Client?

Public

Description

I am trying to authenticate user during the page load it self because, i want to authenticate user with out any button click. To achieve that i was trying to use loginRedirect in onInit life cycle hook of app.component.ts file, When i was trying to do like that i am facing an issue

BrowserAuthError: uninitialized_public_client_application: You must call and await the initialize function before attempting to call any other MSAL API.

Is there any other way to authenticate user with out any button click

Error Message

BrowserAuthError: uninitialized_public_client_application: You must call and await the initialize function before attempting to call any other MSAL API. For more visit: aka.ms/msaljs/browser-errors BrowserAuthError: uninitialized_public_client_application: You must call and await the initialize function before attempting to call any other MSAL API. For more visit: aka.ms/msaljs/browser-errors

Msal Logs

No response

MSAL Configuration

{
auth: {
      clientId: environment.msalConfig.auth.clientId,
      authority: environment.msalConfig.auth.authority,
      redirectUri: window.location.origin,
      postLogoutRedirectUri: '/'
    },
    cache: {
      cacheLocation: BrowserCacheLocation.LocalStorage
    },
}

Relevant Code Snippets

app.component.ts

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit, AfterViewInit, OnDestroy{
  title = 'sso-poc';
  loginDisplay = false;
  private readonly _destroying$ = new Subject<void>();
  constructor(private msalBroadcastService: MsalBroadcastService ,private authService: MsalService) {}
  ngOnDestroy(): void {
    this._destroying$.next(undefined);
    this._destroying$.complete();
  }
  ngAfterViewInit(): void {
   
  }
  //AD auth
  ngOnInit(): void {
    this.authService.loginRedirect();  
   
      this.msalBroadcastService.inProgress$
      .pipe(
        filter((status: InteractionStatus) => status === InteractionStatus.None),
        takeUntil(this._destroying$)
      )
      .subscribe(() => {
        this.setLoginDisplay();
        this.checkAndSetActiveAccount();
      })
  }

  checkAndSetActiveAccount(){
    /**
     * If no active account set but there are accounts signed in, sets first account to active account
     * To use active account set here, subscribe to inProgress$ first in your component
     * Note: Basic usage demonstrated. Your app may require more complicated account selection logic
     */
    let activeAccount = this.authService.instance.getActiveAccount();

    if (!activeAccount && this.authService.instance.getAllAccounts().length > 0) {
      let accounts = this.authService.instance.getAllAccounts();
      this.authService.instance.setActiveAccount(accounts[0]);
    }
  }


  loginRedirect() {
    this.authService.loginRedirect()
  }

  setLoginDisplay() {
    this.loginDisplay = this.authService.instance.getAllAccounts().length > 0;
  }

}
app.module.ts
export function MSALInstanceFactory(): IPublicClientApplication {
  return new PublicClientApplication({
    auth: {
      clientId: environment.msalConfig.auth.clientId,
      authority: environment.msalConfig.auth.authority,
      redirectUri: window.location.origin+"/login",
      postLogoutRedirectUri: window.location.origin+"/redirect-auth",
      navigateToLoginRequestUrl: true
    },
    cache: {
      cacheLocation: BrowserCacheLocation.LocalStorage,
    },
    system: {
      allowNativeBroker: false
    }
  })
}

  /**
   * Configuring the AAD login type and api related permissions
   * @returns AAD Guard configuration
   */
  export function MSALGuardConfigFactory(): MsalGuardConfiguration {
    return {
      interactionType: InteractionType.Redirect,
      authRequest: {
        scopes: [...environment.apiConfig.scopes]
      },
      loginFailedRoute: '/'
    }
  }

/**
 * Interceptor configuration for the API calls
 * @returns 
 */

export function MSALInterceptorConfig(): MsalInterceptorConfiguration {
  const protectedResourceMap = new Map<string, Array<string>>();
  protectedResourceMap.set(environment.apiConfig.uri, environment.apiConfig.scopes)
  return {
    interactionType: InteractionType.Redirect,
    protectedResourceMap
  }
}

@NgModule({
  declarations: [
    AppComponent,
    
  ],
  imports: [
    BrowserModule,
    MsalModule,
    
    AppRoutingModule,
    HttpClientModule,
    FormsModule,
    ReactiveFormsModule,
    NoopAnimationsModule ,
  
  ],
  providers: [
    {
      provide: HTTP_INTERCEPTORS,
      useClass: MsalInterceptor,
      multi: true
    },
    {
      provide: MSAL_INSTANCE,
      useFactory: MSALInstanceFactory,
    },
    {
      provide: MSAL_GUARD_CONFIG,
      useFactory: MSALGuardConfigFactory
    },
    {
      provide: MSAL_INTERCEPTOR_CONFIG,
      useFactory: MSALInterceptorConfig
    },
    MsalService,
    MsalGuard,
    MsalConfigService
  ],
  bootstrap: [AppComponent, MsalRedirectComponent],
  schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
export class AppModule {
}
index.html
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>SsoPoc</title>
  <base href="/">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
  <app-root></app-root>
  <app-redirect></app-redirect>
</body>
</html>

package.json
{
  "name": "sso-poc",
  "version": "0.0.0",
  "scripts": {
    "ng": "ng",
    "start": "ng serve",
    "build": "ng build",
    "watch": "ng build --watch --configuration development",
    "test": "ng test"
  },
  "private": true,
  "dependencies": {
    "@angular/animations": "^16.1.0-next.0",
    "@angular/common": "^16.1.0-next.0",
    "@angular/compiler": "^16.1.0-next.0",
    "@angular/core": "^16.1.0-next.0",
    "@angular/forms": "^16.1.0-next.0",
    "@angular/platform-browser": "^16.1.0-next.0",
    "@angular/platform-browser-dynamic": "^16.1.0-next.0",
    "@angular/router": "^16.1.0-next.0",
    "@azure/msal-angular": "^3.0.8",
    "@azure/msal-browser": "^3.5.0",
    "rxjs": "~7.8.0",
    "tslib": "^2.3.0",
    "zone.js": "~0.13.0"
  },
  "devDependencies": {
    "@angular-devkit/build-angular": "^16.1.0-next.0",
    "@angular/cli": "~16.1.0-next.0",
    "@angular/compiler-cli": "^16.1.0-next.0",
    "@types/jasmine": "~4.3.0",
    "jasmine-core": "~4.6.0",
    "karma": "~6.4.0",
    "karma-chrome-launcher": "~3.2.0",
    "karma-coverage": "~2.2.0",
    "karma-jasmine": "~5.1.0",
    "karma-jasmine-html-reporter": "~2.0.0",
    "typescript": "~5.0.2"
  }
}

Reproduction Steps

  1. Run ng serve in terminal

Expected Behavior

  • User should authenticate successfully with out any button click or once web page was loaded successfully .
  • User should navigate to next page once authentication was done

Identity Provider

Azure AD / MSA

Browsers Affected (Select all that apply)

Chrome, Firefox, Edge

Regression

No response

Source

Internal (Microsoft)

About this issue

  • Original URL
  • State: closed
  • Created 8 months ago
  • Reactions: 1
  • Comments: 20 (4 by maintainers)

Most upvoted comments

I am calling authService.initialize() (not awaited) in the constructor of the app.component.ts. It returns an Observable<void> object and I wonder if there should be a callback function in subscribe, to handle what should happen when the initialization is done.

I have the exact same problem. I have looked at the sample application code: https://github.com/AzureAD/microsoft-authentication-library-for-js/tree/dev/samples/msal-angular-v3-samples/angular16-sample-app, but I cannot find anywhere in this sample code where the public client application is initialized. If i initialize it in the app.module.ts, I get an error because the MSALInstanceFactory method is async, which it is not in your sample code.

@lalimasharda , I tried like this but i am facing the issue called **BrowserAuthError: interaction_in_progress: Interaction is currently in progress. Please ensure that this interaction has been completed before calling an interactive API.**

Since iā€™m using the RedirectComponent istead of handleRedirectObservable i made the changes like this in app.component.ts

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss'],
  
})
export class AppComponent implements AfterViewInit, OnInit, OnDestroy {
 
  constructor(private router:Router,  @Inject(MSAL_GUARD_CONFIG) private msalGuardConfig: MsalGuardConfiguration,
  private msalbroadCastService: MsalBroadcastService,
  private authService: MsalService,) {}
  ngOnDestroy(): void {
  }

  ngAfterViewInit(): void {

  }

  async ngOnInit() {
    await this.authService.instance.initialize();
    await this.authService.instance.loginRedirect();
  }
}

Error i am getting:

**BrowserAuthError: interaction_in_progress: Interaction is currently in progress. Please ensure that this interaction has been completed before calling an interactive API.   For more visit: aka.ms/msaljs/browser-errors
BrowserAuthError: interaction_in_progress: Interaction is currently in progress. Please ensure that this interaction has been completed before calling an interactive API.   For more visit: aka.ms/msaljs/browser-errors**

Could you please let me know what am i missing here šŸ˜ƒ