@shushed/helpers 0.0.26 → 0.0.27

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.
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { JSONSchemaType } from 'ajv';
2
2
  import { DeepRedact } from '@hackylabs/deep-redact';
3
- import { DocumentReference, Firestore } from '@google-cloud/firestore';
3
+ import { Firestore, DocumentReference } from '@google-cloud/firestore';
4
4
  import { PubSub, Topic, Subscription } from '@google-cloud/pubsub';
5
5
  import * as _google_cloud_scheduler_build_protos_protos from '@google-cloud/scheduler/build/protos/protos';
6
6
  import { CloudSchedulerClient } from '@google-cloud/scheduler';
@@ -28715,6 +28715,12 @@ declare class Runtime {
28715
28715
  constructor(opts: Opts);
28716
28716
  }
28717
28717
 
28718
+ declare const isPubSubRequest: (headers: Record<string, string>) => boolean;
28719
+ declare const isCronMessage: (headers: Record<string, string>) => boolean;
28720
+ declare const validateGoogleAuth: (opts: Opts & {
28721
+ serviceAccount?: string | null;
28722
+ }, headers: Record<string, string>, firestore: Firestore) => Promise<void>;
28723
+
28718
28724
  type Level = 'env' | 'workflow' | 'trigger';
28719
28725
  declare class EnvEngine extends Runtime {
28720
28726
  docRef: Record<Level, DocumentReference>;
@@ -28987,11 +28993,14 @@ type index_Secrets = Secrets;
28987
28993
  declare const index_Secrets: typeof Secrets;
28988
28994
  type index_TriggerOnCreateOptions = TriggerOnCreateOptions;
28989
28995
  type index_TriggerOnExecuteOptions = TriggerOnExecuteOptions;
28996
+ declare const index_isCronMessage: typeof isCronMessage;
28997
+ declare const index_isPubSubRequest: typeof isPubSubRequest;
28990
28998
  declare const index_sanitize: typeof sanitize;
28991
28999
  declare const index_sanitizeToString: typeof sanitizeToString;
28992
29000
  declare const index_validate: typeof validate;
29001
+ declare const index_validateGoogleAuth: typeof validateGoogleAuth;
28993
29002
  declare namespace index {
28994
- export { index_EnvEngine as EnvEngine, index_JWKSHelper as JWKSHelper, index_PubSubHelper as PubSubHelper, index_Runtime as Runtime, index_SchedulerHelper as SchedulerHelper, index_Secrets as Secrets, type index_TriggerOnCreateOptions as TriggerOnCreateOptions, type index_TriggerOnExecuteOptions as TriggerOnExecuteOptions, index_sanitize as sanitize, index_sanitizeToString as sanitizeToString, index_validate as validate };
29003
+ export { index_EnvEngine as EnvEngine, index_JWKSHelper as JWKSHelper, index_PubSubHelper as PubSubHelper, index_Runtime as Runtime, index_SchedulerHelper as SchedulerHelper, index_Secrets as Secrets, type index_TriggerOnCreateOptions as TriggerOnCreateOptions, type index_TriggerOnExecuteOptions as TriggerOnExecuteOptions, index_isCronMessage as isCronMessage, index_isPubSubRequest as isPubSubRequest, index_sanitize as sanitize, index_sanitizeToString as sanitizeToString, index_validate as validate, index_validateGoogleAuth as validateGoogleAuth };
28995
29004
  }
28996
29005
 
28997
29006
  export { index as lib, index$9 as schema, index$1 as types };
package/dist/index.js CHANGED
@@ -286,9 +286,12 @@ __export(src_public_exports, {
286
286
  Runtime: () => Runtime,
287
287
  SchedulerHelper: () => scheduler_default,
288
288
  Secrets: () => secret_default,
289
+ isCronMessage: () => isCronMessage,
290
+ isPubSubRequest: () => isPubSubRequest,
289
291
  sanitize: () => sanitize,
290
292
  sanitizeToString: () => sanitizeToString,
291
- validate: () => validate
293
+ validate: () => validate,
294
+ validateGoogleAuth: () => validateGoogleAuth
292
295
  });
293
296
 
294
297
  // src-public/validate.ts
@@ -343,8 +346,8 @@ var sanitize = new import_deep_redact.DeepRedact({
343
346
  replaceStringByLength: true
344
347
  });
345
348
 
346
- // src-public/env.ts
347
- var crypto = __toESM(require("crypto"));
349
+ // src-public/jwks.ts
350
+ var import_jose = require("jose");
348
351
 
349
352
  // src-public/runtime.ts
350
353
  var DEFAULT_BRANCH = "migratedprod";
@@ -370,7 +373,121 @@ var Runtime = class {
370
373
  }
371
374
  };
