oidc-client-rx
Version:
ReactiveX enhanced OIDC and OAuth2 protocol support for browser-based JavaScript applications
115 lines (114 loc) • 6.7 kB
JavaScript
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 };