@shushed/helpers 0.0.25 → 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>;
@@ -28827,12 +28833,6 @@ declare class JWKSHelper extends Runtime {
28827
28833
  private fetchJWKS;
28828
28834
  }
28829
28835
 
28830
- declare const isPubSubRequest: (headers: Record<string, string>) => boolean;
28831
- declare const isCronMessage: (headers: Record<string, string>) => boolean;
28832
- declare const validateGoogleAuth: (opts: Opts & {
28833
- serviceAccount?: string | null;
28834
- }, headers: Record<string, string>, firestore: Firestore) => Promise<void>;
28835
-
28836
28836
  type TriggerOnExecuteOptions = {
28837
28837
  req: {
28838
28838
  status?: number;
package/dist/index.js CHANGED
@@ -346,8 +346,8 @@ var sanitize = new import_deep_redact.DeepRedact({
346
346
  replaceStringByLength: true
347
347
  });
348
348
 
349
- // src-public/env.ts
350
- var crypto = __toESM(require("crypto"));
349
+ // src-public/jwks.ts
350
+ var import_jose = require("jose");
351
351
 
352
352
  // src-public/runtime.ts
353
353
  var DEFAULT_BRANCH = "migratedprod";
@@ -373,16 +373,130 @@ var Runtime = class {
373
373
  }
374
374
  };
375
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
+
376
489
  // src-public/env.ts
490
+ var crypto = __toESM(require("crypto"));
377
491
  var EnvEngine = class extends Runtime {
378
492
  docRef;
379
493
  store;
380
494
  constructor(opts, firestore) {
381
495
  super(opts);
382
496
  this.docRef = {
383
- env: firestore?.doc(`0/0/${this.envName}`),
384
- workflow: firestore?.doc(`${this.workflowId}/0/${this.envName}`),
385
- trigger: firestore?.doc(`${this.workflowId}/${this.triggerId}/${this.envName}`)
497
+ env: firestore?.doc(`hush-${this.envName}/0-0`),
498
+ workflow: firestore?.doc(`hush-${this.envName}/${this.workflowId}-0`),
499
+ trigger: firestore?.doc(`hush-${this.envName}/${this.workflowId}-${this.triggerId}`)
386
500
  };
387
501
  this.store = {
388
502
  env: null,
@@ -828,120 +942,6 @@ var Secrets = class extends Runtime {
828
942
  }
829
943
  };
830
944
  var secret_default = Secrets;
831
-
832
- // src-public/jwks.ts
833
- var import_jose = require("jose");
834
- var CACHE_TABLE = "cache-global";
835
- var JWKSHelper = class extends Runtime {
836
- constructor(opts, firestore) {
837
- super(opts);
838
- this.firestore = firestore;
839
- }
840
- async validateAuthorizationHeader(authorizationHeader, expectedServiceAccount = null) {
841
- const splitted = authorizationHeader.split(".");
842
- const keyMeta = JSON.parse(Buffer.from(splitted[0], "base64").toString("utf-8")) || {};
843
- const meta = JSON.parse(Buffer.from(splitted[1], "base64").toString("utf-8")) || {};
844
- if (!meta.email_verified) {
845
- throw new Error("Authorization header - Email not verified");
846
- }
847
- if (expectedServiceAccount === null) {
848
- expectedServiceAccount = `runtime@${process.env.GCLOUD_PROJECT}.iam.gserviceaccount.com`;
849
- }
850
- if (meta.email !== expectedServiceAccount) {
851
- throw new Error(`Authorization header - Wrong service account, got ${meta?.email}, expected ${expectedServiceAccount}`);
852
- }
853
- if (!keyMeta.kid || !keyMeta.alg) {
854
- throw new Error("Malformed Authorization Header. KID is missing");
855
- }
856
- const key = await this.getKey(keyMeta.kid);
857
- if (!key) {
858
- throw new Error("Incorrect Authorization Header. The key has not been found in the Google JWKS set");
859
- }
860
- const importedKey = await (0, import_jose.importJWK)(key, keyMeta.alg);
861
- let validationCorrect = true;
862
- try {
863
- await (0, import_jose.jwtVerify)(authorizationHeader, importedKey, {
864
- issuer: ["https://accounts.google.com", "accounts.google.com"],
865
- clockTolerance: 0,
866
- audience: process.env.GCLOUD_PROJECT + "/" + this.triggerId
867
- });
868
- } catch (err) {
869
- validationCorrect = false;
870
- }
871
- if (!validationCorrect) {
872
- throw new Error("Incorrect Authorization Header. The JWT is not valid");
873
- }
874
- return validationCorrect;
875
- }
876
- async getKey(keyId) {
877
- const jwks = typeof global.jwks !== "undefined" ? global.jwks : null;
878
- let parsedJwks = null;
879
- if (jwks) {
880
- try {
881
- parsedJwks = JSON.parse(jwks);
882
- } catch (err) {
883
- err.message.toString();
884
- }
885
- }
886
- let keyData = parsedJwks?.keys?.find((x) => x.kid === keyId);
887
- let cachedItem;
888
- if (!keyData) {
889
- cachedItem = await this.firestore.collection(CACHE_TABLE).doc("google-jwks").get();
890
- if (cachedItem.exists && cachedItem.data()?.expires_at.toDate() > /* @__PURE__ */ new Date()) {
891
- keyData = cachedItem.data()?.value;
892
- }
893
- }
894
- keyData = parsedJwks?.keys.find((x) => x.kid === keyId);
895
- if (!keyData) {
896
- const result = await this.fetchJWKS();
897
- parsedJwks = result.jwks;
898
- if (cachedItem) {
899
- cachedItem.ref.set({
900
- value: parsedJwks,
901
- expires_at: result.expiresAt
902
- }, { merge: true });
903
- }
904
- keyData = parsedJwks?.keys.find((x) => x.kid === keyId);
905
- }
906
- if (parsedJwks && cachedItem) {
907
- global.jwks = JSON.stringify(parsedJwks);
908
- }
909
- return keyData;
910
- }
911
- async fetchJWKS() {
912
- const date = /* @__PURE__ */ new Date();
913
- let expiresAt = new Date(Date.now() + 1e3 * 60 * 60);
914
- const response = await fetch("https://www.googleapis.com/oauth2/v3/certs");
915
- const maxAgePair = response.headers.get("Cache-Control")?.split(",").map((x) => x.trim().split("=")).find((x) => x[0] === "max-age");
916
- if (maxAgePair && maxAgePair.length === 2) {
917
- expiresAt = new Date(date.getTime() + parseInt(maxAgePair[1], 10) * 1e3);
918
- }
919
- const jwks = await response.json();
920
- return {
921
- expiresAt,
922
- jwks
923
- };
924
- }
925
- };
926
- var jwks_default = JWKSHelper;
927
-
928
- // src-public/utils.ts
929
- var isPubSubRequest = (headers) => (headers["User-Agent"] || headers["user-agent"]) === "CloudPubSub-Google" || (headers["User-Agent"] || headers["user-agent"] || "").indexOf("APIs-Google") !== -1;
930
- var isCronMessage = (headers) => (headers["User-Agent"] || headers["user-agent"]) === "Google-Cloud-Scheduler";
931
- var validateGoogleAuth = async (opts, headers, firestore) => {
932
- const token = headers.authorization?.split(" ")[1];
933
- if (!token) {
934
- throw new Error(
935
- "Missing authorization header or invalid format, needs to be in format: Bearer <IdToken>"
936
- );
937
- }
938
- try {
939
- const jwksHelper = new jwks_default(opts, firestore);
940
- await jwksHelper.validateAuthorizationHeader(token, opts.serviceAccount || null);
941
- } catch (err) {
942
- throw new Error("Invalid authorization header " + err.message);
943
- }
944
- };
945
945
  // Annotate the CommonJS export names for ESM import in node:
946
946
  0 && (module.exports = {
947
947
  lib,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shushed/helpers",
3
- "version": "0.0.25",
3
+ "version": "0.0.27",
4
4
  "author": "",
5
5
  "license": "UNLICENSED",
6
6
  "description": "",