UNPKG

oidc-client-rx

Version:

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

185 lines (184 loc) 9.74 kB
import { Injectable, inject } from "injection-js"; import { forkJoin, of, throwError } from "rxjs"; import { catchError, map, switchMap, tap } from "rxjs/operators"; import { AutoLoginService } from "../auto-login/auto-login.service.js"; import { CallbackService } from "../callback/callback.service.js"; import { PeriodicallyTokenCheckService } from "../callback/periodically-token-check.service.js"; import { RefreshSessionService } from "../callback/refresh-session.service.js"; import { CheckSessionService } from "../iframe/check-session.service.js"; import { SilentRenewService } from "../iframe/silent-renew.service.js"; import { LoggerService } from "../logging/logger.service.js"; import { PopUpService } from "../login/popup/popup.service.js"; import { EventTypes } from "../public-events/event-types.js"; import { PublicEventsService } from "../public-events/public-events.service.js"; import { StoragePersistenceService } from "../storage/storage-persistence.service.js"; import { UserService } from "../user-data/user.service.js"; import { CurrentUrlService } from "../utils/url/current-url.service.js"; import { AuthStateService } from "./auth-state.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; } class CheckAuthService { getConfig(configuration, url) { const stateParamFromUrl = this.currentUrlService.getStateParamFromCurrentUrl(url); return stateParamFromUrl ? this.getConfigurationWithUrlState([ configuration ], stateParamFromUrl) : configuration; } checkAuth(configuration, allConfigs, url) { if (!configuration) return throwError(()=>new Error("Please provide a configuration before setting up the module")); this.publicEventsService.fireEvent(EventTypes.CheckingAuth); const stateParamFromUrl = this.currentUrlService.getStateParamFromCurrentUrl(url); const config = this.getConfig(configuration, url); if (!config) return throwError(()=>new Error(`could not find matching config for state ${stateParamFromUrl}`)); return this.checkAuthWithConfig(configuration, allConfigs, url); } checkAuthMultiple(allConfigs, url) { const stateParamFromUrl = this.currentUrlService.getStateParamFromCurrentUrl(url); if (stateParamFromUrl) { const config = this.getConfigurationWithUrlState(allConfigs, stateParamFromUrl); if (!config) return throwError(()=>new Error(`could not find matching config for state ${stateParamFromUrl}`)); return this.composeMultipleLoginResults(allConfigs, config, url); } const configs = allConfigs; const allChecks$ = configs.map((x)=>this.checkAuthWithConfig(x, configs, url)); return forkJoin(allChecks$); } checkAuthIncludingServer(configuration, allConfigs) { if (!configuration) return throwError(()=>new Error("Please provide a configuration before setting up the module")); return this.checkAuthWithConfig(configuration, allConfigs).pipe(switchMap((loginResponse)=>{ const { isAuthenticated } = loginResponse; if (isAuthenticated) return of(loginResponse); return this.refreshSessionService.forceRefreshSession(configuration, allConfigs).pipe(tap((loginResponseAfterRefreshSession)=>{ if (loginResponseAfterRefreshSession?.isAuthenticated) this.startCheckSessionAndValidation(configuration, allConfigs); })); })); } checkAuthWithConfig(config, allConfigs, url) { if (!config) { const errorMessage = "Please provide at least one configuration before setting up the module"; this.loggerService.logError(config, errorMessage); const result = { isAuthenticated: false, errorMessage, userData: null, idToken: "", accessToken: "", configId: "" }; return of(result); } const currentUrl = url || this.currentUrlService.getCurrentUrl(); if (!currentUrl) { const errorMessage = "No URL found!"; this.loggerService.logError(config, errorMessage); const result = { isAuthenticated: false, errorMessage, userData: null, idToken: "", accessToken: "", configId: "" }; return of(result); } const { configId, authority } = config; this.loggerService.logDebug(config, `Working with config '${configId}' using '${authority}'`); if (this.popupService.isCurrentlyInPopup(config)) { this.popupService.sendMessageToMainWindow(currentUrl, config); const result = { isAuthenticated: false, errorMessage: "", userData: null, idToken: "", accessToken: "", configId: "" }; return of(result); } const isCallback = this.callbackService.isCallback(currentUrl, config); this.loggerService.logDebug(config, `currentUrl to check auth with: '${currentUrl}'`); const callback$ = isCallback ? this.callbackService.handleCallbackAndFireEvents(currentUrl, config, allConfigs) : of({}); return callback$.pipe(map(()=>{ const isAuthenticated = this.authStateService.areAuthStorageTokensValid(config); this.loggerService.logDebug(config, `checkAuth completed. Firing events now. isAuthenticated: ${isAuthenticated}`); if (isAuthenticated) { this.startCheckSessionAndValidation(config, allConfigs); if (!isCallback) { this.authStateService.setAuthenticatedAndFireEvent(allConfigs); this.userService.publishUserDataIfExists(config, allConfigs); } } this.publicEventsService.fireEvent(EventTypes.CheckingAuthFinished); const result = { isAuthenticated, userData: this.userService.getUserDataFromStore(config), accessToken: this.authStateService.getAccessToken(config), idToken: this.authStateService.getIdToken(config), configId }; return result; }), tap(({ isAuthenticated })=>{ if (isAuthenticated) this.autoLoginService.checkSavedRedirectRouteAndNavigate(config); }), catchError(({ message })=>{ this.loggerService.logError(config, message); this.publicEventsService.fireEvent(EventTypes.CheckingAuthFinishedWithError, message); const result = { isAuthenticated: false, errorMessage: message, userData: null, idToken: "", accessToken: "", configId }; return of(result); })); } startCheckSessionAndValidation(config, allConfigs) { if (this.checkSessionService.isCheckSessionConfigured(config)) this.checkSessionService.start(config); this.periodicallyTokenCheckService.startTokenValidationPeriodically(allConfigs, config); if (this.silentRenewService.isSilentRenewConfigured(config)) this.silentRenewService.getOrCreateIframe(config); } getConfigurationWithUrlState(configurations, stateFromUrl) { if (!stateFromUrl) return null; for (const config of configurations){ const storedState = this.storagePersistenceService.read("authStateControl", config); if (storedState === stateFromUrl) return config; } return null; } composeMultipleLoginResults(configurations, activeConfig, url) { const allOtherConfigs = configurations.filter((x)=>x.configId !== activeConfig.configId); const currentConfigResult = this.checkAuthWithConfig(activeConfig, configurations, url); const allOtherConfigResults = allOtherConfigs.map((config)=>{ const { redirectUrl } = config; return this.checkAuthWithConfig(config, configurations, redirectUrl); }); return forkJoin([ currentConfigResult, ...allOtherConfigResults ]); } constructor(){ this.checkSessionService = inject(CheckSessionService); this.currentUrlService = inject(CurrentUrlService); this.silentRenewService = inject(SilentRenewService); this.userService = inject(UserService); this.loggerService = inject(LoggerService); this.authStateService = inject(AuthStateService); this.callbackService = inject(CallbackService); this.refreshSessionService = inject(RefreshSessionService); this.periodicallyTokenCheckService = inject(PeriodicallyTokenCheckService); this.popupService = inject(PopUpService); this.autoLoginService = inject(AutoLoginService); this.storagePersistenceService = inject(StoragePersistenceService); this.publicEventsService = inject(PublicEventsService); } } CheckAuthService = _ts_decorate([ Injectable() ], CheckAuthService); export { CheckAuthService };