UNPKG

oidc-client-rx

Version:

ReactiveX enhanced OIDC and OAuth2 protocol support for browser-based JavaScript applications

155 lines (154 loc) 8.86 kB
import { Injectable, inject } from "injection-js"; import { BehaviorSubject, Observable, of } from "rxjs"; import { take } from "rxjs/operators"; import { DOCUMENT } from "../dom/index.js"; import { LoggerService } from "../logging/logger.service.js"; import { EventTypes } from "../public-events/event-types.js"; import { PublicEventsService } from "../public-events/public-events.service.js"; import { DESTORY_REF } from "../resources/index.js"; import { StoragePersistenceService } from "../storage/storage-persistence.service.js"; import { IFrameService } from "./existing-iframe.service.js"; function _ts_decorate(decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : null === desc ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if ("object" == typeof Reflect && "function" == typeof Reflect.decorate) r = Reflect.decorate(decorators, target, key, desc); else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; } function _ts_metadata(k, v) { if ("object" == typeof Reflect && "function" == typeof Reflect.metadata) return Reflect.metadata(k, v); } const IFRAME_FOR_CHECK_SESSION_IDENTIFIER = "myiFrameForCheckSession"; class CheckSessionService { get checkSessionChanged$() { return this.checkSessionChangedInternal$.asObservable(); } isCheckSessionConfigured(configuration) { const { startCheckSession } = configuration; return Boolean(startCheckSession); } start(configuration) { if (this.scheduledHeartBeatRunning) return; const { clientId } = configuration; this.pollServerSession(clientId, configuration); } stop() { if (!this.scheduledHeartBeatRunning) return; this.clearScheduledHeartBeat(); this.checkSessionReceived = false; } serverStateChanged(configuration) { const { startCheckSession } = configuration; return Boolean(startCheckSession) && this.checkSessionReceived; } getExistingIframe() { return this.iFrameService.getExistingIFrame(IFRAME_FOR_CHECK_SESSION_IDENTIFIER); } init(configuration) { if (this.lastIFrameRefresh + this.iframeRefreshInterval > Date.now()) return of(void 0); const authWellKnownEndPoints = this.storagePersistenceService.read("authWellKnownEndPoints", configuration); if (!authWellKnownEndPoints) { this.loggerService.logWarning(configuration, "CheckSession - init check session: authWellKnownEndpoints is undefined. Returning."); return of(void 0); } const existingIframe = this.getOrCreateIframe(configuration); this.bindMessageEventToIframe(configuration); const checkSessionIframe = authWellKnownEndPoints.checkSessionIframe; const contentWindow = existingIframe.contentWindow; if (!checkSessionIframe) { this.loggerService.logWarning(configuration, "CheckSession - init check session: checkSessionIframe is not configured to run"); return of(void 0); } if (contentWindow) contentWindow.location.replace(checkSessionIframe); else this.loggerService.logWarning(configuration, "CheckSession - init check session: IFrame contentWindow does not exist"); return new Observable((observer)=>{ existingIframe.onload = ()=>{ this.lastIFrameRefresh = Date.now(); observer.next(void 0); observer.complete(); }; }); } pollServerSession(clientId, configuration) { this.outstandingMessages = 0; const pollServerSessionRecur = ()=>{ this.init(configuration).pipe(take(1)).subscribe(()=>{ const existingIframe = this.getExistingIframe(); if (existingIframe && clientId) { this.loggerService.logDebug(configuration, `CheckSession - clientId : '${clientId}' - existingIframe: '${existingIframe}'`); const sessionState = this.storagePersistenceService.read("session_state", configuration); const authWellKnownEndPoints = this.storagePersistenceService.read("authWellKnownEndPoints", configuration); const contentWindow = existingIframe.contentWindow; if (sessionState && authWellKnownEndPoints?.checkSessionIframe && contentWindow) { const iframeOrigin = new URL(authWellKnownEndPoints.checkSessionIframe)?.origin; this.outstandingMessages++; contentWindow.postMessage(`${clientId} ${sessionState}`, iframeOrigin); } else { this.loggerService.logDebug(configuration, `CheckSession - session_state is '${sessionState}' - AuthWellKnownEndPoints is '${JSON.stringify(authWellKnownEndPoints, null, 2)}'`); this.checkSessionChangedInternal$.next(true); } } else this.loggerService.logWarning(configuration, `CheckSession - OidcSecurityCheckSession pollServerSession checkSession IFrame does not exist: clientId : '${clientId}' - existingIframe: '${existingIframe}'`); if (this.outstandingMessages > 3) this.loggerService.logError(configuration, `CheckSession - OidcSecurityCheckSession not receiving check session response messages. Outstanding messages: '${this.outstandingMessages}'. Server unreachable?`); this.scheduledHeartBeatRunning = this.document?.defaultView?.setTimeout(pollServerSessionRecur, this.heartBeatInterval) ?? null; }); }; pollServerSessionRecur(); } clearScheduledHeartBeat() { if (null !== this.scheduledHeartBeatRunning) { clearTimeout(this.scheduledHeartBeatRunning); this.scheduledHeartBeatRunning = null; } } messageHandler(configuration, e) { const existingIFrame = this.getExistingIframe(); const authWellKnownEndPoints = this.storagePersistenceService.read("authWellKnownEndPoints", configuration); const startsWith = !!authWellKnownEndPoints?.checkSessionIframe?.startsWith(e.origin); this.outstandingMessages = 0; if (existingIFrame && startsWith && e.source === existingIFrame.contentWindow) if ("error" === e.data) this.loggerService.logWarning(configuration, "CheckSession - error from check session messageHandler"); else if ("changed" === e.data) { this.loggerService.logDebug(configuration, `CheckSession - ${e} from check session messageHandler`); this.checkSessionReceived = true; this.eventService.fireEvent(EventTypes.CheckSessionReceived, e.data); this.checkSessionChangedInternal$.next(true); } else { this.eventService.fireEvent(EventTypes.CheckSessionReceived, e.data); this.loggerService.logDebug(configuration, `CheckSession - ${e.data} from check session messageHandler`); } } bindMessageEventToIframe(configuration) { this.iframeMessageEventListener = this.messageHandler.bind(this, configuration); const defaultView = this.document.defaultView; if (defaultView) defaultView.addEventListener("message", this.iframeMessageEventListener, false); } getOrCreateIframe(configuration) { return this.getExistingIframe() || this.iFrameService.addIFrameToWindowBody(IFRAME_FOR_CHECK_SESSION_IDENTIFIER, configuration); } constructor(){ this.destoryRef$ = inject(DESTORY_REF); this.loggerService = inject(LoggerService); this.storagePersistenceService = inject(StoragePersistenceService); this.iFrameService = inject(IFrameService); this.eventService = inject(PublicEventsService); this.document = inject(DOCUMENT); this.checkSessionReceived = false; this.scheduledHeartBeatRunning = null; this.lastIFrameRefresh = 0; this.outstandingMessages = 0; this.heartBeatInterval = 3000; this.iframeRefreshInterval = 60000; this.checkSessionChangedInternal$ = new BehaviorSubject(false); this.destoryRef$.subscribe(()=>{ this.stop(); const windowAsDefaultView = this.document.defaultView; if (windowAsDefaultView && this.iframeMessageEventListener) windowAsDefaultView.removeEventListener("message", this.iframeMessageEventListener, false); }); } } CheckSessionService = _ts_decorate([ Injectable(), _ts_metadata("design:type", Function), _ts_metadata("design:paramtypes", []) ], CheckSessionService); export { CheckSessionService };