firebase-tools 9.19.0 → 9.23.0

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.
Files changed (101) hide show
  1. package/CHANGELOG.md +1 -3
  2. package/lib/api.js +3 -0
  3. package/lib/apiv2.js +7 -4
  4. package/lib/commands/crashlytics-symbols-upload.js +146 -0
  5. package/lib/commands/deploy.js +9 -1
  6. package/lib/commands/ext-configure.js +3 -1
  7. package/lib/commands/ext-dev-deprecate.js +63 -0
  8. package/lib/commands/ext-dev-undeprecate.js +56 -0
  9. package/lib/commands/ext-dev-unpublish.js +10 -3
  10. package/lib/commands/ext-export.js +44 -0
  11. package/lib/commands/ext-install.js +24 -3
  12. package/lib/commands/ext-uninstall.js +6 -0
  13. package/lib/commands/ext-update.js +10 -3
  14. package/lib/commands/functions-config-export.js +115 -0
  15. package/lib/commands/functions-delete.js +47 -25
  16. package/lib/commands/functions-list.js +12 -12
  17. package/lib/commands/index.js +9 -0
  18. package/lib/commands/init.js +3 -0
  19. package/lib/config.js +3 -2
  20. package/lib/deploy/extensions/args.js +2 -0
  21. package/lib/deploy/extensions/deploy.js +49 -0
  22. package/lib/deploy/extensions/deploymentSummary.js +52 -0
  23. package/lib/deploy/extensions/errors.js +31 -0
  24. package/lib/deploy/extensions/index.js +8 -0
  25. package/lib/deploy/extensions/params.js +39 -0
  26. package/lib/deploy/extensions/planner.js +94 -0
  27. package/lib/deploy/extensions/prepare.js +111 -0
  28. package/lib/deploy/extensions/release.js +43 -0
  29. package/lib/deploy/extensions/secrets.js +150 -0
  30. package/lib/deploy/extensions/tasks.js +98 -0
  31. package/lib/deploy/extensions/validate.js +17 -0
  32. package/lib/deploy/functions/backend.js +93 -115
  33. package/lib/deploy/functions/checkIam.js +8 -8
  34. package/lib/deploy/functions/containerCleaner.js +71 -14
  35. package/lib/deploy/functions/deploy.js +4 -10
  36. package/lib/deploy/functions/functionsDeployHelper.js +3 -68
  37. package/lib/deploy/functions/prepare.js +63 -27
  38. package/lib/deploy/functions/pricing.js +17 -17
  39. package/lib/deploy/functions/prompts.js +22 -21
  40. package/lib/deploy/functions/release/executor.js +39 -0
  41. package/lib/deploy/functions/release/fabricator.js +422 -0
  42. package/lib/deploy/functions/release/index.js +73 -0
  43. package/lib/deploy/functions/release/planner.js +162 -0
  44. package/lib/deploy/functions/release/reporter.js +165 -0
  45. package/lib/deploy/functions/release/sourceTokenScraper.js +28 -0
  46. package/lib/deploy/functions/release/timer.js +14 -0
  47. package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +129 -126
  48. package/lib/deploy/functions/runtimes/node/parseTriggers.js +41 -45
  49. package/lib/deploy/functions/triggerRegionHelper.js +40 -0
  50. package/lib/deploy/functions/validate.js +1 -24
  51. package/lib/deploy/index.js +10 -1
  52. package/lib/downloadUtils.js +37 -0
  53. package/lib/emulator/auth/apiSpec.js +549 -6
  54. package/lib/emulator/auth/handlers.js +4 -3
  55. package/lib/emulator/auth/operations.js +154 -14
  56. package/lib/emulator/auth/server.js +26 -15
  57. package/lib/emulator/auth/state.js +151 -13
  58. package/lib/emulator/download.js +2 -31
  59. package/lib/emulator/downloadableEmulators.js +7 -7
  60. package/lib/emulator/functionsEmulator.js +18 -4
  61. package/lib/emulator/functionsEmulatorRuntime.js +29 -7
  62. package/lib/emulator/storage/cloudFunctions.js +37 -7
  63. package/lib/extensions/askUserForConsent.js +14 -1
  64. package/lib/extensions/askUserForParam.js +81 -4
  65. package/lib/extensions/checkProjectBilling.js +7 -7
  66. package/lib/extensions/export.js +107 -0
  67. package/lib/extensions/extensionsApi.js +104 -21
  68. package/lib/extensions/extensionsHelper.js +6 -2
  69. package/lib/extensions/listExtensions.js +16 -11
  70. package/lib/extensions/paramHelper.js +9 -6
  71. package/lib/extensions/provisioningHelper.js +16 -3
  72. package/lib/extensions/refs.js +9 -1
  73. package/lib/extensions/secretsUtils.js +59 -0
  74. package/lib/extensions/updateHelper.js +12 -2
  75. package/lib/extensions/versionHelper.js +14 -0
  76. package/lib/extensions/warnings.js +33 -1
  77. package/lib/functional.js +8 -1
  78. package/lib/functions/env.js +10 -4
  79. package/lib/functions/runtimeConfigExport.js +137 -0
  80. package/lib/gcp/artifactregistry.js +16 -0
  81. package/lib/gcp/cloudfunctions.js +20 -74
  82. package/lib/gcp/cloudfunctionsv2.js +12 -90
  83. package/lib/gcp/cloudscheduler.js +22 -16
  84. package/lib/gcp/cloudtasks.js +143 -0
  85. package/lib/gcp/docker.js +7 -1
  86. package/lib/gcp/proto.js +2 -2
  87. package/lib/gcp/pubsub.js +1 -9
  88. package/lib/gcp/secretManager.js +132 -0
  89. package/lib/gcp/storage.js +16 -0
  90. package/lib/projectUtils.js +10 -1
  91. package/lib/requireInteractive.js +12 -0
  92. package/lib/utils.js +30 -1
  93. package/package.json +5 -4
  94. package/schema/firebase-config.json +9 -0
  95. package/lib/deploy/functions/deploymentPlanner.js +0 -113
  96. package/lib/deploy/functions/deploymentTimer.js +0 -23
  97. package/lib/deploy/functions/errorHandler.js +0 -75
  98. package/lib/deploy/functions/release.js +0 -116
  99. package/lib/deploy/functions/tasks.js +0 -324
  100. package/lib/functions/listFunctions.js +0 -10
  101. package/lib/functionsDelete.js +0 -60
