@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 +11 -2
- package/dist/index.js +120 -99
- package/package.json +1 -1
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 {
|
|
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/
|
|
347
|
-
var
|
|
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,
|