'use strict'; const JwksReplica = require('./JwksReplica.js'); const XsuaaService = require('../service/XsuaaService.js'); const IdentityService = require('../service/IdentityService.js'); class JwksManager { #replicas; constructor() { this.#replicas = new Map(); } /** * Sets the expirationTime of any future JwksReplica objects created by this manager. Does not affect already created (and therefore cached) objects. * @param expirationTime in ms * @returns this */ withExpirationTime(expirationTime) { if (expirationTime > 0) { this.expirationTime = expirationTime; } return this; } /** * Sets the refreshPeriod of any future JwksReplica objects created by this manager. Does not affect already created (and therefore cached) objects. * @param refreshPeriod in ms * @returns this */ withRefreshPeriod(refreshPeriod) { if (refreshPeriod > 0) { this.refreshPeriod = refreshPeriod; } return this; } getXsuaaJwks(uaaDomain, zid, attributes = {}) { if (!uaaDomain) { throw new Error("Cannot get JWKS from empty uaaDomain."); } const jwksParams = { zid }; let jwksReplica, replicaKey; if (!attributes.disableCache) { const keyParts = {domain: uaaDomain, ...jwksParams}; replicaKey = this.createCacheKey(keyParts); jwksReplica = this.#replicas.get(replicaKey); } if (!jwksReplica) { const xsuaaService = new XsuaaService(uaaDomain, zid); jwksReplica = new JwksReplica(xsuaaService, this.expirationTime, this.refreshPeriod).withParams(jwksParams); !attributes.disableCache && this.#replicas.set(replicaKey, jwksReplica); } return jwksReplica; } getIdentityJwks(issuerDomain, serviceCredentials, token, attributes = {}) { if (!issuerDomain) { throw new Error("Cannot get JWKS from empty domain."); } const jwksParams = { client_id: serviceCredentials.clientid, app_tid: token.getAppTID(), azp: token.getAzp() } let jwksReplica, replicaKey; if (!attributes.disableCache) { const keyParts = {domain: issuerDomain, ...jwksParams}; replicaKey = this.createCacheKey(keyParts); jwksReplica = this.#replicas.get(replicaKey); } if (!jwksReplica) { const service = new IdentityService(serviceCredentials).withCustomDomain(issuerDomain); jwksReplica = new JwksReplica(service, this.expirationTime, this.refreshPeriod).withParams(jwksParams); !attributes.disableCache && this.#replicas.set(replicaKey, jwksReplica); } return jwksReplica; } /** * Creates a string cache key from the given key-value pairs, ignoring keys with null or undefined values. * @param {object} parts * @returns a cache key in string format, e.g. app_tid:foo:client_id:bar:azp:baz */ createCacheKey(parts) { if (!parts || Object.entries(parts).length < 1) { throw new Error("Could not create JwksManager key. Key parts must contain at least one element."); } return Object.entries(parts) .filter(([value]) => value != null) .map(([key, value]) => `${key}:${value}`) .join(":"); } } module.exports = JwksManager;