UNPKG

oidc-client-rx

Version:

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

115 lines (114 loc) 6.7 kB
import { Injectable, inject } from "injection-js"; import { TimeoutError, forkJoin, of, throwError, timer } from "rxjs"; import { map, mergeMap, retryWhen, switchMap, take, tap, timeout } from "rxjs/operators"; import { AuthStateService } from "../auth-state/auth-state.service.js"; import { AuthWellKnownService } from "../config/auth-well-known/auth-well-known.service.js"; import { FlowsDataService } from "../flows/flows-data.service.js"; import { RefreshSessionIframeService } from "../iframe/refresh-session-iframe.service.js"; import { SilentRenewService } from "../iframe/silent-renew.service.js"; import { LoggerService } from "../logging/logger.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 { 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; } const MAX_RETRY_ATTEMPTS = 3; class RefreshSessionService { userForceRefreshSession(config, allConfigs, extraCustomParams) { if (!config) return throwError(()=>new Error("Please provide a configuration before setting up the module")); this.persistCustomParams(extraCustomParams, config); return this.forceRefreshSession(config, allConfigs, extraCustomParams).pipe(tap(()=>this.flowsDataService.resetSilentRenewRunning(config))); } forceRefreshSession(config, allConfigs, extraCustomParams) { const { customParamsRefreshTokenRequest, configId } = config; const mergedParams = { ...customParamsRefreshTokenRequest, ...extraCustomParams }; if (this.flowHelper.isCurrentFlowCodeFlowWithRefreshTokens(config)) return this.startRefreshSession(config, allConfigs, mergedParams).pipe(map(()=>{ const isAuthenticated = this.authStateService.areAuthStorageTokensValid(config); if (isAuthenticated) return { idToken: this.authStateService.getIdToken(config), accessToken: this.authStateService.getAccessToken(config), userData: this.userService.getUserDataFromStore(config), isAuthenticated, configId }; return { isAuthenticated: false, errorMessage: "", userData: null, idToken: "", accessToken: "", configId }; })); const { silentRenewTimeoutInSeconds } = config; const timeOutTime = (silentRenewTimeoutInSeconds ?? 0) * 1000; return forkJoin([ this.startRefreshSession(config, allConfigs, extraCustomParams), this.silentRenewService.refreshSessionWithIFrameCompleted$.pipe(take(1)) ]).pipe(timeout(timeOutTime), retryWhen((errors)=>errors.pipe(mergeMap((error, index)=>{ const scalingDuration = 1000; const currentAttempt = index + 1; if (!(error instanceof TimeoutError) || currentAttempt > MAX_RETRY_ATTEMPTS) return throwError(()=>new Error(error)); this.loggerService.logDebug(config, `forceRefreshSession timeout. Attempt #${currentAttempt}`); this.flowsDataService.resetSilentRenewRunning(config); return timer(currentAttempt * scalingDuration); }))), map(([_, callbackContext])=>{ const isAuthenticated = this.authStateService.areAuthStorageTokensValid(config); if (isAuthenticated) return { idToken: callbackContext?.authResult?.id_token ?? "", accessToken: callbackContext?.authResult?.access_token ?? "", userData: this.userService.getUserDataFromStore(config), isAuthenticated, configId }; return { isAuthenticated: false, errorMessage: "", userData: null, idToken: "", accessToken: "", configId }; })); } persistCustomParams(extraCustomParams, config) { const { useRefreshToken } = config; if (extraCustomParams) if (useRefreshToken) this.storagePersistenceService.write("storageCustomParamsRefresh", extraCustomParams, config); else this.storagePersistenceService.write("storageCustomParamsAuthRequest", extraCustomParams, config); } startRefreshSession(config, allConfigs, extraCustomParams) { const isSilentRenewRunning = this.flowsDataService.isSilentRenewRunning(config); this.loggerService.logDebug(config, `Checking: silentRenewRunning: ${isSilentRenewRunning}`); const shouldBeExecuted = !isSilentRenewRunning; if (!shouldBeExecuted) return of(null); return this.authWellKnownService.queryAndStoreAuthWellKnownEndPoints(config).pipe(switchMap(()=>{ this.flowsDataService.setSilentRenewRunning(config); if (this.flowHelper.isCurrentFlowCodeFlowWithRefreshTokens(config)) return this.refreshSessionRefreshTokenService.refreshSessionWithRefreshTokens(config, allConfigs, extraCustomParams); return this.refreshSessionIframeService.refreshSessionWithIframe(config, allConfigs, extraCustomParams); })); } constructor(){ this.flowHelper = inject(FlowHelper); this.flowsDataService = inject(FlowsDataService); this.loggerService = inject(LoggerService); this.silentRenewService = inject(SilentRenewService); this.authStateService = inject(AuthStateService); this.authWellKnownService = inject(AuthWellKnownService); this.refreshSessionIframeService = inject(RefreshSessionIframeService); this.storagePersistenceService = inject(StoragePersistenceService); this.refreshSessionRefreshTokenService = inject(RefreshSessionRefreshTokenService); this.userService = inject(UserService); } } RefreshSessionService = _ts_decorate([ Injectable() ], RefreshSessionService); export { MAX_RETRY_ATTEMPTS, RefreshSessionService };