@@ -7,7 +7,7 @@ const errors_1 = require("./errors");
7
7
  const widget_ui_1 = require("./widget_ui");
8
8
  function registerHandlers(app, getProjectStateByApiKey) {
9
9
  app.get(`/emulator/action`, (req, res) => {
10
- const { mode, oobCode, continueUrl, apiKey } = req.query;
10
+ const { mode, oobCode, continueUrl, apiKey, tenantId } = req.query;
11
11
  if (!apiKey) {
12
12
  return res.status(400).json({
13
13
  authEmulator: {
@@ -24,7 +24,7 @@ function registerHandlers(app, getProjectStateByApiKey) {
24
24
  },
25
25
  });
26
26
  }
27
- const state = getProjectStateByApiKey(apiKey);
27
+ const state = getProjectStateByApiKey(apiKey, tenantId);
28
28
  switch (mode) {
29
29
  case "recoverEmail": {
30
30
  const oob = state.validateOobCode(oobCode);
@@ -159,6 +159,7 @@ function registerHandlers(app, getProjectStateByApiKey) {
159
159
  res.set("Content-Type", "text/html; charset=utf-8");
160
160
  const apiKey = req.query.apiKey;
161
161
  const providerId = req.query.providerId;
162
+ const tenantId = req.query.tenantId;
162
163
  if (!apiKey || !providerId) {
163
164
  return res.status(400).json({
164
165
  authEmulator: {
@@ -166,7 +167,7 @@ function registerHandlers(app, getProjectStateByApiKey) {
166
167
  },
167
168
  });
168
169
  }
169
- const state = getProjectStateByApiKey(apiKey);
170
+ const state = getProjectStateByApiKey(apiKey, tenantId);
170
171
  const providerInfos = state.listProviderInfosByProviderId(providerId);
171
172
  const options = providerInfos
172
173
  .map((info) => `<li class="js-reuse-account mdc-list-item mdc-ripple-upgraded" tabindex="0" data-id-token="${encodeURIComponent(createFakeClaims(info))}">
@@ -50,6 +50,25 @@ exports.authOperations = {
50
50
  batchDelete,
51
51
  batchGet,
52
52
  },
53
+ tenants: {
54
+ create: createTenant,
55
+ delete: deleteTenant,
56
+ get: getTenant,
57
+ list: listTenants,
58
+ patch: updateTenant,
59
+ createSessionCookie,
60
+ accounts: {
61
+ _: signUp,
62
+ batchCreate,
63
+ batchDelete,
64
+ batchGet,
65
+ delete: deleteAccount,
66
+ lookup,
67
+ query: queryAccounts,
68
+ sendOobCode,
69
+ update: setAccountInfo,
70
+ },
71
+ },
53
72
  },
54
73
  },
55
74
  securetoken: {
@@ -83,6 +102,7 @@ const MFA_INELIGIBLE_PROVIDER = new Set([
83
102
  ]);
84
103
  function signUp(state, reqBody, ctx) {
85
104
  var _a;
105
+ errors_1.assert(!state.disableAuth, "PROJECT_DISABLED");
86
106
  errors_1.assert(state.usageMode !== state_1.UsageMode.PASSTHROUGH, "UNSUPPORTED_PASSTHROUGH_OPERATION");
87
107
  let provider;
88
108
  const updates = {
@@ -115,9 +135,11 @@ function signUp(state, reqBody, ctx) {
115
135
  errors_1.assert(reqBody.email, "MISSING_EMAIL");
116
136
  errors_1.assert(reqBody.password, "MISSING_PASSWORD");
117
137
  provider = state_1.PROVIDER_PASSWORD;
138
+ errors_1.assert(state.allowPasswordSignup, "OPERATION_NOT_ALLOWED");
118
139
  }
119
140
  else {
120
141
  provider = state_1.PROVIDER_ANONYMOUS;
142
+ errors_1.assert(state.enableAnonymousUser, "ADMIN_ONLY_OPERATION");
121
143
  }
122
144
  }
123
145
  if (reqBody.email) {
@@ -138,6 +160,9 @@ function signUp(state, reqBody, ctx) {
138
160
  generateEnrollmentIds: true,
139
161
  });
140
162
  }
163
+ if (state instanceof state_1.TenantProjectState) {
164
+ updates.tenantId = state.tenantId;
165
+ }
141
166
  let user;
142
167
  if (reqBody.idToken) {
143
168
  ({ user } = parseIdToken(state, reqBody.idToken));
@@ -158,6 +183,7 @@ function signUp(state, reqBody, ctx) {
158
183
  }
159
184
  function lookup(state, reqBody, ctx) {
160
185
  var _a, _b, _c, _d, _e;
186
+ errors_1.assert(!state.disableAuth, "PROJECT_DISABLED");
161
187
  const seenLocalIds = new Set();
162
188
  const users = [];
163
189
  function tryAddUser(maybeUser) {
@@ -198,6 +224,7 @@ function lookup(state, reqBody, ctx) {
198
224
  }
199
225
  function batchCreate(state, reqBody) {
200
226
  var _a, _b;
227
+ errors_1.assert(!state.disableAuth, "PROJECT_DISABLED");
201
228
  errors_1.assert(state.usageMode !== state_1.UsageMode.PASSTHROUGH, "UNSUPPORTED_PASSTHROUGH_OPERATION");
202
229
  errors_1.assert((_a = reqBody.users) === null || _a === void 0 ? void 0 : _a.length, "MISSING_USER_ACCOUNT");
203
230
  if (reqBody.sanityCheck) {
@@ -238,6 +265,12 @@ function batchCreate(state, reqBody) {
238
265
  photoUrl: userInfo.photoUrl,
239
266
  lastLoginAt: userInfo.lastLoginAt,
240
267
  };
268
+ if (userInfo.tenantId) {
269
+ errors_1.assert(state instanceof state_1.TenantProjectState && state.tenantId === userInfo.tenantId, "Tenant id in userInfo does not match the tenant id in request.");
270
+ }
271
+ if (state instanceof state_1.TenantProjectState) {
272
+ fields.tenantId = state.tenantId;
273
+ }
241
274
  if (userInfo.passwordHash) {
242
275
  fields.passwordHash = userInfo.passwordHash;
243
276
  fields.salt = userInfo.salt;
@@ -369,6 +402,7 @@ function batchDelete(state, reqBody) {
369
402
  return { errors: errors.length ? errors : undefined };
370
403
  }
371
404
  function batchGet(state, reqBody, ctx) {
405
+ errors_1.assert(!state.disableAuth, "PROJECT_DISABLED");
372
406
  const maxResults = Math.min(Math.floor(ctx.params.query.maxResults) || 20, 1000);
373
407
  const users = state.queryUsers({}, { sortByField: "localId", order: "ASC", startToken: ctx.params.query.nextPageToken });
374
408
  let newPageToken = undefined;
@@ -386,6 +420,7 @@ function batchGet(state, reqBody, ctx) {
386
420
  }
387
421
  function createAuthUri(state, reqBody) {
388
422
  var _a;
423
+ errors_1.assert(!state.disableAuth, "PROJECT_DISABLED");
389
424
  errors_1.assert(state.usageMode !== state_1.UsageMode.PASSTHROUGH, "UNSUPPORTED_PASSTHROUGH_OPERATION");
390
425
  const sessionId = reqBody.sessionId || utils_1.randomId(27);
391
426
  if (reqBody.providerId) {
@@ -461,6 +496,7 @@ function createSessionCookie(state, reqBody, ctx) {
461
496
  }
462
497
  function deleteAccount(state, reqBody, ctx) {
463
498
  var _a;
499
+ errors_1.assert(!state.disableAuth, "PROJECT_DISABLED");
464
500
  let user;
465
501
  if ((_a = ctx.security) === null || _a === void 0 ? void 0 : _a.Oauth2) {
466
502
  errors_1.assert(reqBody.localId, "MISSING_LOCAL_ID");
@@ -478,6 +514,8 @@ function deleteAccount(state, reqBody, ctx) {
478
514
  };
479
515
  }
480
516
  function getProjects(state) {
517
+ errors_1.assert(!state.disableAuth, "PROJECT_DISABLED");
518
+ errors_1.assert(state instanceof state_1.AgentProjectState, "UNSUPPORTED_TENANT_OPERATION");
481
519
  return {
482
520
  projectId: state.projectNumber,
483
521
  authorizedDomains: [
@@ -485,7 +523,8 @@ function getProjects(state) {
485
523
  ],
486
524
  };
487
525
  }
488
- function getRecaptchaParams() {
526
+ function getRecaptchaParams(state) {
527
+ errors_1.assert(!state.disableAuth, "PROJECT_DISABLED");
489
528
  return {
490
529
  kind: "identitytoolkit#GetRecaptchaParamResponse",
491
530
  recaptchaStoken: "This-is-a-fake-token__Dont-send-this-to-the-Recaptcha-service__The-Auth-Emulator-does-not-support-Recaptcha",
@@ -494,6 +533,7 @@ function getRecaptchaParams() {
494
533
  }
495
534
  function queryAccounts(state, reqBody) {
496
535
  var _a;
536
+ errors_1.assert(!state.disableAuth, "PROJECT_DISABLED");
497
537
  if ((_a = reqBody.expression) === null || _a === void 0 ? void 0 : _a.length) {
498
538
  throw new errors_1.NotImplementedError("expression is not implemented.");
499
539
  }
@@ -530,7 +570,9 @@ function queryAccounts(state, reqBody) {
530
570
  }
531
571
  function resetPassword(state, reqBody) {
532
572
  var _a;
573
+ errors_1.assert(!state.disableAuth, "PROJECT_DISABLED");
533
574
  errors_1.assert(state.usageMode !== state_1.UsageMode.PASSTHROUGH, "UNSUPPORTED_PASSTHROUGH_OPERATION");
575
+ errors_1.assert(state.allowPasswordSignup, "PASSWORD_LOGIN_DISABLED");
534
576
  errors_1.assert(reqBody.oobCode, "MISSING_OOB_CODE");
535
577
  const oob = state.validateOobCode(reqBody.oobCode);
536
578
  errors_1.assert(oob, "INVALID_OOB_CODE");
@@ -559,6 +601,7 @@ function resetPassword(state, reqBody) {
559
601
  exports.resetPassword = resetPassword;
560
602
  function sendOobCode(state, reqBody, ctx) {
561
603
  var _a;
604
+ errors_1.assert(!state.disableAuth, "PROJECT_DISABLED");
562
605
  errors_1.assert(state.usageMode !== state_1.UsageMode.PASSTHROUGH, "UNSUPPORTED_PASSTHROUGH_OPERATION");
563
606
  errors_1.assert(reqBody.requestType && reqBody.requestType !== "OOB_REQ_TYPE_UNSPECIFIED", "MISSING_REQ_TYPE");
564
607
  if (reqBody.returnOobLink) {
@@ -571,6 +614,7 @@ function sendOobCode(state, reqBody, ctx) {
571
614
  let mode;
572
615
  switch (reqBody.requestType) {
573
616
  case "EMAIL_SIGNIN":
617
+ errors_1.assert(state.enableEmailLinkSignin, "OPERATION_NOT_ALLOWED");
574
618
  mode = "signIn";
575
619
  errors_1.assert(reqBody.email, "MISSING_EMAIL");
576
620
  email = utils_1.canonicalizeEmailAddress(reqBody.email);
@@ -625,6 +669,8 @@ function sendOobCode(state, reqBody, ctx) {
625
669
  }
626
670
  function sendVerificationCode(state, reqBody) {
627
671
  var _a;
672
+ errors_1.assert(!state.disableAuth, "PROJECT_DISABLED");
673
+ errors_1.assert(state instanceof state_1.AgentProjectState, "UNSUPPORTED_TENANT_OPERATION");
628
674
  errors_1.assert(state.usageMode !== state_1.UsageMode.PASSTHROUGH, "UNSUPPORTED_PASSTHROUGH_OPERATION");
629
675
  errors_1.assert(reqBody.phoneNumber && utils_1.isValidPhoneNumber(reqBody.phoneNumber), "INVALID_PHONE_NUMBER : Invalid format.");
630
676
  const user = state.getUserByPhoneNumber(reqBody.phoneNumber);
@@ -637,6 +683,7 @@ function sendVerificationCode(state, reqBody) {
637
683
  }
638
684
  function setAccountInfo(state, reqBody, ctx) {
639
685
  var _a;
686
+ errors_1.assert(!state.disableAuth, "PROJECT_DISABLED");
640
687
  errors_1.assert(state.usageMode !== state_1.UsageMode.PASSTHROUGH, "UNSUPPORTED_PASSTHROUGH_OPERATION");
641
688
  const url = utils_1.authEmulatorUrl(ctx.req);
642
689
  return setAccountInfoImpl(state, reqBody, {
@@ -831,6 +878,9 @@ function createOobRecord(state, email, url, params) {
831
878
  if (params.continueUrl) {
832
879
  url.searchParams.set("continueUrl", params.continueUrl);
833
880
  }
881
+ if (state instanceof state_1.TenantProjectState) {
882
+ url.searchParams.set("tenantId", state.tenantId);
883
+ }
834
884
  return url.toString();
835
885
  });
836
886
  return oobRecord;
@@ -859,6 +909,7 @@ function logOobMessage(oobRecord) {
859
909
  }
860
910
  function signInWithCustomToken(state, reqBody) {
861
911
  var _a;
912
+ errors_1.assert(!state.disableAuth, "PROJECT_DISABLED");
862
913
  errors_1.assert(reqBody.token, "MISSING_CUSTOM_TOKEN");
863
914
  let payload;
864
915
  if (reqBody.token.startsWith("{")) {
@@ -871,6 +922,9 @@ function signInWithCustomToken(state, reqBody) {
871
922
  }
872
923
  else {
873
924
  const decoded = jsonwebtoken_1.decode(reqBody.token, { complete: true });
925
+ if (state instanceof state_1.TenantProjectState) {
926
+ errors_1.assert((decoded === null || decoded === void 0 ? void 0 : decoded.payload.tenant_id) === state.tenantId, "TENANT_ID_MISMATCH");
927
+ }
874
928
  errors_1.assert(decoded, "INVALID_CUSTOM_TOKEN : Invalid assertion format");
875
929
  if (decoded.header.alg !== "none") {
876
930
  emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.AUTH).log("WARN", "Received a signed custom token. Auth Emulator does not validate JWTs and IS NOT SECURE");
@@ -891,6 +945,7 @@ function signInWithCustomToken(state, reqBody) {
891
945
  const updates = {
892
946
  customAuth: true,
893
947
  lastLoginAt: Date.now().toString(),
948
+ tenantId: state instanceof state_1.TenantProjectState ? state.tenantId : undefined,
894
949
  };
895
950
  if (user) {
896
951
  errors_1.assert(!user.disabled, "USER_DISABLED");
@@ -906,6 +961,8 @@ function signInWithCustomToken(state, reqBody) {
906
961
  }
907
962
  function signInWithEmailLink(state, reqBody) {
908
963
  var _a;
964
+ errors_1.assert(!state.disableAuth, "PROJECT_DISABLED");
965
+ errors_1.assert(state.enableEmailLinkSignin, "OPERATION_NOT_ALLOWED");
909
966
  errors_1.assert(state.usageMode !== state_1.UsageMode.PASSTHROUGH, "UNSUPPORTED_PASSTHROUGH_OPERATION");
910
967
  const userFromIdToken = reqBody.idToken ? parseIdToken(state, reqBody.idToken).user : undefined;
911
968
  errors_1.assert(reqBody.email, "MISSING_EMAIL");
@@ -920,6 +977,9 @@ function signInWithEmailLink(state, reqBody) {
920
977
  emailVerified: true,
921
978
  emailLinkSignin: true,
922
979
  };
980
+ if (state instanceof state_1.TenantProjectState) {
981
+ updates.tenantId = state.tenantId;
982
+ }
923
983
  let user = state.getUserByEmail(email);
924
984
  const isNewUser = !user && !userFromIdToken;
925
985
  if (!user) {
@@ -941,7 +1001,7 @@ function signInWithEmailLink(state, reqBody) {
941
1001
  localId: user.localId,
942
1002
  isNewUser,
943
1003
  };
944
- if ((_a = user.mfaInfo) === null || _a === void 0 ? void 0 : _a.length) {
1004
+ if ((state.mfaConfig.state === "ENABLED" || state.mfaConfig.state === "MANDATORY") && ((_a = user.mfaInfo) === null || _a === void 0 ? void 0 : _a.length)) {
945
1005
  return Object.assign(Object.assign({}, response), mfaPending(state, user, state_1.PROVIDER_PASSWORD));
946
1006
  }
947
1007
  else {
@@ -951,6 +1011,7 @@ function signInWithEmailLink(state, reqBody) {
951
1011
  }
952
1012
  function signInWithIdp(state, reqBody) {
953
1013
  var _a, _b;
1014
+ errors_1.assert(!state.disableAuth, "PROJECT_DISABLED");
954
1015
  errors_1.assert(state.usageMode !== state_1.UsageMode.PASSTHROUGH, "UNSUPPORTED_PASSTHROUGH_OPERATION");
955
1016
  if (reqBody.returnRefreshToken) {
956
1017
  throw new errors_1.NotImplementedError("returnRefreshToken is not implemented yet.");
@@ -1023,7 +1084,7 @@ function signInWithIdp(state, reqBody) {
1023
1084
  };
1024
1085
  let user;
1025
1086
  if (response.isNewUser) {
1026
- user = state.createUser(Object.assign(Object.assign({}, accountUpdates.fields), { lastLoginAt: Date.now().toString(), providerUserInfo: [providerUserInfo] }));
1087
+ user = state.createUser(Object.assign(Object.assign({}, accountUpdates.fields), { lastLoginAt: Date.now().toString(), providerUserInfo: [providerUserInfo], tenantId: state instanceof state_1.TenantProjectState ? state.tenantId : undefined }));
1027
1088
  response.localId = user.localId;
1028
1089
  }
1029
1090
  else {
@@ -1037,7 +1098,10 @@ function signInWithIdp(state, reqBody) {
1037
1098
  if (user.email === response.email) {
1038
1099
  response.emailVerified = user.emailVerified;
1039
1100
  }
1040
- if ((_b = user.mfaInfo) === null || _b === void 0 ? void 0 : _b.length) {
1101
+ if (state instanceof state_1.TenantProjectState) {
1102
+ response.tenantId = state.tenantId;
1103
+ }
1104
+ if ((state.mfaConfig.state === "ENABLED" || state.mfaConfig.state === "MANDATORY") && ((_b = user.mfaInfo) === null || _b === void 0 ? void 0 : _b.length)) {
1041
1105
  return Object.assign(Object.assign({}, response), mfaPending(state, user, providerId));
1042
1106
  }
1043
1107
  else {
@@ -1047,6 +1111,8 @@ function signInWithIdp(state, reqBody) {
1047
1111
  }
1048
1112
  function signInWithPassword(state, reqBody) {
1049
1113
  var _a;
1114
+ errors_1.assert(!state.disableAuth, "PROJECT_DISABLED");
1115
+ errors_1.assert(state.allowPasswordSignup, "PASSWORD_LOGIN_DISABLED");
1050
1116
  errors_1.assert(state.usageMode !== state_1.UsageMode.PASSTHROUGH, "UNSUPPORTED_PASSTHROUGH_OPERATION");
1051
1117
  errors_1.assert(reqBody.email, "MISSING_EMAIL");
1052
1118
  errors_1.assert(reqBody.password, "MISSING_PASSWORD");
@@ -1068,7 +1134,7 @@ function signInWithPassword(state, reqBody) {
1068
1134
  localId: user.localId,
1069
1135
  email,
1070
1136
  };
1071
- if ((_a = user.mfaInfo) === null || _a === void 0 ? void 0 : _a.length) {
1137
+ if ((state.mfaConfig.state === "ENABLED" || state.mfaConfig.state === "MANDATORY") && ((_a = user.mfaInfo) === null || _a === void 0 ? void 0 : _a.length)) {
1072
1138
  return Object.assign(Object.assign({}, response), mfaPending(state, user, state_1.PROVIDER_PASSWORD));
1073
1139
  }
1074
1140
  else {
@@ -1078,6 +1144,8 @@ function signInWithPassword(state, reqBody) {
1078
1144
  }
1079
1145
  function signInWithPhoneNumber(state, reqBody) {
1080
1146
  var _a;
1147
+ errors_1.assert(!state.disableAuth, "PROJECT_DISABLED");
1148
+ errors_1.assert(state instanceof state_1.AgentProjectState, "UNSUPPORTED_TENANT_OPERATION");
1081
1149
  errors_1.assert(state.usageMode !== state_1.UsageMode.PASSTHROUGH, "UNSUPPORTED_PASSTHROUGH_OPERATION");
1082
1150
  let phoneNumber;
1083
1151
  if (reqBody.temporaryProof) {
@@ -1191,7 +1259,9 @@ function listVerificationCodesInProject(state) {
1191
1259
  };
1192
1260
  }
1193
1261
  function mfaEnrollmentStart(state, reqBody) {
1194
- var _a;
1262
+ var _a, _b;
1263
+ errors_1.assert(!state.disableAuth, "PROJECT_DISABLED");
1264
+ errors_1.assert((state.mfaConfig.state === "ENABLED" || state.mfaConfig.state === "MANDATORY") && ((_a = state.mfaConfig.enabledProviders) === null || _a === void 0 ? void 0 : _a.includes("PHONE_SMS")), "OPERATION_NOT_ALLOWED : SMS based MFA not enabled.");
1195
1265
  errors_1.assert(reqBody.idToken, "MISSING_ID_TOKEN");
1196
1266
  const { user, signInProvider } = parseIdToken(state, reqBody.idToken);
1197
1267
  errors_1.assert(!MFA_INELIGIBLE_PROVIDER.has(signInProvider), "UNSUPPORTED_FIRST_FACTOR : MFA is not available for the given first factor.");
@@ -1199,7 +1269,7 @@ function mfaEnrollmentStart(state, reqBody) {
1199
1269
  errors_1.assert(reqBody.phoneEnrollmentInfo, "INVALID_ARGUMENT : ((Missing phoneEnrollmentInfo.))");
1200
1270
  const phoneNumber = reqBody.phoneEnrollmentInfo.phoneNumber;
1201
1271
  errors_1.assert(phoneNumber && utils_1.isValidPhoneNumber(phoneNumber), "INVALID_PHONE_NUMBER : Invalid format.");
1202
- errors_1.assert(!((_a = user.mfaInfo) === null || _a === void 0 ? void 0 : _a.some((enrollment) => enrollment.unobfuscatedPhoneInfo === phoneNumber)), "SECOND_FACTOR_EXISTS : Phone number already enrolled as second factor for this account.");
1272
+ errors_1.assert(!((_b = user.mfaInfo) === null || _b === void 0 ? void 0 : _b.some((enrollment) => enrollment.unobfuscatedPhoneInfo === phoneNumber)), "SECOND_FACTOR_EXISTS : Phone number already enrolled as second factor for this account.");
1203
1273
  const { sessionInfo, code } = state.createVerificationCode(phoneNumber);
1204
1274
  emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.AUTH).log("BULLET", `To enroll MFA with ${phoneNumber}, use the code ${code}.`);
1205
1275
  return {
@@ -1209,7 +1279,9 @@ function mfaEnrollmentStart(state, reqBody) {
1209
1279
  };
1210
1280
  }
1211
1281
  function mfaEnrollmentFinalize(state, reqBody) {
1212
- var _a;
1282
+ var _a, _b;
1283
+ errors_1.assert(!state.disableAuth, "PROJECT_DISABLED");
1284
+ errors_1.assert((state.mfaConfig.state === "ENABLED" || state.mfaConfig.state === "MANDATORY") && ((_a = state.mfaConfig.enabledProviders) === null || _a === void 0 ? void 0 : _a.includes("PHONE_SMS")), "OPERATION_NOT_ALLOWED : SMS based MFA not enabled.");
1213
1285
  errors_1.assert(reqBody.idToken, "MISSING_ID_TOKEN");
1214
1286
  let { user, signInProvider } = parseIdToken(state, reqBody.idToken);
1215
1287
  errors_1.assert(!MFA_INELIGIBLE_PROVIDER.has(signInProvider), "UNSUPPORTED_FIRST_FACTOR : MFA is not available for the given first factor.");
@@ -1221,7 +1293,7 @@ function mfaEnrollmentFinalize(state, reqBody) {
1221
1293
  errors_1.assert(code, "MISSING_CODE");
1222
1294
  errors_1.assert(sessionInfo, "MISSING_SESSION_INFO");
1223
1295
  const phoneNumber = verifyPhoneNumber(state, sessionInfo, code);
1224
- errors_1.assert(!((_a = user.mfaInfo) === null || _a === void 0 ? void 0 : _a.some((enrollment) => enrollment.unobfuscatedPhoneInfo === phoneNumber)), "SECOND_FACTOR_EXISTS : Phone number already enrolled as second factor for this account.");
1296
+ errors_1.assert(!((_b = user.mfaInfo) === null || _b === void 0 ? void 0 : _b.some((enrollment) => enrollment.unobfuscatedPhoneInfo === phoneNumber)), "SECOND_FACTOR_EXISTS : Phone number already enrolled as second factor for this account.");
1225
1297
  const existingFactors = user.mfaInfo || [];
1226
1298
  const existingIds = new Set();
1227
1299
  for (const { mfaEnrollmentId } of existingFactors) {
@@ -1248,6 +1320,7 @@ function mfaEnrollmentFinalize(state, reqBody) {
1248
1320
  };
1249
1321
  }
1250
1322
  function mfaEnrollmentWithdraw(state, reqBody) {
1323
+ errors_1.assert(!state.disableAuth, "PROJECT_DISABLED");
1251
1324
  errors_1.assert(reqBody.idToken, "MISSING_ID_TOKEN");
1252
1325
  let { user, signInProvider } = parseIdToken(state, reqBody.idToken);
1253
1326
  errors_1.assert(user.mfaInfo, "MFA_ENROLLMENT_NOT_FOUND");
@@ -1257,11 +1330,13 @@ function mfaEnrollmentWithdraw(state, reqBody) {
1257
1330
  return Object.assign({}, issueTokens(state, user, signInProvider));
1258
1331
  }
1259
1332
  function mfaSignInStart(state, reqBody) {
1260
- var _a;
1333
+ var _a, _b;
1334
+ errors_1.assert(!state.disableAuth, "PROJECT_DISABLED");
1335
+ errors_1.assert((state.mfaConfig.state === "ENABLED" || state.mfaConfig.state === "MANDATORY") && ((_a = state.mfaConfig.enabledProviders) === null || _a === void 0 ? void 0 : _a.includes("PHONE_SMS")), "OPERATION_NOT_ALLOWED : SMS based MFA not enabled.");
1261
1336
  errors_1.assert(reqBody.mfaPendingCredential, "MISSING_MFA_PENDING_CREDENTIAL : Request does not have MFA pending credential.");
1262
1337
  errors_1.assert(reqBody.mfaEnrollmentId, "MISSING_MFA_ENROLLMENT_ID : No second factor identifier is provided.");
1263
1338
  const { user } = parsePendingCredential(state, reqBody.mfaPendingCredential);
1264
- const enrollment = (_a = user.mfaInfo) === null || _a === void 0 ? void 0 : _a.find((factor) => factor.mfaEnrollmentId === reqBody.mfaEnrollmentId);
1339
+ const enrollment = (_b = user.mfaInfo) === null || _b === void 0 ? void 0 : _b.find((factor) => factor.mfaEnrollmentId === reqBody.mfaEnrollmentId);
1265
1340
  errors_1.assert(enrollment, "MFA_ENROLLMENT_NOT_FOUND");
1266
1341
  const phoneNumber = enrollment.unobfuscatedPhoneInfo;
1267
1342
  errors_1.assert(phoneNumber, "INVALID_ARGUMENT : MFA provider not supported!");
@@ -1274,7 +1349,9 @@ function mfaSignInStart(state, reqBody) {
1274
1349
  };
1275
1350
  }
1276
1351
  function mfaSignInFinalize(state, reqBody) {
1277
- var _a;
1352
+ var _a, _b;
1353
+ errors_1.assert(!state.disableAuth, "PROJECT_DISABLED");
1354
+ errors_1.assert((state.mfaConfig.state === "ENABLED" || state.mfaConfig.state === "MANDATORY") && ((_a = state.mfaConfig.enabledProviders) === null || _a === void 0 ? void 0 : _a.includes("PHONE_SMS")), "OPERATION_NOT_ALLOWED : SMS based MFA not enabled.");
1278
1355
  errors_1.assert(reqBody.mfaPendingCredential, "MISSING_CREDENTIAL : Please set MFA Pending Credential.");
1279
1356
  errors_1.assert(reqBody.phoneVerificationInfo, "INVALID_ARGUMENT : MFA provider not supported!");
1280
1357
  if (reqBody.phoneVerificationInfo.androidVerificationProof) {
@@ -1285,7 +1362,7 @@ function mfaSignInFinalize(state, reqBody) {
1285
1362
  errors_1.assert(sessionInfo, "MISSING_SESSION_INFO");
1286
1363
  const phoneNumber = verifyPhoneNumber(state, sessionInfo, code);
1287
1364
  let { user, signInProvider } = parsePendingCredential(state, reqBody.mfaPendingCredential);
1288
- const enrollment = (_a = user.mfaInfo) === null || _a === void 0 ? void 0 : _a.find((enrollment) => enrollment.unobfuscatedPhoneInfo == phoneNumber);
1365
+ const enrollment = (_b = user.mfaInfo) === null || _b === void 0 ? void 0 : _b.find((enrollment) => enrollment.unobfuscatedPhoneInfo == phoneNumber);
1289
1366
  errors_1.assert(enrollment && enrollment.mfaEnrollmentId, "MFA_ENROLLMENT_NOT_FOUND");
1290
1367
  user = state.updateUserByLocalId(user.localId, { lastLoginAt: Date.now().toString() });
1291
1368
  errors_1.assert(!user.disabled, "USER_DISABLED");
@@ -1317,6 +1394,7 @@ function hashPassword(password, salt) {
1317
1394
  function issueTokens(state, user, signInProvider, { extraClaims, secondFactor, } = {}) {
1318
1395
  user = state.updateUserByLocalId(user.localId, { lastRefreshAt: new Date().toISOString() });
1319
1396
  const usageMode = state.usageMode === state_1.UsageMode.PASSTHROUGH ? "passthrough" : undefined;
1397
+ const tenantId = state instanceof state_1.TenantProjectState ? state.tenantId : undefined;
1320
1398
  const expiresInSeconds = 60 * 60;
1321
1399
  const idToken = generateJwt(user, {
1322
1400
  projectId: state.projectId,
@@ -1325,6 +1403,7 @@ function issueTokens(state, user, signInProvider, { extraClaims, secondFactor, }
1325
1403
  extraClaims,
1326
1404
  secondFactor,
1327
1405
  usageMode,
1406
+ tenantId,
1328
1407
  });
1329
1408
  const refreshToken = state.usageMode === state_1.UsageMode.DEFAULT
1330
1409
  ? state.createRefreshTokenFor(user, signInProvider, {
@@ -1345,6 +1424,10 @@ function parseIdToken(state, idToken) {
1345
1424
  if (decoded.header.alg !== "none") {
1346
1425
  emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.AUTH).log("WARN", "Received a signed JWT. Auth Emulator does not validate JWTs and IS NOT SECURE");
1347
1426
  }
1427
+ if (decoded.payload.firebase.tenant) {
1428
+ errors_1.assert(state instanceof state_1.TenantProjectState, "((Parsed token that belongs to tenant in a non-tenant project.))");
1429
+ errors_1.assert(decoded.payload.firebase.tenant === state.tenantId, "TENANT_ID_MISMATCH");
1430
+ }
1348
1431
  const user = state.getUserByLocalId(decoded.payload.user_id);
1349
1432
  errors_1.assert(user, "USER_NOT_FOUND");
1350
1433
  errors_1.assert(!user.validSince || decoded.payload.iat >= Number(user.validSince), "TOKEN_EXPIRED");
@@ -1352,7 +1435,7 @@ function parseIdToken(state, idToken) {
1352
1435
  const signInProvider = decoded.payload.firebase.sign_in_provider;
1353
1436
  return { user, signInProvider, payload: decoded.payload };
1354
1437
  }
1355
- function generateJwt(user, { projectId, signInProvider, expiresInSeconds, extraClaims = {}, secondFactor, usageMode, }) {
1438
+ function generateJwt(user, { projectId, signInProvider, expiresInSeconds, extraClaims = {}, secondFactor, usageMode, tenantId, }) {
1356
1439
  const identities = {};
1357
1440
  if (user.email) {
1358
1441
  identities["email"] = [user.email];
@@ -1375,6 +1458,7 @@ function generateJwt(user, { projectId, signInProvider, expiresInSeconds, extraC
1375
1458
  second_factor_identifier: secondFactor === null || secondFactor === void 0 ? void 0 : secondFactor.identifier,
1376
1459
  sign_in_second_factor: secondFactor === null || secondFactor === void 0 ? void 0 : secondFactor.provider,
1377
1460
  usage_mode: usageMode,
1461
+ tenant: tenantId,
1378
1462
  } });
1379
1463
  const jwtStr = jsonwebtoken_1.sign(customPayloadFields, "", {
1380
1464
  algorithm: "none",
@@ -1667,6 +1751,9 @@ function mfaPending(state, user, signInProvider) {
1667
1751
  signInProvider,
1668
1752
  projectId: state.projectId,
1669
1753
  };
1754
+ if (state instanceof state_1.TenantProjectState) {
1755
+ pendingCredentialPayload.tenantId = state.tenantId;
1756
+ }
1670
1757
  const mfaPendingCredential = Buffer.from(JSON.stringify(pendingCredentialPayload), "utf8").toString("base64");
1671
1758
  return { mfaPendingCredential, mfaInfo: user.mfaInfo.map(redactMfaInfo) };
1672
1759
  }
@@ -1704,8 +1791,61 @@ function parsePendingCredential(state, pendingCredential) {
1704
1791
  }
1705
1792
  errors_1.assert(pendingCredentialPayload._AuthEmulatorMfaPendingCredential, "((Invalid phoneVerificationInfo.mfaPendingCredential.))");
1706
1793
  errors_1.assert(pendingCredentialPayload.projectId === state.projectId, "INVALID_PROJECT_ID : Project ID does not match MFA pending credential.");
1794
+ if (state instanceof state_1.TenantProjectState) {
1795
+ errors_1.assert(pendingCredentialPayload.tenantId === state.tenantId, "INVALID_PROJECT_ID : Project ID does not match MFA pending credential.");
1796
+ }
1707
1797
  const { localId, signInProvider } = pendingCredentialPayload;
1708
1798
  const user = state.getUserByLocalId(localId);
1709
1799
  errors_1.assert(user, "((User in pendingCredentialPayload does not exist.))");
1710
1800
  return { user, signInProvider };
1711
1801
  }
1802
+ function createTenant(state, reqBody) {
1803
+ var _a, _b, _c, _d, _e;
1804
+ if (!(state instanceof state_1.AgentProjectState)) {
1805
+ throw new errors_1.InternalError("INTERNAL_ERROR: Can only create tenant in agent project", "INTERNAL");
1806
+ }
1807
+ const mfaConfig = (_a = reqBody.mfaConfig) !== null && _a !== void 0 ? _a : {};
1808
+ if (!("state" in mfaConfig)) {
1809
+ mfaConfig.state = "DISABLED";
1810
+ }
1811
+ if (!("enabledProviders" in mfaConfig)) {
1812
+ mfaConfig.enabledProviders = [];
1813
+ }
1814
+ const tenant = {
1815
+ displayName: reqBody.displayName,
1816
+ allowPasswordSignup: (_b = reqBody.allowPasswordSignup) !== null && _b !== void 0 ? _b : false,
1817
+ enableEmailLinkSignin: (_c = reqBody.enableEmailLinkSignin) !== null && _c !== void 0 ? _c : false,
1818
+ enableAnonymousUser: (_d = reqBody.enableAnonymousUser) !== null && _d !== void 0 ? _d : false,
1819
+ disableAuth: (_e = reqBody.disableAuth) !== null && _e !== void 0 ? _e : false,
1820
+ mfaConfig: mfaConfig,
1821
+ tenantId: "",
1822
+ };
1823
+ return state.createTenant(tenant);
1824
+ }
1825
+ function listTenants(state, reqBody, ctx) {
1826
+ errors_1.assert(state instanceof state_1.AgentProjectState, "((Can only list tenants in agent project.))");
1827
+ const pageSize = Math.min(Math.floor(ctx.params.query.pageSize) || 20, 1000);
1828
+ const tenants = state.listTenants(ctx.params.query.pageToken);
1829
+ let nextPageToken = undefined;
1830
+ if (pageSize > 0 && tenants.length >= pageSize) {
1831
+ tenants.length = pageSize;
1832
+ nextPageToken = tenants[tenants.length - 1].tenantId;
1833
+ }
1834
+ return {
1835
+ nextPageToken,
1836
+ tenants,
1837
+ };
1838
+ }
1839
+ function deleteTenant(state, reqBody, ctx) {
1840
+ errors_1.assert(state instanceof state_1.TenantProjectState, "((Can only delete tenant on tenant projects.))");
1841
+ state.delete();
1842
+ return {};
1843
+ }
1844
+ function getTenant(state, reqBody, ctx) {
1845
+ errors_1.assert(state instanceof state_1.TenantProjectState, "((Can only get tenant on tenant projects.))");
1846
+ return state.tenantConfig;
1847
+ }
1848
+ function updateTenant(state, reqBody, ctx) {
1849
+ errors_1.assert(state instanceof state_1.TenantProjectState, "((Can only update tenant on tenant projects.))");
1850
+ return state.updateTenant(reqBody, ctx.params.query.updateMask);
1851
+ }
@@ -17,6 +17,7 @@ const lodash_1 = require("lodash");
17
17
  const handlers_1 = require("./handlers");
18
18
  const bodyParser = require("body-parser");
19
19
  const url_1 = require("url");
20
+ const jsonwebtoken_1 = require("jsonwebtoken");
20
21
  const apiSpec = apiSpec_1.default;
21
22
  const API_SPEC_PATH = "/emulator/openapi.json";
22
23
  const AUTH_HEADER_PREFIX = "bearer ";
@@ -70,6 +71,10 @@ async function createApp(defaultProjectId, projectStateForId = new Map()) {
70
71
  const app = express();
71
72
  app.set("json spaces", 2);
72
73
  app.use(cors({ origin: true }));
74
+ app.delete("*", (req, _, next) => {
75
+ delete req.headers["content-type"];
76
+ next();
77
+ });
73
78
  app.get("/", (req, res) => {
74
79
  return res.json({
75
80
  authEmulator: {
@@ -83,7 +88,7 @@ async function createApp(defaultProjectId, projectStateForId = new Map()) {
83
88
  res.json(specWithEmulatorServer(req.protocol, req.headers.host));
84
89
  });
85
90
  registerLegacyRoutes(app);
86
- handlers_1.registerHandlers(app, (apiKey) => getProjectStateById(getProjectIdByApiKey(apiKey)));
91
+ handlers_1.registerHandlers(app, (apiKey, tenantId) => getProjectStateById(getProjectIdByApiKey(apiKey), tenantId));
87
92
  const apiKeyAuthenticator = (ctx, info) => {
88
93
  if (info.in !== "query") {
89
94
  throw new Error('apiKey must be defined as in: "query" in API spec.');
@@ -169,6 +174,9 @@ async function createApp(defaultProjectId, projectStateForId = new Map()) {
169
174
  "google-fieldmask"() {
170
175
  return true;
171
176
  },
177
+ "google-duration"() {
178
+ return true;
179
+ },
172
180
  uint64() {
173
181
  return true;
174
182
  },
@@ -235,21 +243,15 @@ async function createApp(defaultProjectId, projectStateForId = new Map()) {
235
243
  return defaultProjectId;
236
244
  }
237
245
  function getProjectStateById(projectId, tenantId) {
238
- let agentProject = projectStateForId.get(projectId);
239
- if (!agentProject) {
240
- const state = new state_1.AgentProjectState(projectId);
241
- agentProject = { state, tenantProjects: new Map() };
242
- projectStateForId.set(projectId, agentProject);
246
+ let agentState = projectStateForId.get(projectId);
247
+ if (!agentState) {
248
+ agentState = new state_1.AgentProjectState(projectId);
249
+ projectStateForId.set(projectId, agentState);
243
250
  }
244
251
  if (!tenantId) {
245
- return agentProject.state;
252
+ return agentState;
246
253
  }
247
- let tenantState = agentProject.tenantProjects.get(tenantId);
248
- if (!tenantState) {
249
- tenantState = new state_1.TenantProjectState(projectId, tenantId, agentProject.state);
250
- agentProject.tenantProjects.set(tenantId, tenantState);
251
- }
252
- return tenantState;
254
+ return agentState.getTenantProject(tenantId);
253
255
  }
254
256
  }
255
257
  exports.createApp = createApp;
@@ -337,7 +339,7 @@ function toExegesisController(ops, getProjectStateById) {
337
339
  }
338
340
  function toExegesisOperation(operation) {
339
341
  return (ctx) => {
340
- var _a, _b, _c, _d, _e;
342
+ var _a, _b, _c, _d, _e, _f, _g;
341
343
  let targetProjectId = ctx.params.path.targetProjectId || ((_a = ctx.requestBody) === null || _a === void 0 ? void 0 : _a.targetProjectId);
342
344
  if (targetProjectId) {
343
345
  if ((_b = ctx.api.operationObject.security) === null || _b === void 0 ? void 0 : _b.some((sec) => sec.Oauth2)) {
@@ -347,10 +349,19 @@ function toExegesisController(ops, getProjectStateById) {
347
349
  else {
348
350
  targetProjectId = ctx.user;
349
351
  }
352
+ let targetTenantId = undefined;
350
353
  if (ctx.params.path.tenantId && ((_d = ctx.requestBody) === null || _d === void 0 ? void 0 : _d.tenantId)) {
351
354
  errors_2.assert(ctx.params.path.tenantId === ctx.requestBody.tenantId, "TENANT_ID_MISMATCH");
352
355
  }
353
- const targetTenantId = ctx.params.path.tenantId || ((_e = ctx.requestBody) === null || _e === void 0 ? void 0 : _e.tenantId);
356
+ targetTenantId = ctx.params.path.tenantId || ((_e = ctx.requestBody) === null || _e === void 0 ? void 0 : _e.tenantId);
357
+ if ((_f = ctx.requestBody) === null || _f === void 0 ? void 0 : _f.idToken) {
358
+ const idToken = (_g = ctx.requestBody) === null || _g === void 0 ? void 0 : _g.idToken;
359
+ const decoded = jsonwebtoken_1.decode(idToken, { complete: true });
360
+ if ((decoded === null || decoded === void 0 ? void 0 : decoded.payload.firebase.tenant) && targetTenantId) {
361
+ errors_2.assert((decoded === null || decoded === void 0 ? void 0 : decoded.payload.firebase.tenant) === targetTenantId, "TENANT_ID_MISMATCH");
362
+ }
363
+ targetTenantId = targetTenantId || (decoded === null || decoded === void 0 ? void 0 : decoded.payload.firebase.tenant);
364
+ }
354
365
  return operation(getProjectStateById(targetProjectId, targetTenantId), ctx.requestBody, ctx);
355
366
  };
356
367
  }