UNPKG

oidc-client-rx

Version:

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

126 lines (125 loc) 7.99 kB
import { Injectable, inject } from "injection-js"; import { ReplaySubject, forkJoin, of, throwError } from "rxjs"; import { catchError, map, share, switchMap } from "rxjs/operators"; import { AuthStateService } from "../auth-state/auth-state.service.js"; import { ConfigurationService } from "../config/config.service.js"; import { FlowsDataService } from "../flows/flows-data.service.js"; import { ResetAuthDataService } from "../flows/reset-auth-data.service.js"; import { RefreshSessionIframeService } from "../iframe/refresh-session-iframe.service.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 { StoragePersistenceService } from "../storage/storage-persistence.service.js"; import { UserService } from "../user-data/user.service.js"; import { FlowHelper } from "../utils/flowHelper/flow-helper.service.js"; import { IntervalService } from "./interval.service.js"; import { RefreshSessionRefreshTokenService } from "./refresh-session-refresh-token.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 PeriodicallyTokenCheckService { startTokenValidationPeriodically(allConfigs, currentConfig) { const configsWithSilentRenewEnabled = this.getConfigsWithSilentRenewEnabled(allConfigs); if (configsWithSilentRenewEnabled.length <= 0) return of(void 0); if (this.intervalService.isTokenValidationRunning()) return of(void 0); const refreshTimeInSeconds = this.getSmallestRefreshTimeFromConfigs(configsWithSilentRenewEnabled); const periodicallyCheck$ = this.intervalService.startPeriodicTokenCheck(refreshTimeInSeconds).pipe(switchMap(()=>{ const objectWithConfigIdsAndRefreshEvent = {}; for (const config of configsWithSilentRenewEnabled){ const identifier = config.configId; const refreshEvent = this.getRefreshEvent(config, allConfigs); objectWithConfigIdsAndRefreshEvent[identifier] = refreshEvent; } return forkJoin(objectWithConfigIdsAndRefreshEvent); })); const o$ = periodicallyCheck$.pipe(catchError((error)=>{ this.loggerService.logError(currentConfig, "silent renew failed!", error); return throwError(()=>error); }), map((objectWithConfigIds)=>{ for (const [configId, _] of Object.entries(objectWithConfigIds))return void this.configurationService.getOpenIDConfiguration(configId).subscribe((config)=>{ this.loggerService.logDebug(config, "silent renew, periodic check finished!"); if (this.flowHelper.isCurrentFlowCodeFlowWithRefreshTokens(config)) this.flowsDataService.resetSilentRenewRunning(config); }); }), share({ connector: ()=>new ReplaySubject(1), resetOnError: false, resetOnComplete: false, resetOnRefCountZero: false })); this.intervalService.runTokenValidationRunning = o$.subscribe({}); return o$; } getRefreshEvent(config, allConfigs) { const shouldStartRefreshEvent = this.shouldStartPeriodicallyCheckForConfig(config); if (!shouldStartRefreshEvent) return of(null); const refreshEvent$ = this.createRefreshEventForConfig(config, allConfigs); this.publicEventsService.fireEvent(EventTypes.SilentRenewStarted); return refreshEvent$.pipe(catchError((error)=>{ this.loggerService.logError(config, "silent renew failed!", error); this.publicEventsService.fireEvent(EventTypes.SilentRenewFailed, error); this.flowsDataService.resetSilentRenewRunning(config); return throwError(()=>new Error(error)); })); } getSmallestRefreshTimeFromConfigs(configsWithSilentRenewEnabled) { const result = configsWithSilentRenewEnabled.reduce((prev, curr)=>(prev.tokenRefreshInSeconds ?? 0) < (curr.tokenRefreshInSeconds ?? 0) ? prev : curr); return result.tokenRefreshInSeconds ?? 0; } getConfigsWithSilentRenewEnabled(allConfigs) { return allConfigs.filter((x)=>x.silentRenew); } createRefreshEventForConfig(configuration, allConfigs) { this.loggerService.logDebug(configuration, "starting silent renew..."); return this.configurationService.getOpenIDConfiguration(configuration.configId).pipe(switchMap((config)=>{ if (!config?.silentRenew) { this.resetAuthDataService.resetAuthorizationData(config, allConfigs); return of(null); } this.flowsDataService.setSilentRenewRunning(config); if (this.flowHelper.isCurrentFlowCodeFlowWithRefreshTokens(config)) { const customParamsRefresh = this.storagePersistenceService.read("storageCustomParamsRefresh", config) || {}; const { customParamsRefreshTokenRequest } = config; const mergedParams = { ...customParamsRefreshTokenRequest, ...customParamsRefresh }; return this.refreshSessionRefreshTokenService.refreshSessionWithRefreshTokens(config, allConfigs, mergedParams); } const customParams = this.storagePersistenceService.read("storageCustomParamsAuthRequest", config); return this.refreshSessionIframeService.refreshSessionWithIframe(config, allConfigs, customParams); })); } shouldStartPeriodicallyCheckForConfig(config) { const idToken = this.authStateService.getIdToken(config); const isSilentRenewRunning = this.flowsDataService.isSilentRenewRunning(config); const isCodeFlowInProgress = this.flowsDataService.isCodeFlowInProgress(config); const userDataFromStore = this.userService.getUserDataFromStore(config); this.loggerService.logDebug(config, `Checking: silentRenewRunning: ${isSilentRenewRunning}, isCodeFlowInProgress: ${isCodeFlowInProgress} - has idToken: ${!!idToken} - has userData: ${!!userDataFromStore}`); const shouldBeExecuted = !!userDataFromStore && !isSilentRenewRunning && !!idToken && !isCodeFlowInProgress; if (!shouldBeExecuted) return false; const idTokenExpired = this.authStateService.hasIdTokenExpiredAndRenewCheckIsEnabled(config); const accessTokenExpired = this.authStateService.hasAccessTokenExpiredIfExpiryExists(config); return idTokenExpired || accessTokenExpired; } constructor(){ this.resetAuthDataService = inject(ResetAuthDataService); this.flowHelper = inject(FlowHelper); this.flowsDataService = inject(FlowsDataService); this.loggerService = inject(LoggerService); this.userService = inject(UserService); this.authStateService = inject(AuthStateService); this.refreshSessionIframeService = inject(RefreshSessionIframeService); this.refreshSessionRefreshTokenService = inject(RefreshSessionRefreshTokenService); this.intervalService = inject(IntervalService); this.storagePersistenceService = inject(StoragePersistenceService); this.publicEventsService = inject(PublicEventsService); this.configurationService = inject(ConfigurationService); } } PeriodicallyTokenCheckService = _ts_decorate([ Injectable() ], PeriodicallyTokenCheckService); export { PeriodicallyTokenCheckService };