372
375
 
376
+ // src-public/jwks.ts
377
+ var CACHE_TABLE = "cache-global";
378
+ var JWKSHelper = class extends Runtime {
379
+ constructor(opts, firestore) {
380
+ super(opts);
381
+ this.firestore = firestore;
382
+ }
383
+ async validateAuthorizationHeader(authorizationHeader, expectedServiceAccount = null) {
384
+ const splitted = authorizationHeader.split(".");
385
+ const keyMeta = JSON.parse(Buffer.from(splitted[0], "base64").toString("utf-8")) || {};
386
+ const meta = JSON.parse(Buffer.from(splitted[1], "base64").toString("utf-8")) || {};
387
+ if (!meta.email_verified) {
388
+ throw new Error("Authorization header - Email not verified");
389
+ }
390
+ if (expectedServiceAccount === null) {
391
+ expectedServiceAccount = `runtime@${process.env.GCLOUD_PROJECT}.iam.gserviceaccount.com`;
392
+ }
393
+ if (meta.email !== expectedServiceAccount) {
394
+ throw new Error(`Authorization header - Wrong service account, got ${meta?.email}, expected ${expectedServiceAccount}`);
395
+ }
396
+ if (!keyMeta.kid || !keyMeta.alg) {
397
+ throw new Error("Malformed Authorization Header. KID is missing");
398
+ }
399
+ const key = await this.getKey(keyMeta.kid);
400
+ if (!key) {
401
+ throw new Error("Incorrect Authorization Header. The key has not been found in the Google JWKS set");
402
+ }
403
+ const importedKey = await (0, import_jose.importJWK)(key, keyMeta.alg);
404
+ let validationCorrect = true;
405
+ try {
406
+ await (0, import_jose.jwtVerify)(authorizationHeader, importedKey, {
407
+ issuer: ["https://accounts.google.com", "accounts.google.com"],
408
+ clockTolerance: 0,
409
+ audience: process.env.GCLOUD_PROJECT + "/" + this.triggerId
410
+ });
411
+ } catch (err) {
412
+ validationCorrect = false;
413
+ }
414
+ if (!validationCorrect) {
415
+ throw new Error("Incorrect Authorization Header. The JWT is not valid");
416
+ }
417
+ return validationCorrect;
418
+ }
419
+ async getKey(keyId) {
420
+ const jwks = typeof global.jwks !== "undefined" ? global.jwks : null;
421
+ let parsedJwks = null;
422
+ if (jwks) {
423
+ try {
424
+ parsedJwks = JSON.parse(jwks);
425
+ } catch (err) {
426
+ err.message.toString();
427
+ }
428
+ }
429
+ let keyData = parsedJwks?.keys?.find((x) => x.kid === keyId);
430
+ let cachedItem;
431
+ if (!keyData) {
432
+ cachedItem = await this.firestore.collection(CACHE_TABLE).doc("google-jwks").get();
433
+ if (cachedItem.exists && cachedItem.data()?.expires_at.toDate() > /* @__PURE__ */ new Date()) {
434
+ keyData = cachedItem.data()?.value;
435
+ }
436
+ }
437
+ keyData = parsedJwks?.keys.find((x) => x.kid === keyId);
438
+ if (!keyData) {
439
+ const result = await this.fetchJWKS();
440
+ parsedJwks = result.jwks;
441
+ if (cachedItem) {
442
+ cachedItem.ref.set({
443
+ value: parsedJwks,
444
+ expires_at: result.expiresAt
445
+ }, { merge: true });
446
+ }
447
+ keyData = parsedJwks?.keys.find((x) => x.kid === keyId);
448
+ }
449
+ if (parsedJwks && cachedItem) {
450
+ global.jwks = JSON.stringify(parsedJwks);
451
+ }
452
+ return keyData;
453
+ }
454
+ async fetchJWKS() {
455
+ const date = /* @__PURE__ */ new Date();
456
+ let expiresAt = new Date(Date.now() + 1e3 * 60 * 60);
457
+ const response = await fetch("https://www.googleapis.com/oauth2/v3/certs");
458
+ const maxAgePair = response.headers.get("Cache-Control")?.split(",").map((x) => x.trim().split("=")).find((x) => x[0] === "max-age");
459
+ if (maxAgePair && maxAgePair.length === 2) {
460
+ expiresAt = new Date(date.getTime() + parseInt(maxAgePair[1], 10) * 1e3);
461
+ }
462
+ const jwks = await response.json();
463
+ return {
464
+ expiresAt,
465
+ jwks
466
+ };
467
+ }
468
+ };
469
+ var jwks_default = JWKSHelper;
470
+
471
+ // src-public/utils.ts
472
+ var isPubSubRequest = (headers) => (headers["User-Agent"] || headers["user-agent"]) === "CloudPubSub-Google" || (headers["User-Agent"] || headers["user-agent"] || "").indexOf("APIs-Google") !== -1;
473
+ var isCronMessage = (headers) => (headers["User-Agent"] || headers["user-agent"]) === "Google-Cloud-Scheduler";
474
+ var validateGoogleAuth = async (opts, headers, firestore) => {
475
+ const token = headers.authorization?.split(" ")[1];
476
+ if (!token) {
477
+ throw new Error(
478
+ "Missing authorization header or invalid format, needs to be in format: Bearer <IdToken>"
479
+ );
480
+ }
481
+ try {
482
+ const jwksHelper = new jwks_default(opts, firestore);
483
+ await jwksHelper.validateAuthorizationHeader(token, opts.serviceAccount || null);
484
+ } catch (err) {
485
+ throw new Error("Invalid authorization header " + err.message);
486
+ }
487
+ };
488
+
373
489
  // src-public/env.ts
