oidc-client-rx
Version:
ReactiveX enhanced OIDC and OAuth2 protocol support for browser-based JavaScript applications
166 lines (165 loc) • 8.13 kB
JavaScript
import { Injectable, inject } from "injection-js";
import { BehaviorSubject, throwError } from "rxjs";
import { distinctUntilChanged } from "rxjs/operators";
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 { TokenValidationService } from "../validation/token-validation.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 DEFAULT_AUTHRESULT = {
isAuthenticated: false,
allConfigsAuthenticated: []
};
class AuthStateService {
get authenticated$() {
return this.authenticatedInternal$.asObservable().pipe(distinctUntilChanged());
}
setAuthenticatedAndFireEvent(allConfigs) {
const result = this.composeAuthenticatedResult(allConfigs);
this.authenticatedInternal$.next(result);
}
setUnauthenticatedAndFireEvent(currentConfig, allConfigs) {
this.storagePersistenceService.resetAuthStateInStorage(currentConfig);
const result = this.composeUnAuthenticatedResult(allConfigs);
this.authenticatedInternal$.next(result);
}
updateAndPublishAuthState(authenticationResult) {
this.publicEventsService.fireEvent(EventTypes.NewAuthenticationResult, authenticationResult);
}
setAuthorizationData(accessToken, authResult, currentConfig, allConfigs) {
this.loggerService.logDebug(currentConfig, `storing the accessToken '${accessToken}'`);
this.storagePersistenceService.write("authzData", accessToken, currentConfig);
this.persistAccessTokenExpirationTime(authResult, currentConfig);
this.setAuthenticatedAndFireEvent(allConfigs);
}
getAccessToken(configuration) {
if (!configuration) return "";
if (!this.isAuthenticated(configuration)) return "";
const token = this.storagePersistenceService.getAccessToken(configuration);
return this.decodeURIComponentSafely(token);
}
getIdToken(configuration) {
if (!configuration) return "";
if (!this.isAuthenticated(configuration)) return "";
const token = this.storagePersistenceService.getIdToken(configuration);
return this.decodeURIComponentSafely(token);
}
getRefreshToken(configuration) {
if (!configuration) return "";
if (!this.isAuthenticated(configuration)) return "";
const token = this.storagePersistenceService.getRefreshToken(configuration);
return this.decodeURIComponentSafely(token);
}
getAuthenticationResult(configuration) {
if (!configuration) return null;
if (!this.isAuthenticated(configuration)) return null;
return this.storagePersistenceService.getAuthenticationResult(configuration);
}
areAuthStorageTokensValid(configuration) {
if (!configuration) return false;
if (!this.isAuthenticated(configuration)) return false;
if (this.hasIdTokenExpiredAndRenewCheckIsEnabled(configuration)) {
this.loggerService.logDebug(configuration, "persisted idToken is expired");
return false;
}
if (this.hasAccessTokenExpiredIfExpiryExists(configuration)) {
this.loggerService.logDebug(configuration, "persisted accessToken is expired");
return false;
}
this.loggerService.logDebug(configuration, "persisted idToken and accessToken are valid");
return true;
}
hasIdTokenExpiredAndRenewCheckIsEnabled(configuration) {
const { renewTimeBeforeTokenExpiresInSeconds, triggerRefreshWhenIdTokenExpired, disableIdTokenValidation } = configuration;
if (!triggerRefreshWhenIdTokenExpired || disableIdTokenValidation) return false;
const tokenToCheck = this.storagePersistenceService.getIdToken(configuration);
const idTokenExpired = this.tokenValidationService.hasIdTokenExpired(tokenToCheck, configuration, renewTimeBeforeTokenExpiresInSeconds);
if (idTokenExpired) this.publicEventsService.fireEvent(EventTypes.IdTokenExpired, idTokenExpired);
return idTokenExpired;
}
hasAccessTokenExpiredIfExpiryExists(configuration) {
const { renewTimeBeforeTokenExpiresInSeconds } = configuration;
const accessTokenExpiresIn = this.storagePersistenceService.read("access_token_expires_at", configuration);
const accessTokenHasNotExpired = this.tokenValidationService.validateAccessTokenNotExpired(accessTokenExpiresIn, configuration, renewTimeBeforeTokenExpiresInSeconds);
const hasExpired = !accessTokenHasNotExpired;
if (hasExpired) this.publicEventsService.fireEvent(EventTypes.TokenExpired, hasExpired);
return hasExpired;
}
isAuthenticated(configuration) {
if (!configuration) {
throwError(()=>new Error("Please provide a configuration before setting up the module"));
return false;
}
const hasAccessToken = !!this.storagePersistenceService.getAccessToken(configuration);
const hasIdToken = !!this.storagePersistenceService.getIdToken(configuration);
return hasAccessToken && hasIdToken;
}
decodeURIComponentSafely(token) {
if (token) return decodeURIComponent(token);
return "";
}
persistAccessTokenExpirationTime(authResult, configuration) {
if (authResult?.expires_in) {
const accessTokenExpiryTime = new Date(new Date().toUTCString()).valueOf() + 1000 * authResult.expires_in;
this.storagePersistenceService.write("access_token_expires_at", accessTokenExpiryTime, configuration);
}
}
composeAuthenticatedResult(allConfigs) {
if (1 === allConfigs.length) {
const { configId } = allConfigs[0];
return {
isAuthenticated: true,
allConfigsAuthenticated: [
{
configId: configId ?? "",
isAuthenticated: true
}
]
};
}
return this.checkallConfigsIfTheyAreAuthenticated(allConfigs);
}
composeUnAuthenticatedResult(allConfigs) {
if (1 === allConfigs.length) {
const { configId } = allConfigs[0];
return {
isAuthenticated: false,
allConfigsAuthenticated: [
{
configId: configId ?? "",
isAuthenticated: false
}
]
};
}
return this.checkallConfigsIfTheyAreAuthenticated(allConfigs);
}
checkallConfigsIfTheyAreAuthenticated(allConfigs) {
const allConfigsAuthenticated = allConfigs.map((config)=>({
configId: config.configId ?? "",
isAuthenticated: this.isAuthenticated(config)
}));
const isAuthenticated = allConfigsAuthenticated.every((x)=>!!x.isAuthenticated);
return {
allConfigsAuthenticated,
isAuthenticated
};
}
constructor(){
this.storagePersistenceService = inject(StoragePersistenceService);
this.loggerService = inject(LoggerService);
this.publicEventsService = inject(PublicEventsService);
this.tokenValidationService = inject(TokenValidationService);
this.authenticatedInternal$ = new BehaviorSubject(DEFAULT_AUTHRESULT);
}
}
AuthStateService = _ts_decorate([
Injectable()
], AuthStateService);
export { AuthStateService };