@versori/run 0.2.25 → 0.2.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -25,12 +25,20 @@ export declare class VersoriCredentialsFactory implements RoundTripperFactory {
25
25
  private cfgReader;
26
26
  private log;
27
27
  private tracer;
28
+ private oauth2Cache;
28
29
  private readonly defaultDynamic;
29
30
  constructor(credsClient: VersoriCredentialClient, credsBaseURL: string, platformAPIClient: PlatformAPIClient, logger: Logger, tracer: Tracer, cfgReader: ConfigReader);
30
31
  static fromEnv(logger: Logger, tracer: Tracer): VersoriCredentialsFactory;
31
32
  private changeRequestInit;
32
33
  private changeBaseUrl;
33
34
  create(connName: string): Promise<denoFetch | undefined>;
35
+ private apiKeyFetcherStatic;
36
+ private basicAuthFetcherStatic;
37
+ private bearerTokenFetcherStatic;
38
+ private getOAuth2Token;
39
+ private oauth2FetcherStatic;
40
+ private oauth1FetcherStatic;
41
+ private mtlsFetcherStatic;
34
42
  createDynamic(templateName: string): Promise<DynamicFetcher | undefined>;
35
43
  private traceFetch;
36
44
  credentials(): CredentialsProvider;
@@ -1 +1 @@
1
- {"version":3,"file":"roundtripper.d.ts","sourceRoot":"","sources":["../../../../../src/src/dsl/http/versori/roundtripper.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,MAAM,EAAE,MAAM,0CAA0C,CAAC;AAElE,OAAO,EAEH,cAAc,EACd,YAAY,EAGZ,cAAc,EACd,mBAAmB,EACtB,MAAM,aAAa,CAAC;AAQrB,OAAO,EAAE,mBAAmB,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAC;AACrE,OAAO,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAC;AAQ9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAOrD,KAAK,SAAS,GAAG,CACb,KAAK,EAAE,WAAW,GAAG,GAAG,EACxB,IAAI,CAAC,EAAE,WAAW,GAAG;IAAE,MAAM,CAAC,EAAE,IAAI,CAAC,UAAU,CAAA;CAAE,KAChD,OAAO,CAAC,QAAQ,CAAC,CAAC;AAEvB;;;;;;;;;;GAUG;AACH,qBAAa,yBAA0B,YAAW,mBAAmB;IACjE,OAAO,CAAC,WAAW,CAA0B;IAC7C,OAAO,CAAC,YAAY,CAAS;IAE7B,OAAO,CAAC,iBAAiB,CAAoB;IAE7C,OAAO,CAAC,SAAS,CAAe;IAEhC,OAAO,CAAC,GAAG,CAAS;IAEpB,OAAO,CAAC,MAAM,CAAS;IAEvB,OAAO,CAAC,QAAQ,CAAC,cAAc,CAK7B;gBAGE,WAAW,EAAE,uBAAuB,EACpC,YAAY,EAAE,MAAM,EACpB,iBAAiB,EAAE,iBAAiB,EACpC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,YAAY;IAe3B,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,yBAAyB;IAoCzE,OAAO,CAAC,iBAAiB;IAezB,OAAO,CAAC,aAAa;IAwBrB,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC;IAqDxD,aAAa,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC;IA6DxE,OAAO,CAAC,UAAU;IAqClB,WAAW,IAAI,mBAAmB;IAQlC,cAAc,CAAC,YAAY,EAAE,MAAM,GAAG,cAAc;IAyC9C,cAAc,CAChB,IAAI,EAAE,MAAM,EACZ,YAAY,UAAQ,EACpB,YAAY,CAAC,EAAE,MAAM,GACtB,OAAO,CAAC,KAAK,CAAC;IAiBX,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;YAqB9D,uBAAuB;IAoDrC,OAAO,CAAC,aAAa;IAoDrB,OAAO,CAAC,gBAAgB;IAmCxB,OAAO,CAAC,kBAAkB;IAiC1B,OAAO,CAAC,aAAa;IAqErB,OAAO,CAAC,aAAa;IAyErB,OAAO,CAAC,WAAW;YA6BL,QAAQ;YA2BR,MAAM;YA2BN,iBAAiB;CA0BlC"}
1
+ {"version":3,"file":"roundtripper.d.ts","sourceRoot":"","sources":["../../../../../src/src/dsl/http/versori/roundtripper.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,MAAM,EAAE,MAAM,0CAA0C,CAAC;AAElE,OAAO,EAIH,cAAc,EACd,YAAY,EAGZ,cAAc,EACd,mBAAmB,EACtB,MAAM,aAAa,CAAC;AAQrB,OAAO,EAAE,mBAAmB,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAC;AACrE,OAAO,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAC;AAQ9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAOrD,KAAK,SAAS,GAAG,CACb,KAAK,EAAE,WAAW,GAAG,GAAG,EACxB,IAAI,CAAC,EAAE,WAAW,GAAG;IAAE,MAAM,CAAC,EAAE,IAAI,CAAC,UAAU,CAAA;CAAE,KAChD,OAAO,CAAC,QAAQ,CAAC,CAAC;AAavB;;;;;;;;;;GAUG;AACH,qBAAa,yBAA0B,YAAW,mBAAmB;IACjE,OAAO,CAAC,WAAW,CAA0B;IAC7C,OAAO,CAAC,YAAY,CAAS;IAE7B,OAAO,CAAC,iBAAiB,CAAoB;IAE7C,OAAO,CAAC,SAAS,CAAe;IAEhC,OAAO,CAAC,GAAG,CAAS;IAEpB,OAAO,CAAC,MAAM,CAAS;IAEvB,OAAO,CAAC,WAAW,CAAuC;IAE1D,OAAO,CAAC,QAAQ,CAAC,cAAc,CAK7B;gBAGE,WAAW,EAAE,uBAAuB,EACpC,YAAY,EAAE,MAAM,EACpB,iBAAiB,EAAE,iBAAiB,EACpC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,YAAY;IAe3B,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,yBAAyB;IAoCzE,OAAO,CAAC,iBAAiB;IAezB,OAAO,CAAC,aAAa;IAwBf,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC;IAuG9D,OAAO,CAAC,mBAAmB;IAoD3B,OAAO,CAAC,sBAAsB;IA2B9B,OAAO,CAAC,wBAAwB;YA6BlB,cAAc;IAiB5B,OAAO,CAAC,mBAAmB;IA2D3B,OAAO,CAAC,mBAAmB;IAuE3B,OAAO,CAAC,iBAAiB;IAqBzB,aAAa,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC;IAyDxE,OAAO,CAAC,UAAU;IAqClB,WAAW,IAAI,mBAAmB;IAQlC,cAAc,CAAC,YAAY,EAAE,MAAM,GAAG,cAAc;IAyC9C,cAAc,CAChB,IAAI,EAAE,MAAM,EACZ,YAAY,UAAQ,EACpB,YAAY,CAAC,EAAE,MAAM,GACtB,OAAO,CAAC,KAAK,CAAC;IAiBX,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;YAqB9D,uBAAuB;IAoDrC,OAAO,CAAC,aAAa;IAoDrB,OAAO,CAAC,gBAAgB;IAmCxB,OAAO,CAAC,kBAAkB;IAiC1B,OAAO,CAAC,aAAa;IAqErB,OAAO,CAAC,aAAa;IAyErB,OAAO,CAAC,WAAW;YA6BL,QAAQ;YA2BR,MAAM;YA2BN,iBAAiB;CA0BlC"}
@@ -57,6 +57,12 @@ export class VersoriCredentialsFactory {
57
57
  writable: true,
58
58
  value: void 0
59
59
  });
60
+ Object.defineProperty(this, "oauth2Cache", {
61
+ enumerable: true,
62
+ configurable: true,
63
+ writable: true,
64
+ value: {}
65
+ });
60
66
  Object.defineProperty(this, "defaultDynamic", {
61
67
  enumerable: true,
62
68
  configurable: true,
@@ -137,58 +143,341 @@ export class VersoriCredentialsFactory {
137
143
  this.log.debug('Using baseURL from connection', { url: newURL.toString() });
138
144
  return newURL.toString();
139
145
  }
140
- create(connName) {
146
+ async create(connName) {
141
147
  const cnxId = this.cfgReader.getConnectionID(connName);
142
148
  if (!cnxId) {
143
- this.log.debug('Connection not found in config, could be a dynamic connection', {
144
- connectionName: connName,
145
- });
146
149
  return Promise.resolve(undefined);
147
150
  }
148
- const _fn = async (input, init) => {
149
- const conn = await this.platformAPIClient.getStaticConnection(cnxId);
150
- if (!conn) {
151
- this.log.error('Connection not found for user, using default fetch', {
152
- connectionName: connName,
153
- });
154
- return this.traceFetch(input, init);
151
+ const conn = await this.platformAPIClient.getStaticConnection(cnxId);
152
+ let _fetch = this.traceFetch;
153
+ if (!conn) {
154
+ this.log.error('Connection not found - using default fetch', { 'connectionName': connName, 'connectionID': cnxId });
155
+ return _fetch;
156
+ }
157
+ for (const cred of conn?.credentials ?? []) {
158
+ switch (cred.authSchemeConfig.type) {
159
+ case 'api-key': {
160
+ const apiTokenResponse = await this.getToken(cred.credential.id);
161
+ if (!apiTokenResponse?.token?.accessToken || !cred.authSchemeConfig.apiKey) {
162
+ this.log.error('API Key credential is missing token or configuration', {
163
+ connectionName: connName,
164
+ credentialId: cred.credential.id,
165
+ });
166
+ continue;
167
+ }
168
+ _fetch = this.apiKeyFetcherStatic(apiTokenResponse.token.accessToken, cred.authSchemeConfig.apiKey, _fetch);
169
+ break;
170
+ }
171
+ case 'basic-auth': {
172
+ const basicTokenResponse = await this.getToken(cred.credential.id);
173
+ if (!basicTokenResponse?.token?.accessToken) {
174
+ this.log.error('Basic Auth credential is missing token', {
175
+ connectionName: connName,
176
+ credentialId: cred.credential.id,
177
+ });
178
+ continue;
179
+ }
180
+ _fetch = this.basicAuthFetcherStatic(basicTokenResponse.token.accessToken, _fetch);
181
+ break;
182
+ }
183
+ case 'jwt-bearer': {
184
+ const bearerTokenResponse = await this.getToken(cred.credential.id);
185
+ if (!bearerTokenResponse?.token?.accessToken) {
186
+ this.log.error('Bearer Token credential is missing token', {
187
+ connectionName: connName,
188
+ credentialId: cred.credential.id,
189
+ });
190
+ continue;
191
+ }
192
+ _fetch = this.bearerTokenFetcherStatic(bearerTokenResponse.token.accessToken, _fetch);
193
+ break;
194
+ }
195
+ case 'oauth2': {
196
+ if (!cred.credential?.id) {
197
+ this.log.error('OAuth2 credential is missing credential ID', {
198
+ connectionName: connName,
199
+ credentialId: cred.credential.id,
200
+ });
201
+ continue;
202
+ }
203
+ _fetch = this.oauth2FetcherStatic(cred.credential.id, _fetch);
204
+ break;
205
+ }
206
+ case 'oauth1': {
207
+ const oauth1Metadata = await this.getOAuth1Metadata(cred.credential.id);
208
+ if (!oauth1Metadata?.authorizationMetadata) {
209
+ this.log.error('OAuth1 credential is missing metadata', {
210
+ connectionName: connName,
211
+ credentialId: cred.credential.id,
212
+ });
213
+ continue;
214
+ }
215
+ _fetch = this.oauth1FetcherStatic(oauth1Metadata, _fetch);
216
+ break;
217
+ }
218
+ case 'certificate': {
219
+ const mtlsData = await this.getRaw(cred.credential.id);
220
+ if (!mtlsData?.credential?.data['cert'] || !mtlsData?.credential?.data['key']) {
221
+ this.log.error('mTLS credential is missing cert or key data', {
222
+ connectionName: connName,
223
+ credentialId: cred.credential.id,
224
+ });
225
+ continue;
226
+ }
227
+ _fetch = this.mtlsFetcherStatic({
228
+ cert: mtlsData.credential.data['cert'],
229
+ key: mtlsData.credential.data['key'],
230
+ ca: mtlsData.credential.data['ca'],
231
+ }, _fetch);
232
+ break;
233
+ }
155
234
  }
235
+ }
236
+ const _fn = async (input, init) => {
156
237
  const _input = this.changeRequestInit(input, conn);
157
- let _fetch = this.traceFetch;
158
- conn.credentials.forEach((cred) => {
159
- switch (cred.authSchemeConfig.type) {
160
- case 'api-key':
161
- _fetch = this.apiKeyFetcher(cred, _fetch);
162
- break;
163
- case 'basic-auth':
164
- _fetch = this.basicAuthFetcher(cred, _fetch);
165
- break;
166
- case 'jwt-bearer':
167
- _fetch = this.bearerTokenFetcher(cred, _fetch);
168
- break;
169
- case 'oauth2':
170
- _fetch = this.oauth2Fetcher(cred, _fetch);
171
- break;
172
- case 'certificate':
173
- _fetch = this.mtlsFetcher(cred, _fetch);
174
- break;
175
- case 'oauth1':
176
- _fetch = this.oauth1Fetcher(cred, _fetch);
177
- break;
238
+ return _fetch(_input, init);
239
+ };
240
+ return _fn;
241
+ }
242
+ apiKeyFetcherStatic(token, asc, fn) {
243
+ const _fn = async (input, init) => {
244
+ if (!token) {
245
+ throw new Error('Token not found');
246
+ }
247
+ if (!asc) {
248
+ throw new Error('API Key configuration not found');
249
+ }
250
+ if (!init) {
251
+ init = {};
252
+ }
253
+ switch (asc.in) {
254
+ case 'header':
255
+ if (!init.headers) {
256
+ init.headers = new Headers();
257
+ }
258
+ if (init.headers instanceof Headers) {
259
+ init.headers.set(asc.name, token);
260
+ }
261
+ else if (init.headers instanceof Array) {
262
+ init.headers.push([asc.name, token]);
263
+ }
264
+ else {
265
+ init.headers = {
266
+ ...init.headers,
267
+ [asc.name]: token,
268
+ };
269
+ }
270
+ break;
271
+ case 'query':
272
+ if (typeof input === 'string') {
273
+ const _input = new URL(input);
274
+ _input.searchParams.append(asc.name, token);
275
+ input = _input.toString();
276
+ }
277
+ else if (input instanceof Request) {
278
+ const _input = new URL(input.url);
279
+ _input.searchParams.append(asc.name, token);
280
+ input = new Request(_input.toString(), init);
281
+ }
282
+ else if (input instanceof URL) {
283
+ input.searchParams.append(asc.name, token);
284
+ }
285
+ break;
286
+ }
287
+ return fn(input, init);
288
+ };
289
+ return _fn;
290
+ }
291
+ basicAuthFetcherStatic(token, fn) {
292
+ const _fn = async (input, init) => {
293
+ if (!init) {
294
+ init = {};
295
+ }
296
+ if (!init.headers) {
297
+ init.headers = new Headers();
298
+ }
299
+ if (init.headers instanceof Headers) {
300
+ init.headers.set('Authorization', `Basic ${token}`);
301
+ }
302
+ else if (init.headers instanceof Array) {
303
+ init.headers.push(['Authorization', `Basic ${token}`]);
304
+ }
305
+ else {
306
+ init.headers = {
307
+ ...init.headers,
308
+ ['Authorization']: `Basic ${token}`,
309
+ };
310
+ }
311
+ return fn(input, init);
312
+ };
313
+ return _fn;
314
+ }
315
+ bearerTokenFetcherStatic(token, fn) {
316
+ const _fn = async (input, init) => {
317
+ if (!init) {
318
+ init = {};
319
+ }
320
+ if (!init.headers) {
321
+ init.headers = new Headers();
322
+ }
323
+ if (init.headers instanceof Headers) {
324
+ init.headers.set('Authorization', `Bearer ${token}`);
325
+ }
326
+ else if (init.headers instanceof Array) {
327
+ init.headers.push(['Authorization', `Bearer ${token}`]);
328
+ }
329
+ else {
330
+ init.headers = {
331
+ ...init.headers,
332
+ ['Authorization']: `Bearer ${token}`,
333
+ };
334
+ }
335
+ return fn(input, init);
336
+ };
337
+ return _fn;
338
+ }
339
+ // getOauth2Token fetches an OAuth2 token, stores the result in a cache, and returns the token.
340
+ // If forceRefresh is true, it will bypass the cache and fetch a new token then update the cache.
341
+ async getOAuth2Token(credId, forceRefresh = false) {
342
+ if (credId in this.oauth2Cache && !forceRefresh) {
343
+ return Promise.resolve(this.oauth2Cache[credId]);
344
+ }
345
+ const tokenResponse = await this.getToken(credId, forceRefresh);
346
+ this.oauth2Cache[credId] = {
347
+ token: tokenResponse.token?.accessToken || '',
348
+ tokenType: tokenResponse.token?.tokenType,
349
+ };
350
+ this.log.debug('Refreshed OAuth2 token and updating cache');
351
+ return this.oauth2Cache[credId];
352
+ }
353
+ oauth2FetcherStatic(credId, fn) {
354
+ const refreshFn = async (input, init, forceRefresh = false) => {
355
+ const tokenResponse = await this.getOAuth2Token(credId, forceRefresh);
356
+ const token = tokenResponse.token;
357
+ const _tokenType = () => {
358
+ const _tt = tokenResponse.tokenType?.toLowerCase();
359
+ switch (_tt) {
360
+ case 'bearer':
361
+ return 'Bearer';
362
+ case 'mac':
363
+ return 'MAC';
364
+ case 'basic':
365
+ return 'Basic';
178
366
  default:
179
- break;
367
+ return 'Bearer';
180
368
  }
369
+ };
370
+ if (!init) {
371
+ init = {};
372
+ }
373
+ if (!init.headers) {
374
+ init.headers = new Headers();
375
+ }
376
+ if (init.headers instanceof Headers) {
377
+ init.headers.set('Authorization', `${_tokenType()} ${token}`);
378
+ }
379
+ else if (init.headers instanceof Array) {
380
+ init.headers.push(['Authorization', `${_tokenType()} ${token}`]);
381
+ }
382
+ else {
383
+ init.headers = {
384
+ ...init.headers,
385
+ ['Authorization']: `${_tokenType()} ${token}`,
386
+ };
387
+ }
388
+ return fn(input, init);
389
+ };
390
+ const _fn = async (input, init) => {
391
+ const resp = await refreshFn(input, init);
392
+ if (resp.status === 401) {
393
+ this.log.debug('Received 401 Unauthorized response, attempting refresh', {
394
+ url: input,
395
+ status: resp.status,
396
+ });
397
+ return refreshFn(input, init, true);
398
+ }
399
+ return resp;
400
+ };
401
+ return _fn;
402
+ }
403
+ oauth1FetcherStatic(metadata, fn) {
404
+ const _fn = async (input, init) => {
405
+ const authParams = new URLSearchParams(metadata.authorizationMetadata?.authorizationParams);
406
+ if (!metadata.authorizationMetadata) {
407
+ throw new Error('OAuth1 metadata not found');
408
+ }
409
+ const oauth = new OAuth({
410
+ consumer: {
411
+ key: metadata.authorizationMetadata.consumerKey,
412
+ secret: metadata.authorizationMetadata.consumerSecret,
413
+ },
414
+ signature_method: 'HMAC-SHA256',
415
+ hash_function: (baseString, key) => createHmac('sha256', key).update(baseString).digest('base64'),
416
+ realm: authParams.get('realm') || undefined,
181
417
  });
182
- return _fetch(_input, init);
418
+ let url;
419
+ if (input instanceof Request) {
420
+ url = input.url;
421
+ }
422
+ else if (input instanceof URL) {
423
+ url = input.toString();
424
+ }
425
+ else if (typeof input === 'string') {
426
+ url = input;
427
+ }
428
+ else {
429
+ throw new Error('Invalid input type for OAuth1 fetcher');
430
+ }
431
+ const header = oauth.toHeader(oauth.authorize({
432
+ url: url,
433
+ method: init?.method || 'GET',
434
+ }, {
435
+ key: metadata.authorizationMetadata?.oauthToken,
436
+ secret: metadata.authorizationMetadata?.oauthTokenSecret,
437
+ }));
438
+ const authHeader = header.Authorization;
439
+ if (!init) {
440
+ init = {};
441
+ }
442
+ if (!init.headers) {
443
+ init.headers = new Headers();
444
+ }
445
+ if (init.headers instanceof Headers) {
446
+ init.headers.set('Authorization', authHeader);
447
+ }
448
+ else if (init.headers instanceof Array) {
449
+ init.headers.push(['Authorization', authHeader]);
450
+ }
451
+ else {
452
+ init.headers = {
453
+ ...init.headers,
454
+ ['Authorization']: authHeader,
455
+ };
456
+ }
457
+ return fn(input, init);
183
458
  };
184
- return Promise.resolve(_fn);
459
+ return _fn;
460
+ }
461
+ mtlsFetcherStatic(mtlsdata, fn) {
462
+ const _fn = async (input, init) => {
463
+ const cert = new TextDecoder().decode(mtlsdata.cert);
464
+ const key = new TextDecoder().decode(mtlsdata.key);
465
+ const caCerts = new TextDecoder().decode(mtlsdata.ca ?? new Uint8Array());
466
+ const httpClient = Deno.createHttpClient({
467
+ cert,
468
+ key,
469
+ caCerts: caCerts ? [caCerts] : [],
470
+ });
471
+ return fn(input, {
472
+ ...init,
473
+ client: httpClient,
474
+ });
475
+ };
476
+ return _fn;
185
477
  }
186
478
  createDynamic(templateName) {
187
479
  const template = this.cfgReader.getTemplateID(templateName);
188
480
  if (!template) {
189
- this.log.debug('Template not found in config, could be a static connection', {
190
- templateName: templateName,
191
- });
192
481
  return Promise.resolve(undefined);
193
482
  }
194
483
  const _fn = async (activation, input, init) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@versori/run",
3
- "version": "v0.2.25",
3
+ "version": "v0.2.26",
4
4
  "description": "Versori Run",
5
5
  "homepage": "https://github.com/versori/versori-run#readme",
6
6
  "repository": {
@@ -25,12 +25,20 @@ export declare class VersoriCredentialsFactory implements RoundTripperFactory {
25
25
  private cfgReader;
26
26
  private log;
27
27
  private tracer;
28
+ private oauth2Cache;
28
29
  private readonly defaultDynamic;
29
30
  constructor(credsClient: VersoriCredentialClient, credsBaseURL: string, platformAPIClient: PlatformAPIClient, logger: Logger, tracer: Tracer, cfgReader: ConfigReader);
30
31
  static fromEnv(logger: Logger, tracer: Tracer): VersoriCredentialsFactory;
31
32
  private changeRequestInit;
32
33
  private changeBaseUrl;
33
34
  create(connName: string): Promise<denoFetch | undefined>;
35
+ private apiKeyFetcherStatic;
36
+ private basicAuthFetcherStatic;
37
+ private bearerTokenFetcherStatic;
38
+ private getOAuth2Token;
39
+ private oauth2FetcherStatic;
40
+ private oauth1FetcherStatic;
41
+ private mtlsFetcherStatic;
34
42
  createDynamic(templateName: string): Promise<DynamicFetcher | undefined>;
35
43
  private traceFetch;
36
44
  credentials(): CredentialsProvider;
@@ -1 +1 @@
1
- {"version":3,"file":"roundtripper.d.ts","sourceRoot":"","sources":["../../../../../src/src/dsl/http/versori/roundtripper.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,MAAM,EAAE,MAAM,0CAA0C,CAAC;AAElE,OAAO,EAEH,cAAc,EACd,YAAY,EAGZ,cAAc,EACd,mBAAmB,EACtB,MAAM,aAAa,CAAC;AAQrB,OAAO,EAAE,mBAAmB,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAC;AACrE,OAAO,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAC;AAQ9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAOrD,KAAK,SAAS,GAAG,CACb,KAAK,EAAE,WAAW,GAAG,GAAG,EACxB,IAAI,CAAC,EAAE,WAAW,GAAG;IAAE,MAAM,CAAC,EAAE,IAAI,CAAC,UAAU,CAAA;CAAE,KAChD,OAAO,CAAC,QAAQ,CAAC,CAAC;AAEvB;;;;;;;;;;GAUG;AACH,qBAAa,yBAA0B,YAAW,mBAAmB;IACjE,OAAO,CAAC,WAAW,CAA0B;IAC7C,OAAO,CAAC,YAAY,CAAS;IAE7B,OAAO,CAAC,iBAAiB,CAAoB;IAE7C,OAAO,CAAC,SAAS,CAAe;IAEhC,OAAO,CAAC,GAAG,CAAS;IAEpB,OAAO,CAAC,MAAM,CAAS;IAEvB,OAAO,CAAC,QAAQ,CAAC,cAAc,CAK7B;gBAGE,WAAW,EAAE,uBAAuB,EACpC,YAAY,EAAE,MAAM,EACpB,iBAAiB,EAAE,iBAAiB,EACpC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,YAAY;IAe3B,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,yBAAyB;IAoCzE,OAAO,CAAC,iBAAiB;IAezB,OAAO,CAAC,aAAa;IAwBrB,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC;IAqDxD,aAAa,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC;IA6DxE,OAAO,CAAC,UAAU;IAqClB,WAAW,IAAI,mBAAmB;IAQlC,cAAc,CAAC,YAAY,EAAE,MAAM,GAAG,cAAc;IAyC9C,cAAc,CAChB,IAAI,EAAE,MAAM,EACZ,YAAY,UAAQ,EACpB,YAAY,CAAC,EAAE,MAAM,GACtB,OAAO,CAAC,KAAK,CAAC;IAiBX,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;YAqB9D,uBAAuB;IAoDrC,OAAO,CAAC,aAAa;IAoDrB,OAAO,CAAC,gBAAgB;IAmCxB,OAAO,CAAC,kBAAkB;IAiC1B,OAAO,CAAC,aAAa;IAqErB,OAAO,CAAC,aAAa;IAyErB,OAAO,CAAC,WAAW;YA6BL,QAAQ;YA2BR,MAAM;YA2BN,iBAAiB;CA0BlC"}
1
+ {"version":3,"file":"roundtripper.d.ts","sourceRoot":"","sources":["../../../../../src/src/dsl/http/versori/roundtripper.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,MAAM,EAAE,MAAM,0CAA0C,CAAC;AAElE,OAAO,EAIH,cAAc,EACd,YAAY,EAGZ,cAAc,EACd,mBAAmB,EACtB,MAAM,aAAa,CAAC;AAQrB,OAAO,EAAE,mBAAmB,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAC;AACrE,OAAO,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAC;AAQ9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAOrD,KAAK,SAAS,GAAG,CACb,KAAK,EAAE,WAAW,GAAG,GAAG,EACxB,IAAI,CAAC,EAAE,WAAW,GAAG;IAAE,MAAM,CAAC,EAAE,IAAI,CAAC,UAAU,CAAA;CAAE,KAChD,OAAO,CAAC,QAAQ,CAAC,CAAC;AAavB;;;;;;;;;;GAUG;AACH,qBAAa,yBAA0B,YAAW,mBAAmB;IACjE,OAAO,CAAC,WAAW,CAA0B;IAC7C,OAAO,CAAC,YAAY,CAAS;IAE7B,OAAO,CAAC,iBAAiB,CAAoB;IAE7C,OAAO,CAAC,SAAS,CAAe;IAEhC,OAAO,CAAC,GAAG,CAAS;IAEpB,OAAO,CAAC,MAAM,CAAS;IAEvB,OAAO,CAAC,WAAW,CAAuC;IAE1D,OAAO,CAAC,QAAQ,CAAC,cAAc,CAK7B;gBAGE,WAAW,EAAE,uBAAuB,EACpC,YAAY,EAAE,MAAM,EACpB,iBAAiB,EAAE,iBAAiB,EACpC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,YAAY;IAe3B,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,yBAAyB;IAoCzE,OAAO,CAAC,iBAAiB;IAezB,OAAO,CAAC,aAAa;IAwBf,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC;IAuG9D,OAAO,CAAC,mBAAmB;IAoD3B,OAAO,CAAC,sBAAsB;IA2B9B,OAAO,CAAC,wBAAwB;YA6BlB,cAAc;IAiB5B,OAAO,CAAC,mBAAmB;IA2D3B,OAAO,CAAC,mBAAmB;IAuE3B,OAAO,CAAC,iBAAiB;IAqBzB,aAAa,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC;IAyDxE,OAAO,CAAC,UAAU;IAqClB,WAAW,IAAI,mBAAmB;IAQlC,cAAc,CAAC,YAAY,EAAE,MAAM,GAAG,cAAc;IAyC9C,cAAc,CAChB,IAAI,EAAE,MAAM,EACZ,YAAY,UAAQ,EACpB,YAAY,CAAC,EAAE,MAAM,GACtB,OAAO,CAAC,KAAK,CAAC;IAiBX,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;YAqB9D,uBAAuB;IAoDrC,OAAO,CAAC,aAAa;IAoDrB,OAAO,CAAC,gBAAgB;IAmCxB,OAAO,CAAC,kBAAkB;IAiC1B,OAAO,CAAC,aAAa;IAqErB,OAAO,CAAC,aAAa;IAyErB,OAAO,CAAC,WAAW;YA6BL,QAAQ;YA2BR,MAAM;YA2BN,iBAAiB;CA0BlC"}
@@ -63,6 +63,12 @@ class VersoriCredentialsFactory {
63
63
  writable: true,
64
64
  value: void 0
65
65
  });
66
+ Object.defineProperty(this, "oauth2Cache", {
67
+ enumerable: true,
68
+ configurable: true,
69
+ writable: true,
70
+ value: {}
71
+ });
66
72
  Object.defineProperty(this, "defaultDynamic", {
67
73
  enumerable: true,
68
74
  configurable: true,
@@ -143,58 +149,341 @@ class VersoriCredentialsFactory {
143
149
  this.log.debug('Using baseURL from connection', { url: newURL.toString() });
144
150
  return newURL.toString();
145
151
  }
146
- create(connName) {
152
+ async create(connName) {
147
153
  const cnxId = this.cfgReader.getConnectionID(connName);
148
154
  if (!cnxId) {
149
- this.log.debug('Connection not found in config, could be a dynamic connection', {
150
- connectionName: connName,
151
- });
152
155
  return Promise.resolve(undefined);
153
156
  }
154
- const _fn = async (input, init) => {
155
- const conn = await this.platformAPIClient.getStaticConnection(cnxId);
156
- if (!conn) {
157
- this.log.error('Connection not found for user, using default fetch', {
158
- connectionName: connName,
159
- });
160
- return this.traceFetch(input, init);
157
+ const conn = await this.platformAPIClient.getStaticConnection(cnxId);
158
+ let _fetch = this.traceFetch;
159
+ if (!conn) {
160
+ this.log.error('Connection not found - using default fetch', { 'connectionName': connName, 'connectionID': cnxId });
161
+ return _fetch;
162
+ }
163
+ for (const cred of conn?.credentials ?? []) {
164
+ switch (cred.authSchemeConfig.type) {
165
+ case 'api-key': {
166
+ const apiTokenResponse = await this.getToken(cred.credential.id);
167
+ if (!apiTokenResponse?.token?.accessToken || !cred.authSchemeConfig.apiKey) {
168
+ this.log.error('API Key credential is missing token or configuration', {
169
+ connectionName: connName,
170
+ credentialId: cred.credential.id,
171
+ });
172
+ continue;
173
+ }
174
+ _fetch = this.apiKeyFetcherStatic(apiTokenResponse.token.accessToken, cred.authSchemeConfig.apiKey, _fetch);
175
+ break;
176
+ }
177
+ case 'basic-auth': {
178
+ const basicTokenResponse = await this.getToken(cred.credential.id);
179
+ if (!basicTokenResponse?.token?.accessToken) {
180
+ this.log.error('Basic Auth credential is missing token', {
181
+ connectionName: connName,
182
+ credentialId: cred.credential.id,
183
+ });
184
+ continue;
185
+ }
186
+ _fetch = this.basicAuthFetcherStatic(basicTokenResponse.token.accessToken, _fetch);
187
+ break;
188
+ }
189
+ case 'jwt-bearer': {
190
+ const bearerTokenResponse = await this.getToken(cred.credential.id);
191
+ if (!bearerTokenResponse?.token?.accessToken) {
192
+ this.log.error('Bearer Token credential is missing token', {
193
+ connectionName: connName,
194
+ credentialId: cred.credential.id,
195
+ });
196
+ continue;
197
+ }
198
+ _fetch = this.bearerTokenFetcherStatic(bearerTokenResponse.token.accessToken, _fetch);
199
+ break;
200
+ }
201
+ case 'oauth2': {
202
+ if (!cred.credential?.id) {
203
+ this.log.error('OAuth2 credential is missing credential ID', {
204
+ connectionName: connName,
205
+ credentialId: cred.credential.id,
206
+ });
207
+ continue;
208
+ }
209
+ _fetch = this.oauth2FetcherStatic(cred.credential.id, _fetch);
210
+ break;
211
+ }
212
+ case 'oauth1': {
213
+ const oauth1Metadata = await this.getOAuth1Metadata(cred.credential.id);
214
+ if (!oauth1Metadata?.authorizationMetadata) {
215
+ this.log.error('OAuth1 credential is missing metadata', {
216
+ connectionName: connName,
217
+ credentialId: cred.credential.id,
218
+ });
219
+ continue;
220
+ }
221
+ _fetch = this.oauth1FetcherStatic(oauth1Metadata, _fetch);
222
+ break;
223
+ }
224
+ case 'certificate': {
225
+ const mtlsData = await this.getRaw(cred.credential.id);
226
+ if (!mtlsData?.credential?.data['cert'] || !mtlsData?.credential?.data['key']) {
227
+ this.log.error('mTLS credential is missing cert or key data', {
228
+ connectionName: connName,
229
+ credentialId: cred.credential.id,
230
+ });
231
+ continue;
232
+ }
233
+ _fetch = this.mtlsFetcherStatic({
234
+ cert: mtlsData.credential.data['cert'],
235
+ key: mtlsData.credential.data['key'],
236
+ ca: mtlsData.credential.data['ca'],
237
+ }, _fetch);
238
+ break;
239
+ }
161
240
  }
241
+ }
242
+ const _fn = async (input, init) => {
162
243
  const _input = this.changeRequestInit(input, conn);
163
- let _fetch = this.traceFetch;
164
- conn.credentials.forEach((cred) => {
165
- switch (cred.authSchemeConfig.type) {
166
- case 'api-key':
167
- _fetch = this.apiKeyFetcher(cred, _fetch);
168
- break;
169
- case 'basic-auth':
170
- _fetch = this.basicAuthFetcher(cred, _fetch);
171
- break;
172
- case 'jwt-bearer':
173
- _fetch = this.bearerTokenFetcher(cred, _fetch);
174
- break;
175
- case 'oauth2':
176
- _fetch = this.oauth2Fetcher(cred, _fetch);
177
- break;
178
- case 'certificate':
179
- _fetch = this.mtlsFetcher(cred, _fetch);
180
- break;
181
- case 'oauth1':
182
- _fetch = this.oauth1Fetcher(cred, _fetch);
183
- break;
244
+ return _fetch(_input, init);
245
+ };
246
+ return _fn;
247
+ }
248
+ apiKeyFetcherStatic(token, asc, fn) {
249
+ const _fn = async (input, init) => {
250
+ if (!token) {
251
+ throw new Error('Token not found');
252
+ }
253
+ if (!asc) {
254
+ throw new Error('API Key configuration not found');
255
+ }
256
+ if (!init) {
257
+ init = {};
258
+ }
259
+ switch (asc.in) {
260
+ case 'header':
261
+ if (!init.headers) {
262
+ init.headers = new Headers();
263
+ }
264
+ if (init.headers instanceof Headers) {
265
+ init.headers.set(asc.name, token);
266
+ }
267
+ else if (init.headers instanceof Array) {
268
+ init.headers.push([asc.name, token]);
269
+ }
270
+ else {
271
+ init.headers = {
272
+ ...init.headers,
273
+ [asc.name]: token,
274
+ };
275
+ }
276
+ break;
277
+ case 'query':
278
+ if (typeof input === 'string') {
279
+ const _input = new URL(input);
280
+ _input.searchParams.append(asc.name, token);
281
+ input = _input.toString();
282
+ }
283
+ else if (input instanceof Request) {
284
+ const _input = new URL(input.url);
285
+ _input.searchParams.append(asc.name, token);
286
+ input = new Request(_input.toString(), init);
287
+ }
288
+ else if (input instanceof URL) {
289
+ input.searchParams.append(asc.name, token);
290
+ }
291
+ break;
292
+ }
293
+ return fn(input, init);
294
+ };
295
+ return _fn;
296
+ }
297
+ basicAuthFetcherStatic(token, fn) {
298
+ const _fn = async (input, init) => {
299
+ if (!init) {
300
+ init = {};
301
+ }
302
+ if (!init.headers) {
303
+ init.headers = new Headers();
304
+ }
305
+ if (init.headers instanceof Headers) {
306
+ init.headers.set('Authorization', `Basic ${token}`);
307
+ }
308
+ else if (init.headers instanceof Array) {
309
+ init.headers.push(['Authorization', `Basic ${token}`]);
310
+ }
311
+ else {
312
+ init.headers = {
313
+ ...init.headers,
314
+ ['Authorization']: `Basic ${token}`,
315
+ };
316
+ }
317
+ return fn(input, init);
318
+ };
319
+ return _fn;
320
+ }
321
+ bearerTokenFetcherStatic(token, fn) {
322
+ const _fn = async (input, init) => {
323
+ if (!init) {
324
+ init = {};
325
+ }
326
+ if (!init.headers) {
327
+ init.headers = new Headers();
328
+ }
329
+ if (init.headers instanceof Headers) {
330
+ init.headers.set('Authorization', `Bearer ${token}`);
331
+ }
332
+ else if (init.headers instanceof Array) {
333
+ init.headers.push(['Authorization', `Bearer ${token}`]);
334
+ }
335
+ else {
336
+ init.headers = {
337
+ ...init.headers,
338
+ ['Authorization']: `Bearer ${token}`,
339
+ };
340
+ }
341
+ return fn(input, init);
342
+ };
343
+ return _fn;
344
+ }
345
+ // getOauth2Token fetches an OAuth2 token, stores the result in a cache, and returns the token.
346
+ // If forceRefresh is true, it will bypass the cache and fetch a new token then update the cache.
347
+ async getOAuth2Token(credId, forceRefresh = false) {
348
+ if (credId in this.oauth2Cache && !forceRefresh) {
349
+ return Promise.resolve(this.oauth2Cache[credId]);
350
+ }
351
+ const tokenResponse = await this.getToken(credId, forceRefresh);
352
+ this.oauth2Cache[credId] = {
353
+ token: tokenResponse.token?.accessToken || '',
354
+ tokenType: tokenResponse.token?.tokenType,
355
+ };
356
+ this.log.debug('Refreshed OAuth2 token and updating cache');
357
+ return this.oauth2Cache[credId];
358
+ }
359
+ oauth2FetcherStatic(credId, fn) {
360
+ const refreshFn = async (input, init, forceRefresh = false) => {
361
+ const tokenResponse = await this.getOAuth2Token(credId, forceRefresh);
362
+ const token = tokenResponse.token;
363
+ const _tokenType = () => {
364
+ const _tt = tokenResponse.tokenType?.toLowerCase();
365
+ switch (_tt) {
366
+ case 'bearer':
367
+ return 'Bearer';
368
+ case 'mac':
369
+ return 'MAC';
370
+ case 'basic':
371
+ return 'Basic';
184
372
  default:
185
- break;
373
+ return 'Bearer';
186
374
  }
375
+ };
376
+ if (!init) {
377
+ init = {};
378
+ }
379
+ if (!init.headers) {
380
+ init.headers = new Headers();
381
+ }
382
+ if (init.headers instanceof Headers) {
383
+ init.headers.set('Authorization', `${_tokenType()} ${token}`);
384
+ }
385
+ else if (init.headers instanceof Array) {
386
+ init.headers.push(['Authorization', `${_tokenType()} ${token}`]);
387
+ }
388
+ else {
389
+ init.headers = {
390
+ ...init.headers,
391
+ ['Authorization']: `${_tokenType()} ${token}`,
392
+ };
393
+ }
394
+ return fn(input, init);
395
+ };
396
+ const _fn = async (input, init) => {
397
+ const resp = await refreshFn(input, init);
398
+ if (resp.status === 401) {
399
+ this.log.debug('Received 401 Unauthorized response, attempting refresh', {
400
+ url: input,
401
+ status: resp.status,
402
+ });
403
+ return refreshFn(input, init, true);
404
+ }
405
+ return resp;
406
+ };
407
+ return _fn;
408
+ }
409
+ oauth1FetcherStatic(metadata, fn) {
410
+ const _fn = async (input, init) => {
411
+ const authParams = new node_url_1.URLSearchParams(metadata.authorizationMetadata?.authorizationParams);
412
+ if (!metadata.authorizationMetadata) {
413
+ throw new Error('OAuth1 metadata not found');
414
+ }
415
+ const oauth = new oauth_1_0a_1.default({
416
+ consumer: {
417
+ key: metadata.authorizationMetadata.consumerKey,
418
+ secret: metadata.authorizationMetadata.consumerSecret,
419
+ },
420
+ signature_method: 'HMAC-SHA256',
421
+ hash_function: (baseString, key) => (0, node_crypto_1.createHmac)('sha256', key).update(baseString).digest('base64'),
422
+ realm: authParams.get('realm') || undefined,
187
423
  });
188
- return _fetch(_input, init);
424
+ let url;
425
+ if (input instanceof Request) {
426
+ url = input.url;
427
+ }
428
+ else if (input instanceof URL) {
429
+ url = input.toString();
430
+ }
431
+ else if (typeof input === 'string') {
432
+ url = input;
433
+ }
434
+ else {
435
+ throw new Error('Invalid input type for OAuth1 fetcher');
436
+ }
437
+ const header = oauth.toHeader(oauth.authorize({
438
+ url: url,
439
+ method: init?.method || 'GET',
440
+ }, {
441
+ key: metadata.authorizationMetadata?.oauthToken,
442
+ secret: metadata.authorizationMetadata?.oauthTokenSecret,
443
+ }));
444
+ const authHeader = header.Authorization;
445
+ if (!init) {
446
+ init = {};
447
+ }
448
+ if (!init.headers) {
449
+ init.headers = new Headers();
450
+ }
451
+ if (init.headers instanceof Headers) {
452
+ init.headers.set('Authorization', authHeader);
453
+ }
454
+ else if (init.headers instanceof Array) {
455
+ init.headers.push(['Authorization', authHeader]);
456
+ }
457
+ else {
458
+ init.headers = {
459
+ ...init.headers,
460
+ ['Authorization']: authHeader,
461
+ };
462
+ }
463
+ return fn(input, init);
189
464
  };
190
- return Promise.resolve(_fn);
465
+ return _fn;
466
+ }
467
+ mtlsFetcherStatic(mtlsdata, fn) {
468
+ const _fn = async (input, init) => {
469
+ const cert = new TextDecoder().decode(mtlsdata.cert);
470
+ const key = new TextDecoder().decode(mtlsdata.key);
471
+ const caCerts = new TextDecoder().decode(mtlsdata.ca ?? new Uint8Array());
472
+ const httpClient = Deno.createHttpClient({
473
+ cert,
474
+ key,
475
+ caCerts: caCerts ? [caCerts] : [],
476
+ });
477
+ return fn(input, {
478
+ ...init,
479
+ client: httpClient,
480
+ });
481
+ };
482
+ return _fn;
191
483
  }
192
484
  createDynamic(templateName) {
193
485
  const template = this.cfgReader.getTemplateID(templateName);
194
486
  if (!template) {
195
- this.log.debug('Template not found in config, could be a static connection', {
196
- templateName: templateName,
197
- });
198
487
  return Promise.resolve(undefined);
199
488
  }
200
489
  const _fn = async (activation, input, init) => {