I am currently facing an issue with Azure AD (SSO) authentication using MSAL. Below are the details of my setup:
Angular: 14.3.0 msal-angular: 3.0.20 msal-browser: 3.17.0 I am encountering an initialization error loop with the MSAL client. The stack trace is as follows:
Error processing redirect 1: 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
at createBrowserAuthError (BrowserAuthError.mjs:269:12)
at blockAPICallsBeforeInitialize (BrowserUtils.mjs:112:37)
at StandardController.mjs:175:38
at Generator.next (<anonymous>)
at asyncGeneratorStep (asyncToGenerator.js:3:1)
at _next (asyncToGenerator.js:25:1)
at asyncToGenerator.js:32:1
at new ZoneAwarePromise (zone.js:1429:21)
at asyncToGenerator.js:21:1
at StandardController.handleRedirectPromise (StandardController.mjs:172:39)
Despite reviewing several resources, I am unable to identify the cause of the issue. Here is my implementation:
`app.module.ts:
MsalModule.forRoot(
new PublicClientApplication({
auth: {
clientId: environment.azureClientId,
authority: environment.azureTenantId,
redirectUri: environment.azureRedirectUri,
},
cache: {
cacheLocation: 'localStorage',
storeAuthStateInCookie: true,
},
}),
{
interactionType: InteractionType.Redirect,
authRequest: {
scopes: ['user.read'],
},
},
{
interactionType: InteractionType.Redirect,
protectedResourceMap: new Map([
['https://graph.microsoft.com/v1.0/me', ['user.read']],
]),
}
)
app.component.ts:
async ngOnInit(): Promise<void> {
this.theme = this.themeService.theme;
try {
await this.msalService.instance.handleRedirectPromise().then((response) => {
if (response.account !== null) {
this.msalService.instance.setActiveAccount(response.account);
}
const activeAccount = this.msalService.instance.getActiveAccount();
if (!activeAccount) {
console.log("User not authenticated, redirecting to Azure AD login.");
setTimeout(() => {
this.msalService.loginRedirect();
}, 100);
} else {
console.log("Authenticated user:", activeAccount);
this.setupApplication();
}
}).catch((error) => {
console.error("Error processing redirect 1:", error);
});
} catch (err) {
console.error("Error processing redirect:", err);
}
}
Final Considerations: The configurations for URI, callback URI, and client ID are correct. I can successfully redirect to Azure AD, perform the login, but on the return, it seems like the session is lost. Once logged in to Azure AD, subsequent interactions with the application result in the aforementioned error, and I am unable to capture the login token.
Could you please assist me with this issue?
Thank you!
Best regards,
I followed this documentation to create angular with AzureAd.
Angular: 14.3.0 msal-angular: 3.0.20 msal-browser: 3.17.0
The packages msal-angular: 3.0.20, msal-browser: 3.17.0 for Angular V14.3.0 is not compatible, so I downgraded these two packages.
"@azure/msal-angular": "^2.1.2",
"@azure/msal-browser": "^2.22.1"
To Avoid the Error, configure the authority URL instead of TenantId in environment.ts.
environment.ts:
export const environment = {
production: false,
azureClientId: '<ClientId>',
authority: 'https://login.microsoftonline.com/<TenantId>',
azureRedirectUri: 'http://localhost:4200'
};
app.module.ts:
import { BrowserModule } from "@angular/platform-browser";
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
import { NgModule } from "@angular/core";
import { HTTP_INTERCEPTORS, HttpClientModule } from "@angular/common/http"; // Import
import { MatButtonModule } from "@angular/material/button";
import { MatToolbarModule } from "@angular/material/toolbar";
import { MatListModule } from "@angular/material/list";
import { AppRoutingModule } from "./app-routing.module";
import { AppComponent } from "./app.component";
import { HomeComponent } from "./home/home.component";
import { ProfileComponent } from "./profile/profile.component";
import {
MsalModule,
MsalRedirectComponent,
MsalGuard,
MsalInterceptor,
} from "@azure/msal-angular"; // Import MsalInterceptor
import {
InteractionType,
PublicClientApplication,
} from "@azure/msal-browser";
import { environment } from "./environment";
const isIE =
window.navigator.userAgent.indexOf("MSIE ") > -1 ||
window.navigator.userAgent.indexOf("Trident/") > -1;
@NgModule({
declarations: [AppComponent, HomeComponent, ProfileComponent],
imports: [
BrowserModule,
BrowserAnimationsModule,
AppRoutingModule,
MatButtonModule,
MatToolbarModule,
MatListModule,
HttpClientModule,
MsalModule.forRoot(
new PublicClientApplication({
auth: {
clientId: environment.azureClientId,
authority: environment.authority,
redirectUri: environment.azureRedirectUri,
},
cache: {
cacheLocation: "localStorage",
storeAuthStateInCookie: isIE,
},
}),
{
interactionType: InteractionType.Redirect,
authRequest: {
scopes: ["user.read"],
},
},
{
interactionType: InteractionType.Redirect,
protectedResourceMap: new Map([
["https://graph.microsoft.com/v1.0/me", ["user.read"]],
]),
}
),
],
providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: MsalInterceptor,
multi: true,
},
MsalGuard,
],
bootstrap: [AppComponent, MsalRedirectComponent],
})
export class AppModule {}
app.component.ts:
import { Component, OnInit, OnDestroy, Inject } from '@angular/core';
import { MsalService, MsalBroadcastService, MSAL_GUARD_CONFIG, MsalGuardConfiguration } from '@azure/msal-angular';
import { InteractionStatus, PopupRequest } from '@azure/msal-browser';
import { Subject } from 'rxjs';
import { filter, takeUntil } from 'rxjs/operators';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit, OnDestroy {
title = 'msal-angular-tutorial';
isIframe = false;
loginDisplay = false;
private readonly _destroying$ = new Subject<void>();
constructor(@Inject(MSAL_GUARD_CONFIG) private msalGuardConfig: MsalGuardConfiguration, private broadcastService: MsalBroadcastService, private authService: MsalService) { }
ngOnInit() {
this.isIframe = window !== window.parent && !window.opener;
this.broadcastService.inProgress$
.pipe(
filter((status: InteractionStatus) => status === InteractionStatus.None),
takeUntil(this._destroying$)
)
.subscribe(() => {
this.setLoginDisplay();
})
}
login() {
if (this.msalGuardConfig.authRequest){
this.authService.loginPopup({...this.msalGuardConfig.authRequest} as PopupRequest)
.subscribe({
next: (result) => {
console.log(result);
this.setLoginDisplay();
},
error: (error) => console.log(error)
});
} else {
this.authService.loginPopup()
.subscribe({
next: (result) => {
console.log(result);
this.setLoginDisplay();
},
error: (error) => console.log(error)
});
}
}
logout() {
this.authService.logoutPopup({
mainWindowRedirectUri: "/"
});
}
setLoginDisplay() {
this.loginDisplay = this.authService.instance.getAllAccounts().length > 0;
}
ngOnDestroy(): void {
this._destroying$.next(undefined);
this._destroying$.complete();
}
}
After making above changes, I successfully Logged in.
Output: