-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Description
Core Library
MSAL.js (@azure/msal-browser)
Core Library Version
2.22.1
Wrapper Library
MSAL Angular (@azure/msal-angular)
Wrapper Library Version
2.1.2
Public or Confidential Client?
Confidential
Description
Hi Team,
we're facing below issue while logging from SSO Microsoft.
- when user is logging from SSO from our website using Microsoft login pop-up first time he's able to login. after some time(idle time/multiple login and logout) when he tried to login via SSO, he's unable to login and pop-up is disappearing.
Error Message
User Account: null Error Json: {"eventType":"msal:loginFailure","interactionType":"popup","payload":null,"error":{},"timestamp":1745852447496}
User Account: null Error:{"errorCode":"user_cancelled","errorMessage":"User cancelled the flow.","subError":"","name":"BrowserAuthError","correlationId":"0baa75c7-ec09-4f5f-b512-cc0d9285413f"}
Error; [Mon, 28 Apr 2025 15:00:47 GMT] : [0baa75c7-ec09-4f5f-b512-cc0d9285413f] : msal.js.browser@2.39.0 : Error - PopupHandler.monitorPopupForHash - window closed
User Account: null user_cancelled: User cancelled the flow.
User Account: null RetryCount: 0 Error:user_cancelled: User cancelled the flow.
MSAL Logs
Error; [Tue, 29 Apr 2025 04:04:03 GMT] : [ffc4ccec-33df-41eb-99b3-de453c6e37b5] : msal.js.browser@2.39.0 : Error - PopupHandler.monitorPopupForHash - window closed
Network Trace (Preferrably Fiddler)
- SentPendingTo pick up a draggable item, press the space bar. While dragging, use the arrow keys to move the item. Press space again to drop the item in its new position, or press escape to cancel.
MSAL Configuration
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { NgModule } from '@angular/core';
import { ActivatedRoute, ParamMap, Router } from '@angular/router';
import { MatButtonModule } from '@angular/material/button';
import { MatToolbarModule } from '@angular/material/toolbar';
import { MatListModule } from '@angular/material/list';
import { MatMenuModule } from '@angular/material/menu';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { HomeComponent } from './home/home.component';
import { ProfileComponent } from './profile/profile.component';
import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http';
import { IPublicClientApplication, PublicClientApplication, InteractionType, BrowserCacheLocation, LogLevel } from '@azure/msal-browser';
import { MsalGuard, MsalInterceptor, MsalBroadcastService, MsalInterceptorConfiguration, MsalModule, MsalService, MSAL_GUARD_CONFIG, MSAL_INSTANCE, MSAL_INTERCEPTOR_CONFIG, MsalGuardConfiguration, MsalRedirectComponent } from '@azure/msal-angular';
import { FailedComponent } from './failed/failed.component';
import { SSOAuthenticationComponent } from './sso-authentication/sso-authentication.component';
import { ssoAuthenticationLogoutComponent } from './sso-authentication-logout/sso-authentication-logout.component';
import { environment } from 'src/environments/environment';
import { HeaderComponent } from './header/header.component';
import { FooterComponent } from './footer/footer.component';
import { SocialLoginModule, SocialAuthServiceConfig, GoogleInitOptions } from '@abacritt/angularx-social-login';
import {
GoogleLoginProvider,
FacebookLoginProvider
} from '@abacritt/angularx-social-login';
import { EmailAutenticationComponent } from './email-autentication/email-autentication.component';
// import ngx-translate and the http loader
import {TranslateLoader, TranslateModule} from '@ngx-translate/core';
import {TranslateHttpLoader} from '@ngx-translate/http-loader';
import {HttpClient} from '@angular/common/http';
import { ResetPasswordReminderComponent } from './reset-password-reminder/reset-password-reminder.component';
import { ResetPasswordComponent } from './reset-password/reset-password.component';
import { PasswordExpiredComponent } from './password-expired/password-expired.component';
import { ErrorpageComponent } from './errorpage/errorpage.component';
//import { ErrorInterceptor } from './_helpers/error.interceptor';
import { ReactiveFormsModule } from '@angular/forms';
import { PasswordResetComponent } from './password-reset/password-reset.component';
import { ResetPasswordSuccessComponent } from './reset-password-success/reset-password-success.component';
import { ResetPasswordErrorComponent } from './reset-password-error/reset-password-error.component';
import { ConversionUtils } from 'turbocommons-ts/utils/ConversionUtils';
import { ssoSsoLogoutComponent } from './sso-sso-logout/sso-sso-logout.component';
const isIE = window.navigator.userAgent.indexOf("MSIE ") > -1 || window.navigator.userAgent.indexOf("Trident/") > -1; // Remove this line to use Angular Universal
let clientIdPv='';
let postLogoutRedirectUri='';
export function HttpLoaderFactory(http: HttpClient) {
return new TranslateHttpLoader(http, '../../assets/i18n/', '.json');
}
export function loggerCallback(logLevel: LogLevel, message: string) {
console.log(message);
}
export function MSALInstanceFactory(): IPublicClientApplication {
clientIdPv= localStorage.getItem('clienIdLS')!;
postLogoutRedirectUri = localStorage.getItem("postLogoutRedirectUri")!;
return new PublicClientApplication({
auth: {
clientId: environment.azureSSOClientId,
authority: 'https://login.microsoftonline.com/common', // PPE testing environment.
redirectUri: '/',
postLogoutRedirectUri: '/'
},
cache: {
cacheLocation: BrowserCacheLocation.LocalStorage,
storeAuthStateInCookie: true, // Helps with KMSI scenarios
// storeAuthStateInCookie: isIE, // set to true for IE 11. Remove this line to use Angular Universal
},
system: {
loggerOptions: {
//loggerCallback,
// added for verbose logging
loggerCallback: (level: LogLevel, message: string | Error | any, containsPii: boolean) => {
try {
if (containsPii) {
return; // skip logging sensitive info
}
// Check if message is a string, because only strings have includes method
if (typeof message === 'string') {
if (
message.includes('Silent token acquisition failed') ||
message.includes('Token renewal operation failed due to timeout') ||
message.includes('login_required') ||
message.includes('interaction_in_progress') ||
message.includes('token_renewal_error') ||
message.includes('timeout')
) {
console.warn('loggerCallback:Token/Session Warning', message);
}
} else if (message instanceof Error) {
console.warn('loggerCallback:ErrorMessage:', message.message); // Handle the case when message is an instance of Error
console.warn('loggerCallback:ErrorStackTrace:', message.stack); // Log stack trace if available
}
else if (typeof message === 'object' && message !== null) { // Handle message when it's a generic object (not string or Error)
console.warn('loggerCallback.ObjectMessage:', message);
console.warn('loggerCallback.SerializedObject:', JSON.stringify(message));
}
// log everything if environment is not prod
if ( !environment.production ||
message.includes('timeout') ||
(environment.production && level === LogLevel.Error)
)
{
if (typeof message !== 'string') {
message = JSON.stringify(message);
}
var logTextBase64Str=ConversionUtils.stringToBase64(LogLevel[level] + "; " + message);
// 2. Call Custom API
const logEntry = {
ClientId:clientIdPv
,LogText:logTextBase64Str
,Type: 'MSAL.js'
};
fetch(environment.baseAPIUrl+'/api/v1/log', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(logEntry),
})
.then(response => {
if (!response.ok) {
console.error('Failed to send MSAL log to API:', response.status, response.statusText);
}
})
.catch(error => {
console.error('Error sending MSAL log to API:', error);
});
}
switch (level) {
case LogLevel.Error:
console.error(message);
break;
case LogLevel.Info:
console.info(message);
break;
case LogLevel.Verbose:
console.debug(message);
break;
case LogLevel.Warning:
console.warn(message);
break;
}
} catch (err) {
console.error("MSAL Logger internal error:", err);
}
},
//logLevel: LogLevel.Info,
logLevel: LogLevel.Verbose,
piiLoggingEnabled: false
}
,tokenRenewalOffsetSeconds: 600 //MSAL will try to renew tokens 10 minutes before they expire
,windowHashTimeout: 120000 // 120 seconds/2 minutes
,navigateFrameWait: 60000 // 60 seconds
//,asyncPopups: false, // default value is false
/* Defaults
windowHashTimeout : 9000ms //timeout period for processing the hash fragment (#) in the URL after a redirect during an authentication flow
navigateFrameWait : 500ms //while acquiring token in background, is the maximum amount of time (in milliseconds) that MSAL will wait for the browser to successfully navigate the hidden iframe to the Microsoft authorization endpoint.
tokenRenewalOffsetSeconds : 300s //early renewal buffer for tokens i.e. 5 minutes before expiry
loadFrameTimeout : 0 timeout //for loading iframes, how long the library will wait for the hidden iframe to complete its task before considering the renewal attempt a failure
*/
}
});
}
// required for AOT compilation
// export function HttpLoaderFactory(http: HttpClient): TranslateHttpLoader {
// return new TranslateHttpLoader(http);
// }
export function MSALInterceptorConfigFactory(): MsalInterceptorConfiguration {
const protectedResourceMap = new Map<string, Array<string>>();
protectedResourceMap.set('https://graph.microsoft.com/v1.0/me', ['user.read']); // Prod environment. Uncomment to use.
//protectedResourceMap.set('https://graph.microsoft-ppe.com/v1.0/me', ['user.read']);
return {
interactionType: InteractionType.Popup,
protectedResourceMap
};
}
export function MSALGuardConfigFactory(): MsalGuardConfiguration {
return {
interactionType: InteractionType.Popup,
authRequest: {
scopes: ["api://"+environment.scopeId+"/.default"], // client ID of the API
prompt: 'select_account', // Forces account selection, may reduce KMSI prompts
},
loginFailedRoute: '/login-failed'
};
}
const googleLoginOptions: GoogleInitOptions = {
oneTapEnabled: false, // default is true
scopes: 'https://www.googleapis.com/auth/calendar.readonly'
}; // https://developers.google.com/identity/oauth2/web/guides/use-token-model#initialize_a_token_client
@NgModule({
declarations: [
AppComponent,
HomeComponent,
ProfileComponent,
FailedComponent,
ssoAuthenticationComponent,
ssoAuthenticationLogoutComponent,
HeaderComponent,
FooterComponent,
EmailAutenticationComponent,
ResetPasswordReminderComponent,
ResetPasswordComponent,
PasswordExpiredComponent,
ErrorpageComponent,
PasswordResetComponent,
ResetPasswordSuccessComponent,
ResetPasswordErrorComponent,
ssoSsoLogoutComponent
],
imports: [
BrowserModule,
BrowserAnimationsModule,
AppRoutingModule,
MatButtonModule,
MatToolbarModule,
MatListModule,
MatMenuModule,
HttpClientModule,
MsalModule,
SocialLoginModule,
// ngx-translate and the loader module
HttpClientModule,
ReactiveFormsModule,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useFactory: HttpLoaderFactory,
deps: [HttpClient]
}
})
],
providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: MsalInterceptor,
multi: true
},
//{ provide: HTTP_INTERCEPTORS, useClass: JwtInterceptor, multi: true },
//{ provide: HTTP_INTERCEPTORS, useClass: ErrorInterceptor, multi: true },
{
provide: MSAL_INSTANCE,
useFactory: MSALInstanceFactory
},
{
provide: MSAL_GUARD_CONFIG,
useFactory: MSALGuardConfigFactory
},
{
provide: MSAL_INTERCEPTOR_CONFIG,
useFactory: MSALInterceptorConfigFactory
},
{
provide: 'SocialAuthServiceConfig',
useValue: {
autoLogin: false,
providers: [
{
id: GoogleLoginProvider.PROVIDER_ID,
provider: new GoogleLoginProvider(
environment.googleSSOClientId,googleLoginOptions
)
}
/* ,
{
id: FacebookLoginProvider.PROVIDER_ID,
provider: new FacebookLoginProvider('clientId')
} */
],
onError: (err) => {
console.error(err);
}
} as SocialAuthServiceConfig,
},
MsalService,
MsalGuard,
MsalBroadcastService
],
bootstrap: [AppComponent, MsalRedirectComponent]
})
export class AppModule {
constructor(private route: ActivatedRoute) {
//alert(this.route.snapshot.queryParamMap.get('clientId'));
//this.route.params.subscribe( params => clientIdPv =(params['clientId']));
//clientIdPv=localStorage.getItem('clienIdLS')!;
//alert(clientIdPv);
//alert(this.clientId);
//localStorage.setItem("clienIdLS",this.clientId); */
}
ngOnInit(): void {
/* let clientIdPv=0;
this.route.paramMap.subscribe((params: ParamMap) => {
clientIdPv= +params.get('clientId')!
})
alert(clientIdPv); */
}
}
Relevant Code Snippets
declare var $: any;
@Component({
selector: 'app-sso-authentication',
templateUrl: './sso-authentication.component.html',
styleUrls: ['./sso-authentication.component.css'],
providers: [DataSharingService]
})
export class SSOAuthenticationComponent implements OnInit {
title = 'SSOAuthUI';
private readonly _destroying$ = new Subject<void>();
isIframe = false;
loginDisplay = false;
providerAccessToken = '';
refreshTokenFromRCAPAPI = '';
accessTokenFromRCAPAPI = '';
azureTokenResponseGv = '';
public resultContentObj = new ResultContent();
safeSrc: SafeResourceUrl;
clientAppLoginUrl = '';
callBackUrlFromQueryString = '';
clientIdFromQueryString = '';
loginUrlFromQueryString = '';
isUnAuthorized = '';
showLoginPopup = '';
googleRefreshToken = '';
googleSSOClientId = environment.googleSSOClientId;
langQS = '';
appLang: any;
currentLoggedInUserEmail: any;
loginFailureCounter:number=0;
constructor(@Inject(MSAL_GUARD_CONFIG) private msalGuardConfig: MsalGuardConfiguration
, private ssoAuthService: SSOAuthenticationService
, private authService: MsalService
, private msalBroadcastService: MsalBroadcastService
, private dataShareService: DataSharingService
, private auzureTokenRespService: AzureTokenResponseService
, private sanitizer: DomSanitizer
, private route: ActivatedRoute
, private googleAutheService: GoogleAuthenticationService
, private socialAuthService: SocialAuthService
, private user: UserSessionService
, private langLoaderSvc: TranslationLoaderService
, private langSvc: TranslateService
, private commonUtilSvc: CommonUtilService) {
let clientId = localStorage.getItem("clienIdLS")!;
let callbackUrl = localStorage.getItem("callbackUrl_" + clientId)!;
this.clientAppLoginUrl = localStorage.getItem("loginUrl_" + clientId)!;
this.LogError("Log.constructor", "clientAppLoginUrl from local storage :" + this.clientAppLoginUrl, clientId);
this.safeSrc = this.sanitizer.bypassSecurityTrustResourceUrl(callbackUrl);
//this.route.params.subscribe( params => this.clientAppLoginUrl=params['LoginUrl']);
this.route.queryParams.subscribe(params => {
this.callBackUrlFromQueryString = params['CallBackUrl'];
this.clientIdFromQueryString = params['SSOClientId'];
this.loginUrlFromQueryString = params['LoginUrl'];
this.LogError("Log.this.route.queryParams.subscribe", "loginUrlFromQueryString :" + this.loginUrlFromQueryString, this.clientIdFromQueryString);
this.isUnAuthorized = params['IsUnAuthorized'];
this.showLoginPopup = params['ShowLoginPopup'];
this.clientAppLoginUrl = (this.clientAppLoginUrl == null && this.loginUrlFromQueryString != null)
? this.loginUrlFromQueryString : this.clientAppLoginUrl;
this.LogError("Log.this.route.queryParams.subscribe", "clientAppLoginUrl :" + this.clientAppLoginUrl, this.clientIdFromQueryString);
clientId = (clientId == null && this.clientIdFromQueryString != null) ? this.clientIdFromQueryString : clientId;
if(params['LN']!=null && params['LN']!= undefined && params['LN'].length>0) //check if request url has params['LN'] multiple times
{
if(params['LN'].toString().indexOf(',')!=-1) //check if request url has params['LN'] multiple times means (ex: en-us,en-us,en-us), then split by comma and get 1st value
this.langQS= params['LN'].toString().split(',')[0];
else
this.langQS = params['LN'].toString();
}
else
this.langQS='en-us'; //default lang
});
}
InitLang() {
const lang = this.user.getAppLanguage();
const browserLang = navigator.language.toLowerCase();
if (lang === browserLang) {
this.langLoaderSvc.use(lang);
this.appLang = lang;
} else {
this.langLoaderSvc.use(lang);
this.appLang = browserLang;
}
}
disableGoogleButton(callbackUrl: string) {
if (callbackUrl != null && callbackUrl.indexOf("rmail.") != -1)
$("#pnlGoogleBtn").addClass("disabledbutton");
}
displaySpinner() {
$('#pnlSpinner').css("display", "block");
}
hideSpinner() {
$('#pnlSpinner').css("display", "none");
}
displaySSOButtons() {
$('#pnlSSOButtons').css("display", "block");
}
hideSSOButtons() {
$('#pnlSSOButtons').attr('style', 'display: none !important');
}
googleBtnOnMouseOver() {
alert('hi');
$("#googleBtn").attr('title', 'Sign in with Google');
//document.getElementById('googleBtn')?.setAttribute('title', 'Sign in with Google');
}
logRequestUrlAllQueryStringValues() {
var currentReqUrl = window.location.href;
this.LogError("Log.ngOnInit.logRequestUrlAllQueryStringValues", "Current Request Url: " + currentReqUrl + " ", this.clientIdFromQueryString);
const urlString = window.location.search; // Get the query string part of the URL
const searchParams = new URLSearchParams(urlString);
// Loop using forEach
searchParams.forEach((value, key) => {
console.log(`Key: ${key}, Value: ${value}`);
this.LogError("Log.ngOnInit.logRequestUrlAllQueryStringValues", "key:" + key + " " + "value:" + value, this.clientIdFromQueryString);
});
}
getLoginUrlFragment(): string {
let loginUrl: any = this.loginUrlFromQueryString;
var currentReqUrl = window.location.href;
const urlString = window.location.search; // Get the query string part of the URL
const searchParams = new URLSearchParams(urlString);
let loginUrlFragment = '';
let i: number = 0;
// Loop using forEach
searchParams.forEach((value, key) => {
if (key.toLowerCase() != "SSOClientId".toLowerCase()
&& key.toLowerCase() != "CallBackUrl".toLowerCase()
&& key.toLowerCase() != "LoginUrl".toLowerCase()
&& key.toLowerCase() != "ShowLoginPopup".toLowerCase()
&& key.toLowerCase() != "errorReason".toLowerCase()
) {
if (i == 0)
loginUrlFragment += key + "=" + value;
else
loginUrlFragment += "&" + key + "=" + value;
i++;
}
});
return loginUrlFragment;
}
ngOnInit(): void {
this.logRequestUrlAllQueryStringValues();
if (this.langQS != null && this.langQS != '')
this.appLang = this.langQS;
else
this.appLang = 'en-us';
this.langSvc.setDefaultLang(this.appLang);
localStorage.setItem('appLang', this.appLang);
this.InitLang();
$('#divProcessingCommon').css("display", "none");
//this.disableGoogleButton(this.callBackUrlFromQueryString); //this is temp fix only
this.displaySSOButtons();
this.getGoogleTokenResponseAndRedirectToClientApplication();
this.addHandleRedirectPromise();
this.addMsalEventListeners();
}
private addHandleRedirectPromise() : void{
this.authService.instance.handleRedirectPromise()
.then((response) => {
if (response && response.account) {
this.authService.instance.setActiveAccount(response.account);
}
this.currentLoggedInUserEmail = this.authService.instance.getActiveAccount();
console.log('Initial Account:', this.currentLoggedInUserEmail);
this.LogError("Log.addHandleRedirectPromise.handleRedirectPromise", "User Account: " + this.currentLoggedInUserEmail, this.clientIdFromQueryString)
})
.catch((error: string) => {
console.error('Error handling redirect promise:', error);
this.LogError("Log.addHandleRedirectPromise.handleRedirectPromise.Error", error, this.clientIdFromQueryString)
});
}
//Region Microsoft login popup code
//Code addded for login success, login failure , inprogress event types to call in ngOnInit method
private addMsalEventListeners(): void {
this.msalBroadcastService.msalSubject$
.pipe(filter((msg: EventMessage) => msg.eventType === EventType.LOGIN_SUCCESS))
.subscribe((msg: EventMessage) => {
const payload = msg.payload as AuthenticationResult;
this.authService.instance.setActiveAccount(payload.account);
const loginAccount = payload.account;
this.currentLoggedInUserEmail = loginAccount?.username;
this.LogError("Log.addMsalEventListeners.Result", "User Account: " + loginAccount?.username + " " + this.convertObjToJsonString(msg), this.clientIdFromQueryString);
},
error => {
var errorDesc = error.toString();
let errorReason = '';
this.displayActualErrorInConsole(errorDesc, "addMsalEventListeners.LOGIN_SUCCESS");
if (typeof errorDesc == "string" && errorDesc.indexOf(ErrorReason.CONSENT_REQUIRED) > -1) {
errorReason = ErrorReason.CONSENT_REQUIRED;
this.redirectToClientAppLoginWhenErrorOccured(errorReason);
}
else {
errorReason = ErrorReason.UNKNOWN_ERROR_OCCURED;
this.redirectToClientAppLoginWhenErrorOccured(errorReason);
}
}
);
this.msalBroadcastService.msalSubject$
.pipe(filter((msg: EventMessage) => msg.eventType === EventType.LOGIN_FAILURE || msg.eventType === EventType.ACQUIRE_TOKEN_FAILURE))
.subscribe((msg: EventMessage) => {
console.error('MSAL Popup Login Failure:', msg);
const errorJson = JSON.stringify(msg, Object.getOwnPropertyNames(msg));
const errorObjJson=JSON.stringify(msg?.error);
this.LogError("Log.addMsalEventListeners.FailureEvent", "User Account: " + this.currentLoggedInUserEmail + " Error Json: " + errorJson , this.clientIdFromQueryString)
this.LogError("Log.addMsalEventListeners.FailureEvent1", "User Account: " + this.currentLoggedInUserEmail + " Error:" + errorObjJson, this.clientIdFromQueryString);
});
this.msalBroadcastService.inProgress$
.pipe(filter((status: InteractionStatus) => status === InteractionStatus.None)
,takeUntil(this._destroying$)
)
.subscribe(() => {
this.setLoginDisplay();
this.checkAndSetActiveAccount();
});
}
//End region Microsoft login popup code
generateIframeHtmlElement() {
this.hidePnlComingSoon();
let clientId = this.getSSOClientId();
let callbackUrl = this.getCallBackUrl();
this.clearLocalStorageTokenResponse();
this.generateDataShareIframeHtmlElement(callbackUrl, clientId);
}
convertObjToJsonString(obj: any) {
let response;
response = JSON.stringify(obj);
return response;
}
//#endregion
CancelCurrentRequest() {
window.location.href = this.loginUrlFromQueryString; //+'?errorReason='+ErrorReason.USER_CANCELLED_CURRENT_REQUEST;
}
ngOnDestroy(): void {
this._destroying$.next(undefined);
this._destroying$.complete();
}
async AzureSignIn() {
this.generateIframeHtmlElement();
if (!this.isUserAlreadyLoggedin()) {
console.log("first time login");
this.LogError("Log.AzureSignIn", "first time login", this.clientIdFromQueryString);
await this.firstTimeLoginAndDisplayLoginPopup();
}
else if (this.isUserAlreadyLoggedin() &&
(this.isUnAuthorized != null && this.isUnAuthorized == 'Y')
|| (this.showLoginPopup != null && this.showLoginPopup == 'Y')
) {
console.log('Already logged in, unauthorized request/unable to login due to some reason case, show loginpopup');
this.LogError("Log.AzureSignIn", "Already logged in.Unauthorized request/unable to login due to some reason case,show loginpopup", this.clientIdFromQueryString);
//this.promptForPasswordForUnAuthroizedRequest();
this.chooseAccountToLoginForAlreadyLoggedIn(true);
}
else if (this.isUserAlreadyLoggedin()) {
console.log("AlreadyLoggedIn");
this.LogError("Log.AzureSignIn", "AlreadyLoggedIn", this.clientIdFromQueryString);
this.chooseAccountToLoginForAlreadyLoggedIn(false);
}
}
async firstTimeLoginAndDisplayLoginPopup() {
this.LogError("Log.firstTimeLoginAndDisplayLoginPopup", "Begining of the method", this.clientIdFromQueryString);
await this.loginPopup();
this.hideSSOButtons();
/* Moved below block to ngoninit */
this.LogError("Log.firstTimeLoginAndDisplayLoginPopup", "End of the method", this.clientIdFromQueryString);
}
chooseAccountToLoginForAlreadyLoggedIn(IsUnAuthorized: boolean) {
this.hideSSOButtons();
if (IsUnAuthorized) {
console.log("already logged in, but unauthorized, autologin");
this.LogError("Log.chooseAccountToLoginForAlreadyLoggedIn", "already logged in, but unauthorized", this.clientIdFromQueryString);
}
else {
console.log("already logged in");
this.LogError("Log.chooseAccountToLoginForAlreadyLoggedIn", "already logged in", this.clientIdFromQueryString);
}
let scopeId = environment.scopeId
var request = {
scopes: ["api://" + scopeId + "/.default"],
prompt: 'select_account'
};
this.authService.loginPopup(request)
.subscribe((result: AuthenticationResult) => {
console.log(result);
const payload = result as AuthenticationResult;
const loginAccount = payload.account;
this.currentLoggedInUserEmail = loginAccount?.username;
this.LogError("Log.chooseAccountToLoginForAlreadyLoggedIn.loginPopup.Result", "User Account: " + loginAccount?.username + " " + this.convertObjToJsonString(result), this.clientIdFromQueryString);
this.LogError("Log.chooseAccountToLoginForAlreadyLoggedIn.loginPopup.AuthenticationResult", "User Account: " + loginAccount?.username, this.clientIdFromQueryString);
this.authService.instance.setActiveAccount(payload.account);
this.LogError("Log.chooseAccountToLoginForAlreadyLoggedIn.loginPopup.Response", this.convertObjToJsonString(payload), this.clientIdFromQueryString);
let azureTokenRespObj = this.getAzureTokenResponseObjectForAquireTokenSilent(payload);//this.getAzureTokenResponseObject(payload);
let azuretokenresp = this.getAzureTokenJsonResponseAsString(azureTokenRespObj);
//this.azureTokenResponseGv=azuretokenresp;
if (azuretokenresp != null && azuretokenresp != undefined && azuretokenresp.length>0)
{
this.LogError("Log.chooseAccountToLoginForAlreadyLoggedIn.loginPopup.If", "User Account: " + loginAccount?.username + " " , this.clientIdFromQueryString);
this.redirectToClientApplication(azuretokenresp);
}
else {
let errorReason = ErrorReason.AZURE_TOKEN_EMPTY_RESPONSE_ACQUIRE_TOKEN_POPUP;//"Azure token response is empty from acquireTokenPopup method";
this.LogError("Log.chooseAccountToLoginForAlreadyLoggedIn.loginPopup.Else", "User Account: " + loginAccount?.username + " " + "ErrorReason:"+ errorReason, this.clientIdFromQueryString);
this.redirectToClientAppLoginWhenErrorOccured(errorReason);
}
//this.redirectToClientApplication();
},
error => {
//if(IsUnAuthorized)
//{
var errorDesc = error.toString();
let errorReason = ''
this.LogError("Log.chooseAccountToLoginForAlreadyLoggedIn.loginPopup.Error", "User Account: " + this.currentLoggedInUserEmail + this.convertObjToJsonString(errorDesc), this.clientIdFromQueryString);
//alert(typeof errorDesc);
if (typeof errorDesc == "string" && errorDesc.indexOf(ErrorReason.USER_CANCELLED) > -1) {
errorReason = ErrorReason.USER_CANCELLED_FROM_UNAUTHORIZED_AT_LOGIN_POPUP;
this.redirectToClientAppLoginWhenErrorOccured(errorReason);
//this.acquireTokenSilientImpl(request);
}
else {
this.displayActualErrorInConsole(errorDesc, "chooseAccountToLoginForAlreadyLoggedIn.loginPopup.Request.Error");
errorReason = ErrorReason.UNKNOWN_ERROR_OCCURED;
this.redirectToClientAppLoginWhenErrorOccured(errorReason);
}
//}
},
);
}
getAzureTokenResponseObject(payload: AuthenticationResult) {
let refreshtokenObj = <RefreshTokenResponseModel>this.auzureTokenRespService.getRefreshTokenJsonFromLocalStorage();
this.currentLoggedInUserEmail = payload.account?.username;
this.LogError("Log.getAzureTokenResponseObject.refreshtokenObj", "User Account : " + payload.account?.username! + " " + this.convertObjToJsonString(refreshtokenObj), this.clientIdFromQueryString);
let azureTokenRespObj = new AzureTokenResponseModel();
azureTokenRespObj.accesstoken = payload.accessToken;//payload.idToken;
azureTokenRespObj.useremail = payload.account?.username!;
azureTokenRespObj.username = payload.account?.name!;
azureTokenRespObj.idtoken = payload.idToken;
azureTokenRespObj.refreshtoken = refreshtokenObj.secret;
azureTokenRespObj.ssotype = environment.azureADProvider;
this.LogError("Log.getAzureTokenResponseObject.azureTokenRespObj", "User Account : " + azureTokenRespObj.useremail + " " + this.convertObjToJsonString(azureTokenRespObj), this.clientIdFromQueryString);
return azureTokenRespObj;
}
getAzureTokenResponseObjectForAquireTokenSilent(payload: AuthenticationResult) {
/* let refreshtokenObj=<RefreshTokenResponseModel>this.auzureTokenRespService.getRefreshTokenJsonFromLocalStorage()
let azureTokenRespObj=new AzureTokenResponseModel();
azureTokenRespObj.accesstoken= payload.accessToken;
azureTokenRespObj.useremail=payload.account?.username!;
azureTokenRespObj.username=payload.account?.name!;
azureTokenRespObj.idtoken=payload.idToken;
azureTokenRespObj.refreshtoken=refreshtokenObj.secret;
azureTokenRespObj.ssotype=environment.azureADProvider;
return azureTokenRespObj; */
return this.getAzureTokenResponseObject(payload);
}
setLoginDisplay() {
this.loginDisplay = this.authService.instance.getAllAccounts().length > 0;
if (this.loginDisplay)
$('#btnLogout').css('display', 'block');
}
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]);
this.LogError("Log.checkAndSetActiveAccount", "Multiple accounts, setting active account to: " + accounts[0], this.clientIdFromQueryString);
}
}
//If it is coming back after a failure we can prompt for username/password
async loginPopupPromptForUserNamAndPwd() {
if(this.loginFailureCounter<2)
{
this.loginFailureCounter= this.loginFailureCounter+1;
let scopeId = environment.scopeId;
var request = {
scopes: ["api://" + scopeId + "/.default"],
prompt: 'login'
};
if (this.msalGuardConfig.authRequest) {
try {
const response: AuthenticationResult = await firstValueFrom(
this.authService.loginPopup(request)
);
const loginAccount = response.account;
this.currentLoggedInUserEmail = loginAccount?.username;
this.LogError("Log.loginPopupPromptForUserNamAndPwd.msalGuardConfig.authRequest", "User Account: " + loginAccount?.username, this.clientIdFromQueryString);
this.authService.instance.setActiveAccount(response.account);
} catch (error: any) {
const errorDesc = error?.message || error.toString();
this.displayActualErrorInConsole(errorDesc, "loginPopupForUserNamAndPwd.Request.Error");
}
} else {
try {
const response: AuthenticationResult = await firstValueFrom(
this.authService.loginPopup(request)
);
const loginAccount = response.account;
this.currentLoggedInUserEmail = loginAccount?.username;
this.LogError("Log.loginPopupPromptForUserNamAndPwd.msalGuardConfig.authRequest.Else", "User Account: " + loginAccount?.username, this.clientIdFromQueryString);
this.authService.instance.setActiveAccount(response.account);
} catch (error: any) {
const errorDesc = error?.message || error.toString();
this.displayActualErrorInConsole(errorDesc, "loginPopupForUserNamAndPwd.msalGuardConfig.authRequest.Else.Error");
let errorReason = ErrorReason.LOGIN_POPUP_FAILURE;
this.redirectToClientAppLoginWhenErrorOccured(errorReason);
}
}
}
else
{
this.LogError("Log.loginPopupPromptForUserNamAndPwd.Else", "User Account: " + this.currentLoggedInUserEmail +"Login Attempts exceeded more than 1 time, Login attempts tried: "+ this.loginFailureCounter, this.clientIdFromQueryString);
let errorReason = ErrorReason.AZURE_TOKEN_EMPTY_RESPONSE_FIRST_TIME_LOGIN;
this.redirectToClientAppLoginWhenErrorOccured(errorReason);
}
}
handleLoginResponse(response:AuthenticationResult)
{
const loginAccount = response.account;
this.currentLoggedInUserEmail = loginAccount?.username;
this.LogError("Log.handleLoginResponse", "User Account: " + loginAccount?.username, this.clientIdFromQueryString);
this.authService.instance.setActiveAccount(response.account);
let azureTokenRespObj = this.getAzureTokenResponseObject(response);
let azuretokenresp = this.getAzureTokenJsonResponseAsString(azureTokenRespObj);
if (azuretokenresp != null && azuretokenresp != undefined && azuretokenresp.length > 0) {
localStorage.setItem("azureTokenRespLocalStorage", azuretokenresp);
this.LogError("Log.handleLoginResponse.LOGIN_SUCCESS", "RedirectToClient. User Account: " + loginAccount?.username, this.clientIdFromQueryString);
this.redirectToClientApplication(azuretokenresp); // This posts the message.
}
else //LOGIN_SUCCESS fallback scenario
{
let errorReason = ErrorReason.AZURE_TOKEN_EMPTY_RESPONSE_FIRST_TIME_LOGIN; //"Azure token response is empty for First time azure login";
this.LogError("Log.handleLoginResponse.LOGIN_SUCCESS.Else", "User Account: " + loginAccount?.username, this.clientIdFromQueryString);
this.redirectToClientAppLoginWhenErrorOccured(errorReason);
//this.loginPopupPromptForUserNamAndPwd();
//this.invokeAcquireTokenSilent();
}
}
async loginPopup() {
let scopeId = environment.scopeId;
var request = {
scopes: ["api://" + scopeId + "/.default"],
prompt: 'select_account'
};
var requestWithPwd = {
scopes: ["api://" + scopeId + "/.default"],
prompt: 'login'
};
const maxAzureLoginRetries : number= environment.maxAzureLoginRetries; //3 configured in environment.ts file
const maxAzureLoginDelay : number = environment.maxAzureLoginDelay;
const maxAzureLoginWithSelectRetries : number = environment.maxAzureLoginWithSelectRetries;
if (this.msalGuardConfig.authRequest) {
for (let i = 0; i < maxAzureLoginRetries; i++)
{
try {
this.LogError("Log.loginPopup.For.Try","User Account: " + this.currentLoggedInUserEmail + " RetryCount: "+ i.toString(),this.clientIdFromQueryString);
const currentRequest = (i < maxAzureLoginWithSelectRetries) ? request : requestWithPwd;
const response: AuthenticationResult = await firstValueFrom(
this.authService.loginPopup(currentRequest)
);
this.handleLoginResponse(response);
this.LogError("Log.loginPopup.For.Try","User Account: " + this.currentLoggedInUserEmail + " successful login in RetryCount: "+ i.toString() ,this.clientIdFromQueryString);
break;
} catch (error: any) {
const errorDesc = error?.message || error.toString();
this.LogError("Log.loginPopup.For.catch","User Account: " + this.currentLoggedInUserEmail + " RetryCount: "+ i.toString() + " Error:"+ errorDesc,this.clientIdFromQueryString);
this.displayActualErrorInConsole(errorDesc, "loginPopup.For.catch");
//this.LogError("Log.loginPopup.LOGIN_FAILURE.Else", "User Account: " + this.currentLoggedInUserEmail + " " + errorDesc.error?.message, this.clientIdFromQueryString);
if (errorDesc?.includes('popups are blocked in the browser'))
{
this.redirectToClientAppLoginWhenErrorOccured(ErrorReason.POPUP_BLOCKED_IN_BROWSER);
break;
}
if (i==maxAzureLoginRetries-1) //3-1=2
{
this.redirectToClientAppLoginWhenErrorOccured(ErrorReason.GENERIC_LOGIN_FAILURE);
}
else
await delayLoop(maxAzureLoginDelay); //2sec
}
}
} else {
try {
const response: AuthenticationResult = await firstValueFrom(
this.authService.loginPopup(request)
);
const loginAccount = response.account;
this.currentLoggedInUserEmail = loginAccount?.username;
this.LogError("Log.msalGuardConfig.authRequest.Else", "User Account: " + loginAccount?.username, this.clientIdFromQueryString);
this.authService.instance.setActiveAccount(response.account);
} catch (error: any) {
const errorDesc = error?.message || error.toString();
this.displayActualErrorInConsole(errorDesc, "loginPopup.msalGuardConfig.authRequest.Else.Error");
let errorReason = ErrorReason.LOGIN_POPUP_FAILURE;
this.redirectToClientAppLoginWhenErrorOccured(errorReason);
}
}
}
displayPnlComingSoon() {
$('#pnlComingSoon').css("display", "block");
setTimeout(function () {
$('#pnlComingSoon').css("display", "none");
}, 2000);
}
hidePnlComingSoon() {
$('#pnlComingSoon').css("display", "none");
}
redirectToClientAppLoginWhenErrorOccured(errorDesc: string) {
console.log("redirectToClientAppLoginWhenErrorOccured error is " + errorDesc);
this.LogError("Log.redirectToClientAppLoginWhenErrorOccured", "User Account: " + this.currentLoggedInUserEmail + " error is :" + errorDesc, this.clientIdFromQueryString);
this.clearLocalStorage();
this.LogError("Log.redirectToClientAppLoginWhenErrorOccured.Request", "User Account: " + this.currentLoggedInUserEmail + " Client App Login Url: " + this.clientAppLoginUrl + "?errorReason=" + errorDesc, this.clientIdFromQueryString);
let loginUrlFragment = this.getLoginUrlFragment();
if (loginUrlFragment.length > 0) {
let loginUrl = this.clientAppLoginUrl.indexOf('?') > 0 ? this.clientAppLoginUrl + '&' + loginUrlFragment + "&errorReason=" + errorDesc
: this.clientAppLoginUrl + "?" + loginUrlFragment + "&errorReason=" + errorDesc;
this.LogError("Log.redirectToClientAppLoginWhenErrorOccured.ModifiedLoginUrl", "User Account: " + this.currentLoggedInUserEmail + " Client App Login Url: " + loginUrl, this.clientIdFromQueryString);
window.location.href = loginUrl;
}
else
window.location.href = this.clientAppLoginUrl + "?errorReason=" + errorDesc;
}
displayActualErrorInConsole(errorDesc: string, errorOrigin: string) {
console.log(errorOrigin+ ":" + ' Actual error is:' + errorDesc);
//console.log(errorDesc);
this.LogError("Log.displayActualErrorInConsole." + errorOrigin, "User Account: " + this.currentLoggedInUserEmail + " " + errorDesc, this.clientIdFromQueryString);
}
logout(popup?: boolean) {
let returnUrl = window.location.href;
let clientId = this.getSSOClientId();
let callbackUrl = this.getCallBackUrl();
if (popup) {
this.authService.logoutPopup({
mainWindowRedirectUri: returnUrl
});
} else {
this.authService.logoutRedirect();
}
}
generateDataShareIframeHtmlElement(callbackUrl: string, ssoClientId: string) {
//removeIframeElement(ssoClientId);
const iframe = document.createElement('IFRAME');
iframe.id = 'receiverSOS1_' + ssoClientId;
//iframe.id = 'receiverIframe';
iframe.style.display = "none";
iframe.style.width = "1px";
//iframe.sandbox="allow-storage-access-by-user-activation allow-scripts allow-same-origin"
(<HTMLIFrameElement>iframe).src = callbackUrl;
document.body.appendChild(iframe);
}
postLocalStorageData(linkURL: any, portal: any) {
this.dataShareService.postCrossDomainMessage(linkURL, portal);
}
isUserAlreadyLoggedin() {
let isLoggedIn = this.authService.instance.getAllAccounts().length > 0;
if (isLoggedIn) {
let accountInfo = this.authService.instance.getAllAccounts();
for (let item of accountInfo) {
this.LogError("Log.isUserAlreadyLoggedin", "UserName: " + item?.username, this.clientIdFromQueryString);
}
}
else {
this.LogError("Log.isUserAlreadyLoggedin", "First time login, no user account already logged in", this.clientIdFromQueryString);
}
/* if(isLoggedIn)
this.setAzureTokenResponseFromCookie(); */
return isLoggedIn;
}
getssoClientId() {
return this.clientIdFromQueryString;
}
getCallBackUrl() {
return this.callBackUrlFromQueryString;
}
getAzureClientId() {
return environment.azureSSOClientId;
}
getAzureTokenJsonResponseAsString(azureTokenRespObj: AzureTokenResponseModel) {
let response: string = "";
response = JSON.stringify(azureTokenRespObj);
let userEmail = azureTokenRespObj?.useremail;
this.currentLoggedInUserEmail = userEmail;
this.LogError("Log.getAzureTokenJsonResponseAsString.Response", "User Account : " + userEmail + " " + response, this.clientIdFromQueryString);
return response;
}
clearLocalStorageTokenResponse() {
let clientId = this.getssoClientId();
localStorage.removeItem("azuretokenresp_" + this.getAzureClientId());
}
redirectToClientApplication(azureTokenResponse: string) {
this.onLoad(azureTokenResponse);
//this.dataShareService.postCrossDomainMessage("test");
}
onLoad(azureTokenResponse: string) {
let clientId = this.getssoClientId();
let callbackUrl = this.getCallBackUrl();
//alert(callbackUrl);
let azuretokenResp = azureTokenResponse;
const date = new Date();
//POST MESSAGE
console.log('is window:load');
var iframe = document.getElementById("receiverSOS1_" + clientId);
//const iframe = document.getElementById("receiverIframe");
console.log(iframe);
//alert(iframe)
//if (iframe == null) { return; }
if (iframe == null) {
this.generateDataShareIframeHtmlElement(callbackUrl, clientId);
iframe = document.getElementById("receiverSOS1_" + clientId);
}
const iWindow = (iframe as HTMLIFrameElement).contentWindow!;
let msgToPost = JSON.stringify(azuretokenResp);
let userEmail = this.currentLoggedInUserEmail;
this.LogError("Log.onLoad", "User Account : " + userEmail + " CallbackUrl is :" + callbackUrl, clientId);
if (msgToPost != null && msgToPost != '') {
console.log('Azure token response exists');
this.LogError("Log.onLoad", "User Account : " + userEmail + " Azure token response exists and msgToPost is :" + msgToPost, clientId);
}
else {
console.log('Azure token response not exists');
this.LogError("Log.onLoad", "User Account : " + userEmail + " Azure token response does not exists." + msgToPost, clientId);
}
//let domainUrl=getDomainUrl(callbackUrl);
this.displaySpinner();
/* iWindow.addEventListener("load", function() {
console.log("Finish");
this.alert('iframe loaded');
});
*/
try {
if (iframe != null && msgToPost != null && msgToPost != '') {
this.clearLocalStorage();
iWindow.postMessage(msgToPost, '*');
setTimeout(function () {
//iWindow.postMessage(msgToPost, callbackUrl);
//alert(callbackUrl);
console.log('callbackUr is ' + callbackUrl);
window.location.href = callbackUrl;
}, 1000);
}
else {
let errorDesc = '';
if (iframe == null) {
errorDesc = ErrorReason.COULD_NOT_ABLE_TO_SEND_MESSAGE;
this.LogError("Log.onLoad.Post.SendMessageFail", "User Account : " + userEmail + " Error: COULD_NOT_ABLE_TO_SEND_MESSAGE",this.clientIdFromQueryString);
this.redirectToClientAppLoginWhenErrorOccured(errorDesc);
}
else if (msgToPost == null || msgToPost == '') {
errorDesc = ErrorReason.AZURE_TOKEN_EMPTY_RESPONSE_BEFORE_SENDING_MESSAGE;
this.LogError("Log.onLoad.Post.EmptyMsgToPost", "User Account : " + userEmail + " Error: msgToPost is empty",this.clientIdFromQueryString);
this.redirectToClientAppLoginWhenErrorOccured(errorDesc);
}
}
} catch (error: any) {
const errorCatchDesc = error?.message || error.toString();
this.LogError("Log.onLoad.Post.Error", "User Account : " + userEmail + " Error:"+ errorCatchDesc,this.clientIdFromQueryString);
this.displayActualErrorInConsole(errorCatchDesc, "Log.onLoad.Post.Error");
const errorReturnDesc = ErrorReason.UNEXPECTED_ERROR_DURING_TOKEN_POST_MESSAGE;
this.redirectToClientAppLoginWhenErrorOccured(errorReturnDesc);
}
}
clearLocalStorage() {
localStorage.clear();
}
setCookie(name: string, value: string, expireDays: number, path: string = '') {
let d: Date = new Date();
d.setTime(d.getTime() + expireDays * 24 * 60 * 60 * 1000);
let expires: string = `expires=${d.toUTCString()}`;
let cpath: string = path ? `; path=${path}` : '';
document.cookie = `${name}=${value}; ${expires}${cpath}`;
}
LogError(type: string, logText: string, clientId: string) {
this.commonUtilSvc.LogError(type, logText, clientId);
}
private getCookie(name: string) {
let ca: Array<string> = document.cookie.split(';');
let caLen: number = ca.length;
let cookieName = `${name}=`;
let c: string;
for (let i: number = 0; i < caLen; i += 1) {
c = ca[i].replace(/^\s+/g, '');
if (c.indexOf(cookieName) == 0) {
return c.substring(cookieName.length, c.length);
}
}
return '';
}
private deleteCookie(name: string) {
this.setCookie(name, '', -1);
}
}
//Refernces - testPauseLoop
//https://medium.com/@chrisjr06/why-and-how-to-avoid-await-in-a-for-loop-32b86722171
function delayLoop(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms));
}
function getDomainUrl(callbackUrl: any) {
var str = callbackUrl,
delimiter = '/',
start = 3,
tokens = str.split(delimiter).slice(start),
result = tokens.join(delimiter);
console.log(result)
// To get the substring BEFORE the nth occurence
var tokens2 = str.split(delimiter).slice(0, start),
result2 = tokens2.join(delimiter); // this
console.log(result2)
return result2
}
function removeIframeElement(ssoClientId: string) {
document.getElementById('receiverSOS1_' + ssoClientId)?.remove();
}
function detectPopupBlocker() {
var isPopupblocker = false;
var windowUrl = 'about:blank';
var windowId = 'TestPopup_' + new Date().getTime();
var windowFeatures = 'left=0%,top=0%,width=0.1%,height=0.1%';
var windowRef = window.open(windowUrl, windowId, windowFeatures);
if (!windowRef) {
//alert('A popup blocker was detected. Please turn it off to use this application.');
//return false;
$('#pnlPopupblockerMsg').css("display", "block");
isPopupblocker = true;
}
else {
// No popup blocker was detected...
windowRef.close();
isPopupblocker = false;
$('#pnlPopupblockerMsg').css("display", "none");
//document.getElementById('pageContent').style.display = 'block';
}
return isPopupblocker;
}
function postMessageToClientAppIfUserAlreadyLoggedin(clientId: string, callbackUrl: string, azuretokenResp: string) {
alert('postMessageToClientApp');
//const iframeWin = document.getElementById('h5-iframe').contentWindow;
const iframe = document.getElementById("receiverSOS1_" + clientId);
//const iframe = document.getElementById("receiverIframe");
console.log(iframe);
//let azuretokenResp= this.azureTokenResponseGv;
var msgToPost = JSON.stringify(azuretokenResp);
//alert(iframe)
if (iframe == null) { return; }
const iWindow = (iframe as HTMLIFrameElement).contentWindow!;
window.addEventListener(
'message',
(e) => {
const { data } = e;
console.log('receive page load', data);
if (data.pageLoaded) {
/* iframeWin.postMessage("hello", '*'); */
// setTimeout(function () {
iWindow.postMessage("rrrrr", '*');
/* if(callbackUrl.indexOf('?IsAzureSSO=Y')==-1)
callbackUrl=callbackUrl+"?IsAzureSSO=Y"; */
//alert('callbackUrl'+callbackUrl);
window.location.href = callbackUrl;
//}, 1000);
}
else {
alert('iframe page is not loaded');
setTimeout(function () {
iWindow.postMessage('not', '*');
/* if(callbackUrl.indexOf('?IsAzureSSO=Y')==-1)
callbackUrl=callbackUrl+"?IsAzureSSO=Y"; */
//alert('callbackUrl'+callbackUrl);
window.location.href = callbackUrl;
}, 1000);
}
},
false,
);
}
function displaySpinner() {
throw new Error('Function not implemented.');
}
Reproduction Steps
- login via sso microsoft login pop-up.
- be idle for some time in the same tab.
- logout of the application.
- try to login, user is not logged-in directly. pop-up is disappearing.
Expected Behavior
use has to login after idle/multiple time login and logouts.
Identity Provider
Entra ID (formerly Azure AD) / MSA
Browsers Affected (Select all that apply)
Chrome, Edge
Regression
No response
Activity
[-]Browser auth error and user cancelled the flow for SSO MSAL-angular.[/-][+]BrowserAuthError and user cancelled the flow for SSO MSAL-angular.[/+]tnorling commentedon Jul 1, 2025
Do you have a Cross-Origin-Opener-Policy set on your site? This can cause the behavior you're describing. I'd also suggest updating to the latest version as v2 is missing > 2 years worth of updates
sneti-rpost commentedon Jul 10, 2025
Hi @tnorling we did move to latest version still we're facing same issue. let us know if any steps to take care from our side.
tnorling commentedon Jul 10, 2025
Please check if you have a COOP policy set on your site.
sneti-rpost commentedon Jul 14, 2025
Hi @tnorling,
We've cross verified for COOP policy, we're not adding any header from our side. below is the workflow we're following. Please suggest if any.
thanks.