Skip to content

Commit 1202586

Browse files
saikumar-anapuramsaikumar.anupuram
and
saikumar.anupuram
authored
SCAL-252103: Implement event for SAML popup closed without authentication (#201)
Co-authored-by: saikumar.anupuram <[email protected]>
1 parent 5d27865 commit 1202586

File tree

2 files changed

+75
-2
lines changed

2 files changed

+75
-2
lines changed

src/auth.spec.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,45 @@ describe('Unit test for auth', () => {
385385
expect(global.window.location.href).toBe(samalLoginUrl);
386386
});
387387

388+
it('should emit SAML_POPUP_CLOSED_NO_AUTH when popup window is closed', async () => {
389+
jest.useFakeTimers();
390+
const mockPopupWindow = {
391+
closed: false,
392+
focus: jest.fn(),
393+
close: jest.fn()
394+
};
395+
global.window.open = jest.fn().mockReturnValue(mockPopupWindow);
396+
Object.defineProperty(window, 'location', {
397+
value: {
398+
href: '',
399+
hash: '',
400+
},
401+
});
402+
spyOn(authInstance, 'samlCompletionPromise').and.returnValue(Promise.resolve(false));
403+
const emitSpy = jest.fn();
404+
const mockEventEmitter = {
405+
emit: emitSpy,
406+
once: jest.fn(),
407+
on: jest.fn()
408+
};
409+
authInstance.setAuthEE(mockEventEmitter as any);
410+
jest.spyOn(tokenAuthService, 'isActiveService')
411+
.mockReturnValueOnce(false)
412+
.mockReturnValueOnce(true);
413+
expect(
414+
await authInstance.doSamlAuth({
415+
...embedConfig.doSamlAuthNoRedirect,
416+
}),
417+
).toBe(true);
418+
document.getElementById('ts-auth-btn').click();
419+
mockPopupWindow.closed = true;
420+
jest.advanceTimersByTime(1000);
421+
window.postMessage({ type: EmbedEvent.SAMLComplete }, '*');
422+
await authInstance.samlCompletionPromise;
423+
expect(emitSpy).toHaveBeenCalledWith(authInstance.AuthStatus.SAML_POPUP_CLOSED_NO_AUTH);
424+
jest.useRealTimers();
425+
authInstance.setAuthEE(null);
426+
});
388427
it('when user is not loggedIn, in config noRedirect is true and isAtSSORedirectUrl is false', async () => {
389428
Object.defineProperty(window, 'location', {
390429
value: {
@@ -408,6 +447,19 @@ describe('Unit test for auth', () => {
408447
await authInstance.samlCompletionPromise;
409448
expect(authInstance.loggedInStatus).toBe(true);
410449
});
450+
451+
it('should support emitting SAML_POPUP_CLOSED_NO_AUTH event', () => {
452+
const emitSpy = jest.fn();
453+
const mockEventEmitter = {
454+
emit: emitSpy,
455+
once: jest.fn()
456+
};
457+
authInstance.setAuthEE(mockEventEmitter as any);
458+
authInstance.getAuthEE().emit(authInstance.AuthStatus.SAML_POPUP_CLOSED_NO_AUTH);
459+
expect(emitSpy).toHaveBeenCalledWith(authInstance.AuthStatus.SAML_POPUP_CLOSED_NO_AUTH);
460+
authInstance.setAuthEE(null);
461+
});
462+
411463
});
412464

413465
describe('doOIDCAuth', () => {

src/auth.ts

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,11 @@ export enum AuthStatus {
7777
* @version SDK: 1.19.0
7878
*/
7979
WAITING_FOR_POPUP = 'WAITING_FOR_POPUP',
80+
81+
/**
82+
* Emitted when the SAML popup is closed without authentication
83+
*/
84+
SAML_POPUP_CLOSED_NO_AUTH = 'SAML_POPUP_CLOSED_NO_AUTH',
8085
}
8186

8287
/**
@@ -96,13 +101,13 @@ export interface AuthEventEmitter {
96101
* @param listener
97102
*/
98103
on(
99-
event: AuthStatus.SDK_SUCCESS | AuthStatus.LOGOUT | AuthStatus.WAITING_FOR_POPUP,
104+
event: AuthStatus.SDK_SUCCESS | AuthStatus.LOGOUT | AuthStatus.WAITING_FOR_POPUP | AuthStatus.SAML_POPUP_CLOSED_NO_AUTH,
100105
listener: () => void,
101106
): this;
102107
on(event: AuthStatus.SUCCESS, listener: (sessionInfo: any) => void): this;
103108
once(event: AuthStatus.FAILURE, listener: (failureType: AuthFailureType) => void): this;
104109
once(
105-
event: AuthStatus.SDK_SUCCESS | AuthStatus.LOGOUT | AuthStatus.WAITING_FOR_POPUP,
110+
event: AuthStatus.SDK_SUCCESS | AuthStatus.LOGOUT | AuthStatus.WAITING_FOR_POPUP | AuthStatus.SAML_POPUP_CLOSED_NO_AUTH,
106111
listener: () => void,
107112
): this;
108113
once(event: AuthStatus.SUCCESS, listener: (sessionInfo: any) => void): this;
@@ -364,17 +369,29 @@ export const doBasicAuth = async (embedConfig: EmbedConfig): Promise<boolean> =>
364369
* @param triggerText
365370
*/
366371
async function samlPopupFlow(ssoURL: string, triggerContainer: DOMSelector, triggerText: string) {
372+
let popupClosedCheck: NodeJS.Timeout;
367373
const openPopup = () => {
368374
if (samlAuthWindow === null || samlAuthWindow.closed) {
369375
samlAuthWindow = window.open(
370376
ssoURL,
371377
'_blank',
372378
'location=no,height=570,width=520,scrollbars=yes,status=yes',
373379
);
380+
if (samlAuthWindow) {
381+
popupClosedCheck = setInterval(() => {
382+
if (samlAuthWindow.closed) {
383+
clearInterval(popupClosedCheck);
384+
if (samlCompletionPromise && !samlCompletionResolved) {
385+
authEE?.emit(AuthStatus.SAML_POPUP_CLOSED_NO_AUTH);
386+
}
387+
}
388+
}, 500);
389+
}
374390
} else {
375391
samlAuthWindow.focus();
376392
}
377393
};
394+
let samlCompletionResolved = false;
378395
authEE?.emit(AuthStatus.WAITING_FOR_POPUP);
379396
const containerEl = getDOMNode(triggerContainer);
380397
if (containerEl) {
@@ -386,6 +403,10 @@ async function samlPopupFlow(ssoURL: string, triggerContainer: DOMSelector, trig
386403
samlCompletionPromise = samlCompletionPromise || new Promise<void>((resolve, reject) => {
387404
window.addEventListener('message', (e) => {
388405
if (e.data.type === EmbedEvent.SAMLComplete) {
406+
samlCompletionResolved = true;
407+
if (popupClosedCheck) {
408+
clearInterval(popupClosedCheck);
409+
}
389410
(e.source as Window).close();
390411
resolve();
391412
}

0 commit comments

Comments
 (0)