490
+ var crypto = __toESM(require("crypto"));
374
491
  var EnvEngine = class extends Runtime {
375
492
  docRef;
376
493
  store;
@@ -825,102 +942,6 @@ var Secrets = class extends Runtime {
825
942
  }
826
943
  };
827
944
  var secret_default = Secrets;
828
-
829
- // src-public/jwks.ts
830
- var import_jose = require("jose");
831
- var CACHE_TABLE = "cache-global";
832
- var JWKSHelper = class extends Runtime {
833
- constructor(opts, firestore) {
834
- super(opts);
835
- this.firestore = firestore;
836
- }
837
- async validateAuthorizationHeader(authorizationHeader, expectedServiceAccount = null) {
838
- const splitted = authorizationHeader.split(".");
839
- const keyMeta = JSON.parse(Buffer.from(splitted[0], "base64").toString("utf-8")) || {};
840
- const meta = JSON.parse(Buffer.from(splitted[1], "base64").toString("utf-8")) || {};
841
- if (!meta.email_verified) {
842
- throw new Error("Authorization header - Email not verified");
843
- }
844
- if (expectedServiceAccount === null) {
845
- expectedServiceAccount = `runtime@${process.env.GCLOUD_PROJECT}.iam.gserviceaccount.com`;
846
- }
847
- if (meta.email !== expectedServiceAccount) {
848
- throw new Error(`Authorization header - Wrong service account, got ${meta?.email}, expected ${expectedServiceAccount}`);
849
- }
850
- if (!keyMeta.kid || !keyMeta.alg) {
851
- throw new Error("Malformed Authorization Header. KID is missing");
852
- }
853
- const key = await this.getKey(keyMeta.kid);
854
- if (!key) {
855
- throw new Error("Incorrect Authorization Header. The key has not been found in the Google JWKS set");
856
- }
857
- const importedKey = await (0, import_jose.importJWK)(key, keyMeta.alg);
858
- let validationCorrect = true;
859
- try {
860
- await (0, import_jose.jwtVerify)(authorizationHeader, importedKey, {
861
- issuer: ["https://accounts.google.com", "accounts.google.com"],
862
- clockTolerance: 0,
863
- audience: process.env.GCLOUD_PROJECT + "/" + this.triggerId
864
- });
865
- } catch (err) {
866
- validationCorrect = false;
867
- }
868
- if (!validationCorrect) {
869
- throw new Error("Incorrect Authorization Header. The JWT is not valid");
870
- }
871
- return validationCorrect;
872
- }
873
- async getKey(keyId) {
874
- const jwks = typeof global.jwks !== "undefined" ? global.jwks : null;
875
- let parsedJwks = null;
876
- if (jwks) {
877
- try {
878
- parsedJwks = JSON.parse(jwks);
879
- } catch (err) {
880
- err.message.toString();
881
- }
882
- }
883
- let keyData = parsedJwks?.keys?.find((x) => x.kid === keyId);
884
- let cachedItem;
885
- if (!keyData) {
886
- cachedItem = await this.firestore.collection(CACHE_TABLE).doc("google-jwks").get();
887
- if (cachedItem.exists && cachedItem.data()?.expires_at.toDate() > /* @__PURE__ */ new Date()) {
888
- keyData = cachedItem.data()?.value;
889
- }
890
- }
891
- keyData = parsedJwks?.keys.find((x) => x.kid === keyId);
892
- if (!keyData) {
893
- const result = await this.fetchJWKS();
894
- parsedJwks = result.jwks;
895
- if (cachedItem) {
896
- cachedItem.ref.set({
897
- value: parsedJwks,
898
- expires_at: result.expiresAt
899
- }, { merge: true });
900
- }
901
- keyData = parsedJwks?.keys.find((x) => x.kid === keyId);
902
- }
903
- if (parsedJwks && cachedItem) {
904
- global.jwks = JSON.stringify(parsedJwks);
905
- }
906
- return keyData;
907
- }
908
- async fetchJWKS() {
909
- const date = /* @__PURE__ */ new Date();
910
- let expiresAt = new Date(Date.now() + 1e3 * 60 * 60);
911
- const response = await fetch("https://www.googleapis.com/oauth2/v3/certs");
912
- const maxAgePair = response.headers.get("Cache-Control")?.split(",").map((x) => x.trim().split("=")).find((x) => x[0] === "max-age");
913
- if (maxAgePair && maxAgePair.length === 2) {
914
- expiresAt = new Date(date.getTime() + parseInt(maxAgePair[1], 10) * 1e3);
915
- }
916
- const jwks = await response.json();
917
- return {
918
- expiresAt,
919
- jwks
920
- };
921
- }
922
- };
923
- var jwks_default = JWKSHelper;
924
945
  // Annotate the CommonJS export names for ESM import in node:
925
946
  0 && (module.exports = {
926
947
  lib,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shushed/helpers",
3
- "version": "0.0.26",
3
+ "version": "0.0.27",
4
4
  "author": "",
5
5
  "license": "UNLICENSED",
6
6
  "description": "",