@dereekb/firebase-server 13.32.0 → 13.34.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 (79) hide show
  1. package/calcom/index.cjs.default.js +1 -0
  2. package/calcom/index.cjs.js +1451 -0
  3. package/calcom/index.cjs.mjs +2 -0
  4. package/calcom/index.d.ts +1 -0
  5. package/calcom/index.esm.js +1437 -0
  6. package/calcom/package.json +29 -0
  7. package/calcom/src/index.d.ts +1 -0
  8. package/calcom/src/lib/calcom.oauth.connection.cache.d.ts +49 -0
  9. package/calcom/src/lib/calcom.oauth.connection.config.d.ts +75 -0
  10. package/calcom/src/lib/calcom.oauth.connection.context.d.ts +84 -0
  11. package/calcom/src/lib/calcom.oauth.connection.controller.d.ts +18 -0
  12. package/calcom/src/lib/calcom.oauth.connection.module.d.ts +55 -0
  13. package/calcom/src/lib/calcom.oauth.connection.service.d.ts +33 -0
  14. package/calcom/src/lib/index.d.ts +6 -0
  15. package/discord/index.cjs.default.js +1 -0
  16. package/discord/index.cjs.js +761 -0
  17. package/discord/index.cjs.mjs +2 -0
  18. package/discord/index.d.ts +1 -0
  19. package/discord/index.esm.js +753 -0
  20. package/discord/package.json +29 -0
  21. package/discord/src/index.d.ts +1 -0
  22. package/discord/src/lib/discord.oauth.connection.config.d.ts +69 -0
  23. package/discord/src/lib/discord.oauth.connection.controller.d.ts +18 -0
  24. package/discord/src/lib/discord.oauth.connection.module.d.ts +39 -0
  25. package/discord/src/lib/discord.oauth.connection.service.d.ts +43 -0
  26. package/discord/src/lib/index.d.ts +4 -0
  27. package/index.cjs.js +42 -9
  28. package/index.esm.js +42 -9
  29. package/mailgun/package.json +9 -9
  30. package/mcp/package.json +11 -11
  31. package/model/index.cjs.js +4243 -855
  32. package/model/index.esm.js +4183 -862
  33. package/model/package.json +10 -9
  34. package/model/src/lib/index.d.ts +2 -0
  35. package/model/src/lib/system/index.d.ts +2 -0
  36. package/model/src/lib/system/system.private.d.ts +63 -0
  37. package/model/src/lib/system/system.private.module.d.ts +70 -0
  38. package/model/src/lib/userexternalconnection/index.d.ts +8 -0
  39. package/model/src/lib/userexternalconnection/oauth/index.d.ts +7 -0
  40. package/model/src/lib/userexternalconnection/oauth/userexternalconnection.oauth.config.d.ts +110 -0
  41. package/model/src/lib/userexternalconnection/oauth/userexternalconnection.oauth.controller.d.ts +68 -0
  42. package/model/src/lib/userexternalconnection/oauth/userexternalconnection.oauth.error.d.ts +29 -0
  43. package/model/src/lib/userexternalconnection/oauth/userexternalconnection.oauth.refresh.d.ts +24 -0
  44. package/model/src/lib/userexternalconnection/oauth/userexternalconnection.oauth.registry.d.ts +54 -0
  45. package/model/src/lib/userexternalconnection/oauth/userexternalconnection.oauth.service.d.ts +216 -0
  46. package/model/src/lib/userexternalconnection/oauth/userexternalconnection.oauth.state.d.ts +114 -0
  47. package/model/src/lib/userexternalconnection/userexternalconnection.accessor.service.d.ts +111 -0
  48. package/model/src/lib/userexternalconnection/userexternalconnection.action.server.d.ts +172 -0
  49. package/model/src/lib/userexternalconnection/userexternalconnection.error.d.ts +44 -0
  50. package/model/src/lib/userexternalconnection/userexternalconnection.module.d.ts +129 -0
  51. package/model/src/lib/userexternalconnection/userexternalconnection.private.d.ts +192 -0
  52. package/model/src/lib/userexternalconnection/userexternalconnection.reader.service.d.ts +157 -0
  53. package/model/src/lib/userexternalconnection/userexternalconnection.refresh.service.d.ts +46 -0
  54. package/oidc/index.cjs.js +104 -78
  55. package/oidc/index.esm.js +104 -78
  56. package/oidc/package.json +10 -10
  57. package/package.json +24 -10
  58. package/src/lib/env/env.config.d.ts +15 -0
  59. package/src/lib/env/env.service.d.ts +7 -0
  60. package/src/lib/firestore/snapshot/snapshot.field.encrypt.d.ts +21 -2
  61. package/src/lib/nest/env/env.service.d.ts +1 -0
  62. package/test/index.cjs.js +14 -2
  63. package/test/index.esm.js +15 -3
  64. package/test/package.json +11 -11
  65. package/test/src/lib/firebase/firebase.admin.auth.d.ts +1 -1
  66. package/twilio/package.json +8 -8
  67. package/zoho/README.md +38 -0
  68. package/zoho/index.cjs.js +1418 -64
  69. package/zoho/index.esm.js +1401 -67
  70. package/zoho/package.json +13 -9
  71. package/zoho/src/lib/index.d.ts +6 -0
  72. package/zoho/src/lib/zoho.accounts.firebase.d.ts +10 -3
  73. package/zoho/src/lib/zoho.accounts.firebase.module.d.ts +51 -0
  74. package/zoho/src/lib/zoho.accounts.firebase.system.d.ts +51 -8
  75. package/zoho/src/lib/zoho.oauth.connection.cache.d.ts +45 -0
  76. package/zoho/src/lib/zoho.oauth.connection.config.d.ts +80 -0
  77. package/zoho/src/lib/zoho.oauth.connection.controller.d.ts +18 -0
  78. package/zoho/src/lib/zoho.oauth.connection.module.d.ts +43 -0
  79. package/zoho/src/lib/zoho.oauth.connection.service.d.ts +107 -0
package/oidc/index.esm.js CHANGED
@@ -6217,7 +6217,7 @@ var OidcInteractionController_1;
6217
6217
  * @throws {HttpException} HTTP 400 when the consent interaction cannot be completed.
6218
6218
  */ function postConsent(uid, body, res) {
6219
6219
  return _async_to_generator$2(function() {
6220
- var startedAt, clientId, accountId, _ref, _prompt_details_missingOIDCScope, _this_accountService_providerConfig_adminOnlyScopes, _ref1, _ref2, _ref3, _prompt_details_missingOIDCClaims, _prompt_details_missingResourceScopes, _this_accountService_delegate_isAdminUser, _this_accountService_delegate, _ref4, redirectTo, interaction, prompt, params, session, missingOIDCScope, existingGrant, _tmp, encounteredOIDCScopes, clientPayload, requestedOIDCScopeSet, providerProfiles, configAdminOnlyScopes, adminOnlyScopes, requestedAdminOnlyScopes, requestsServiceToken, isAdmin, _tmp1, _ref5, redirectTo1, _oidcClientProviderProfileScopes, clientUnlockedScopes, clientRequiredScopes, profileGatedScopes, disallowedGatedScopes, _ref6, redirectTo2, missingRequiredScopes, _ref7, redirectTo3, requestedRawTtl, clientMaxSessionTtl, expiresInSeconds, grant, _tmp2, _resolveEffectiveSubset, granted, rejected, _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, value, encounteredOIDCClaims, missingOIDCClaims, _resolveEffectiveSubset1, granted1, rejected1, missingResourceScopes, _iteratorNormalCompletion1, _didIteratorError1, _iteratorError1, _iterator1, _step1, _step_value, indicator, scopes, _body_grantedResourceScopes, requestedSubset, encounteredResourceScopes, _resolveEffectiveSubset2, granted2, rejected2, _iteratorNormalCompletion2, _didIteratorError2, _iteratorError2, _iterator2, _step2, value1, grantId, _ref8, redirectTo4, err;
6220
+ var startedAt, clientId, accountId, _ref, _prompt_details_missingOIDCScope, _ref1, _this_accountService_providerConfig_adminOnlyScopes, _ref2, _ref3, _prompt_details_missingOIDCClaims, _prompt_details_missingResourceScopes, _this_accountService_delegate_isAdminUser, _this_accountService_delegate, _ref4, redirectTo, interaction, prompt, params, session, missingOIDCScope, existingGrant, _tmp, encounteredOIDCScopes, clientPayload, requestedOIDCScopeSet, providerProfiles, _oidcClientProviderProfileScopes, clientUnlockedScopes, clientRequiredScopes, profileGatedScopes, disallowedGatedScopes, _ref5, redirectTo1, missingRequiredScopes, _ref6, redirectTo2, effectiveOIDCScopes, heldOIDCScopes, consentedOIDCScopeSet, configAdminOnlyScopes, adminOnlyScopes, consentedAdminOnlyScopes, requestsServiceToken, isAdmin, _tmp1, _ref7, redirectTo3, requestedRawTtl, clientMaxSessionTtl, expiresInSeconds, grant, _tmp2, granted, rejected, _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, value, encounteredOIDCClaims, missingOIDCClaims, _resolveEffectiveSubset, granted1, rejected1, missingResourceScopes, _iteratorNormalCompletion1, _didIteratorError1, _iteratorError1, _iterator1, _step1, _step_value, indicator, scopes, _ref8, _body_grantedResourceScopes, requestedSubset, encounteredResourceScopes, _resolveEffectiveSubset1, granted2, rejected2, _iteratorNormalCompletion2, _didIteratorError2, _iteratorError2, _iterator2, _step2, value1, grantId, _ref9, redirectTo4, err;
6221
6221
  return _ts_generator$2(this, function(_state) {
6222
6222
  switch(_state.label){
6223
6223
  case 0:
@@ -6299,66 +6299,44 @@ var OidcInteractionController_1;
6299
6299
  ];
6300
6300
  case 9:
6301
6301
  clientPayload = _state.sent();
6302
- // Admin-only scope gate. The requested OIDC scope set is `missingOIDCScope ∪ encounteredOIDCScopes`
6303
- // (a fresh consent has everything in `missing`; a re-consent may carry already-granted scopes in
6304
- // `encountered`). If that set intersects the admin-only scopes — the provider's `adminOnlyScopes`
6305
- // unioned with the scopes of every profile marked `adminOnly` — and the resolving user is not an
6306
- // admin, hard-reject with `access_denied` rather than silently dropping the scope.
6302
+ // The requested OIDC scope set is `missingOIDCScope ∪ encounteredOIDCScopes` (a fresh consent has
6303
+ // everything in `missing`; a re-consent may carry already-granted scopes in `encountered`). The two
6304
+ // provider-profile gates below judge the REQUEST — what this client may ask for at all — so they
6305
+ // read this set. The admin-only gate further down judges what the user actually consented to
6306
+ // instead, and reads `consentedOIDCScopeSet`.
6307
6307
  requestedOIDCScopeSet = new Set(_to_consumable_array$1(missingOIDCScope).concat(_to_consumable_array$1(encounteredOIDCScopes)));
6308
6308
  providerProfiles = this.accountService.providerConfig.providerProfiles;
6309
- configAdminOnlyScopes = (_this_accountService_providerConfig_adminOnlyScopes = this.accountService.providerConfig.adminOnlyScopes) !== null && _this_accountService_providerConfig_adminOnlyScopes !== void 0 ? _this_accountService_providerConfig_adminOnlyScopes : [];
6310
- adminOnlyScopes = new Set(_to_consumable_array$1(configAdminOnlyScopes).concat(_to_consumable_array$1(adminOnlyScopesForOidcProviderProfiles(providerProfiles !== null && providerProfiles !== void 0 ? providerProfiles : []))));
6311
- requestedAdminOnlyScopes = Array.from(adminOnlyScopes).filter(function(scope) {
6312
- return requestedOIDCScopeSet.has(scope);
6313
- });
6314
- // Deliberately sourced from the provider config's `adminOnlyScopes` ALONE, unlike the gate above:
6315
- // this flag also selects the (highest) service-token TTL tier further down, and a profile marked
6316
- // `adminOnly` must gate by admin status WITHOUT widening its grant's lifetime to that tier.
6317
- requestsServiceToken = configAdminOnlyScopes.some(function(scope) {
6318
- return requestedOIDCScopeSet.has(scope);
6309
+ // Provider-profile scope gate. Any scope referenced by a provider profile is "gated" — available
6310
+ // only to clients whose assigned profiles unlock it (a client with NO assigned profiles resolves
6311
+ // to the registry's default profiles). Independent of the admin-only gate below; a scope may be
6312
+ // subject to both.
6313
+ _oidcClientProviderProfileScopes = oidcClientProviderProfileScopes(providerProfiles, (_ref1 = clientPayload === null || clientPayload === void 0 ? void 0 : clientPayload.dbx_provider_profiles) !== null && _ref1 !== void 0 ? _ref1 : undefined), clientUnlockedScopes = _oidcClientProviderProfileScopes.unlocked, clientRequiredScopes = _oidcClientProviderProfileScopes.required;
6314
+ profileGatedScopes = scopesForOidcProviderProfiles(providerProfiles !== null && providerProfiles !== void 0 ? providerProfiles : []);
6315
+ // Unlock gate: reject any requested gated scope this client's profiles do not unlock.
6316
+ disallowedGatedScopes = Array.from(requestedOIDCScopeSet).filter(function(scope) {
6317
+ return profileGatedScopes.has(scope) && !clientUnlockedScopes.has(scope);
6319
6318
  });
6320
- if (!accountId) return [
6319
+ if (!(disallowedGatedScopes.length > 0)) return [
6321
6320
  3,
6322
6321
  11
6323
6322
  ];
6324
- return [
6325
- 4,
6326
- (_ref1 = (_this_accountService_delegate_isAdminUser = (_this_accountService_delegate = this.accountService.delegate).isAdminUser) === null || _this_accountService_delegate_isAdminUser === void 0 ? void 0 : _this_accountService_delegate_isAdminUser.call(_this_accountService_delegate, this.accountService.userContext(accountId).authUserContext)) !== null && _ref1 !== void 0 ? _ref1 : false
6327
- ];
6328
- case 10:
6329
- _tmp1 = _state.sent();
6330
- return [
6331
- 3,
6332
- 12
6333
- ];
6334
- case 11:
6335
- _tmp1 = false;
6336
- _state.label = 12;
6337
- case 12:
6338
- isAdmin = _tmp1;
6339
- if (!(requestedAdminOnlyScopes.length > 0 && !isAdmin)) return [
6340
- 3,
6341
- 14
6342
- ];
6343
6323
  return [
6344
6324
  4,
6345
6325
  this.oidcInteractionService.finishInteractionByUid(uid, {
6346
6326
  error: 'access_denied',
6347
- error_description: "The following scope(s) are restricted to admins: ".concat(requestedAdminOnlyScopes.join(', '), ".")
6327
+ error_description: "The following scope(s) are not available to this client: ".concat(disallowedGatedScopes.join(', '), ".")
6348
6328
  }, {
6349
6329
  mergeWithLastSubmission: true
6350
6330
  })
6351
6331
  ];
6352
- case 13:
6332
+ case 10:
6353
6333
  _ref5 = _state.sent(), redirectTo1 = _ref5.returnTo;
6354
6334
  emitOidcAnalyticsEvent(this._analytics, {
6355
6335
  type: 'consent',
6356
6336
  isSuccessful: false,
6357
6337
  uid: accountId,
6358
6338
  clientId: clientId,
6359
- serviceToken: requestsServiceToken,
6360
- isAdmin: false,
6361
- reason: 'service_token_non_admin',
6339
+ reason: 'scope_not_unlocked_for_client',
6362
6340
  durationMs: Date.now() - startedAt
6363
6341
  }, this._logger);
6364
6342
  res.json({
@@ -6367,38 +6345,33 @@ var OidcInteractionController_1;
6367
6345
  return [
6368
6346
  2
6369
6347
  ];
6370
- case 14:
6371
- // Provider-profile scope gate. Any scope referenced by a provider profile is "gated" — available
6372
- // only to clients whose assigned profiles unlock it (a client with NO assigned profiles resolves
6373
- // to the registry's default profiles). Independent of the admin-only gate above; a scope may be
6374
- // subject to both.
6375
- _oidcClientProviderProfileScopes = oidcClientProviderProfileScopes(providerProfiles, (_ref2 = clientPayload === null || clientPayload === void 0 ? void 0 : clientPayload.dbx_provider_profiles) !== null && _ref2 !== void 0 ? _ref2 : undefined), clientUnlockedScopes = _oidcClientProviderProfileScopes.unlocked, clientRequiredScopes = _oidcClientProviderProfileScopes.required;
6376
- profileGatedScopes = scopesForOidcProviderProfiles(providerProfiles !== null && providerProfiles !== void 0 ? providerProfiles : []);
6377
- // Unlock gate: reject any requested gated scope this client's profiles do not unlock.
6378
- disallowedGatedScopes = Array.from(requestedOIDCScopeSet).filter(function(scope) {
6379
- return profileGatedScopes.has(scope) && !clientUnlockedScopes.has(scope);
6348
+ case 11:
6349
+ // Required gate: a scope a client's profiles force-require (`require: 'required'`) must be present
6350
+ // in the request. Reject when the client did not request a required scope.
6351
+ missingRequiredScopes = Array.from(clientRequiredScopes).filter(function(scope) {
6352
+ return !requestedOIDCScopeSet.has(scope);
6380
6353
  });
6381
- if (!(disallowedGatedScopes.length > 0)) return [
6354
+ if (!(missingRequiredScopes.length > 0)) return [
6382
6355
  3,
6383
- 16
6356
+ 13
6384
6357
  ];
6385
6358
  return [
6386
6359
  4,
6387
6360
  this.oidcInteractionService.finishInteractionByUid(uid, {
6388
6361
  error: 'access_denied',
6389
- error_description: "The following scope(s) are not available to this client: ".concat(disallowedGatedScopes.join(', '), ".")
6362
+ error_description: "This client must request the required scope(s): ".concat(missingRequiredScopes.join(', '), ".")
6390
6363
  }, {
6391
6364
  mergeWithLastSubmission: true
6392
6365
  })
6393
6366
  ];
6394
- case 15:
6367
+ case 12:
6395
6368
  _ref6 = _state.sent(), redirectTo2 = _ref6.returnTo;
6396
6369
  emitOidcAnalyticsEvent(this._analytics, {
6397
6370
  type: 'consent',
6398
6371
  isSuccessful: false,
6399
6372
  uid: accountId,
6400
6373
  clientId: clientId,
6401
- reason: 'scope_not_unlocked_for_client',
6374
+ reason: 'required_scope_missing',
6402
6375
  durationMs: Date.now() - startedAt
6403
6376
  }, this._logger);
6404
6377
  res.json({
@@ -6407,13 +6380,62 @@ var OidcInteractionController_1;
6407
6380
  return [
6408
6381
  2
6409
6382
  ];
6410
- case 16:
6411
- // Required gate: a scope a client's profiles force-require (`require: 'required'`) must be present
6412
- // in the request. Reject when the client did not request a required scope.
6413
- missingRequiredScopes = Array.from(clientRequiredScopes).filter(function(scope) {
6414
- return !requestedOIDCScopeSet.has(scope);
6383
+ case 13:
6384
+ // The scopes this consent actually puts in force. Resolved once here and re-applied to the grant
6385
+ // further down, so the admin-only gate below judges exactly the set that will be granted.
6386
+ //
6387
+ // Force-grants the client's required profile scopes (in addition to `openid`) so they cannot be
6388
+ // dropped at consent — the required gate above already guarantees they were requested.
6389
+ effectiveOIDCScopes = resolveEffectiveSubset({
6390
+ missing: missingOIDCScope,
6391
+ requestedSubset: body.grantedOIDCScopes,
6392
+ alwaysGranted: _to_consumable_array$1(ALWAYS_GRANTED_OIDC_SCOPES).concat(_to_consumable_array$1(clientRequiredScopes)),
6393
+ alreadyEncountered: encounteredOIDCScopes
6415
6394
  });
6416
- if (!(missingRequiredScopes.length > 0)) return [
6395
+ // Scopes the existing Grant already holds (granted, minus any it rejected). Nothing here revokes
6396
+ // them, so they stay in force through this consent and the gate below has to count them too.
6397
+ heldOIDCScopes = existingGrant ? existingGrant.getOIDCScope().split(' ').filter(Boolean) : [];
6398
+ consentedOIDCScopeSet = new Set(_to_consumable_array$1(heldOIDCScopes).concat(_to_consumable_array$1(effectiveOIDCScopes.granted)));
6399
+ // Admin-only scope gate. If the consented set intersects the admin-only scopes — the provider's
6400
+ // `adminOnlyScopes` unioned with the scopes of every profile marked `adminOnly` — and the resolving
6401
+ // user is not an admin, hard-reject with `access_denied` rather than silently dropping the scope.
6402
+ //
6403
+ // Deliberately gated on what the user CONSENTED to rather than on what the client requested: the
6404
+ // consent UI renders a deselectable checkbox for every scope in the auth request, and a client
6405
+ // cannot know the caller's admin status when it builds that request. Gating on the request would
6406
+ // make an admin-only scope in `scopes_supported` fatal for every non-admin — deselecting it at
6407
+ // consent has to be a way through.
6408
+ configAdminOnlyScopes = (_this_accountService_providerConfig_adminOnlyScopes = this.accountService.providerConfig.adminOnlyScopes) !== null && _this_accountService_providerConfig_adminOnlyScopes !== void 0 ? _this_accountService_providerConfig_adminOnlyScopes : [];
6409
+ adminOnlyScopes = new Set(_to_consumable_array$1(configAdminOnlyScopes).concat(_to_consumable_array$1(adminOnlyScopesForOidcProviderProfiles(providerProfiles !== null && providerProfiles !== void 0 ? providerProfiles : []))));
6410
+ consentedAdminOnlyScopes = Array.from(adminOnlyScopes).filter(function(scope) {
6411
+ return consentedOIDCScopeSet.has(scope);
6412
+ });
6413
+ // Deliberately sourced from the provider config's `adminOnlyScopes` ALONE, unlike the gate above:
6414
+ // this flag also selects the (highest) service-token TTL tier further down, and a profile marked
6415
+ // `adminOnly` must gate by admin status WITHOUT widening its grant's lifetime to that tier.
6416
+ requestsServiceToken = configAdminOnlyScopes.some(function(scope) {
6417
+ return consentedOIDCScopeSet.has(scope);
6418
+ });
6419
+ if (!accountId) return [
6420
+ 3,
6421
+ 15
6422
+ ];
6423
+ return [
6424
+ 4,
6425
+ (_ref2 = (_this_accountService_delegate_isAdminUser = (_this_accountService_delegate = this.accountService.delegate).isAdminUser) === null || _this_accountService_delegate_isAdminUser === void 0 ? void 0 : _this_accountService_delegate_isAdminUser.call(_this_accountService_delegate, this.accountService.userContext(accountId).authUserContext)) !== null && _ref2 !== void 0 ? _ref2 : false
6426
+ ];
6427
+ case 14:
6428
+ _tmp1 = _state.sent();
6429
+ return [
6430
+ 3,
6431
+ 16
6432
+ ];
6433
+ case 15:
6434
+ _tmp1 = false;
6435
+ _state.label = 16;
6436
+ case 16:
6437
+ isAdmin = _tmp1;
6438
+ if (!(consentedAdminOnlyScopes.length > 0 && !isAdmin)) return [
6417
6439
  3,
6418
6440
  18
6419
6441
  ];
@@ -6421,7 +6443,7 @@ var OidcInteractionController_1;
6421
6443
  4,
6422
6444
  this.oidcInteractionService.finishInteractionByUid(uid, {
6423
6445
  error: 'access_denied',
6424
- error_description: "This client must request the required scope(s): ".concat(missingRequiredScopes.join(', '), ".")
6446
+ error_description: "The following scope(s) are restricted to admins: ".concat(consentedAdminOnlyScopes.join(', '), ".")
6425
6447
  }, {
6426
6448
  mergeWithLastSubmission: true
6427
6449
  })
@@ -6433,7 +6455,9 @@ var OidcInteractionController_1;
6433
6455
  isSuccessful: false,
6434
6456
  uid: accountId,
6435
6457
  clientId: clientId,
6436
- reason: 'required_scope_missing',
6458
+ serviceToken: requestsServiceToken,
6459
+ isAdmin: false,
6460
+ reason: 'service_token_non_admin',
6437
6461
  durationMs: Date.now() - startedAt
6438
6462
  }, this._logger);
6439
6463
  res.json({
@@ -6476,14 +6500,7 @@ var OidcInteractionController_1;
6476
6500
  case 21:
6477
6501
  grant = _tmp2;
6478
6502
  if (missingOIDCScope.length > 0) {
6479
- // Force-grant the client's required profile scopes (in addition to `openid`) so they cannot be
6480
- // dropped at consent — the required gate above already guarantees they were requested.
6481
- _resolveEffectiveSubset = resolveEffectiveSubset({
6482
- missing: missingOIDCScope,
6483
- requestedSubset: body.grantedOIDCScopes,
6484
- alwaysGranted: _to_consumable_array$1(ALWAYS_GRANTED_OIDC_SCOPES).concat(_to_consumable_array$1(clientRequiredScopes)),
6485
- alreadyEncountered: encounteredOIDCScopes
6486
- }), granted = _resolveEffectiveSubset.granted, rejected = _resolveEffectiveSubset.rejected;
6503
+ granted = effectiveOIDCScopes.granted, rejected = effectiveOIDCScopes.rejected;
6487
6504
  if (granted.length > 0) {
6488
6505
  grant.addOIDCScope(granted.join(' '));
6489
6506
  }
@@ -6511,11 +6528,11 @@ var OidcInteractionController_1;
6511
6528
  encounteredOIDCClaims = grant.getOIDCClaimsEncountered();
6512
6529
  missingOIDCClaims = (_prompt_details_missingOIDCClaims = prompt.details.missingOIDCClaims) !== null && _prompt_details_missingOIDCClaims !== void 0 ? _prompt_details_missingOIDCClaims : [];
6513
6530
  if (missingOIDCClaims.length > 0) {
6514
- _resolveEffectiveSubset1 = resolveEffectiveSubset({
6531
+ _resolveEffectiveSubset = resolveEffectiveSubset({
6515
6532
  missing: missingOIDCClaims,
6516
6533
  requestedSubset: body.grantedOIDCClaims,
6517
6534
  alreadyEncountered: encounteredOIDCClaims
6518
- }), granted1 = _resolveEffectiveSubset1.granted, rejected1 = _resolveEffectiveSubset1.rejected;
6535
+ }), granted1 = _resolveEffectiveSubset.granted, rejected1 = _resolveEffectiveSubset.rejected;
6519
6536
  if (granted1.length > 0) {
6520
6537
  grant.addOIDCClaims(granted1);
6521
6538
  }
@@ -6529,13 +6546,22 @@ var OidcInteractionController_1;
6529
6546
  for(_iterator1 = Object.entries(missingResourceScopes)[Symbol.iterator](); !(_iteratorNormalCompletion1 = (_step1 = _iterator1.next()).done); _iteratorNormalCompletion1 = true){
6530
6547
  _step_value = _sliced_to_array(_step1.value, 2), indicator = _step_value[0], scopes = _step_value[1];
6531
6548
  ;
6532
- requestedSubset = (_body_grantedResourceScopes = body.grantedResourceScopes) === null || _body_grantedResourceScopes === void 0 ? void 0 : _body_grantedResourceScopes[indicator];
6549
+ ;
6550
+ // A submission with no explicit per-resource selection falls back to the OIDC scope selection
6551
+ // rather than granting everything the resource server declares. A resource server's scope list
6552
+ // is drawn from the same provider scopes the consent UI renders as one checkbox list, so a scope
6553
+ // deselected there must not reappear on the resource-bound access token — otherwise deselecting
6554
+ // an admin-only scope would clear the gate above while the token still carried it.
6555
+ // `grantedOIDCScopes` being absent still means "grant everything requested".
6556
+ requestedSubset = (_ref8 = (_body_grantedResourceScopes = body.grantedResourceScopes) === null || _body_grantedResourceScopes === void 0 ? void 0 : _body_grantedResourceScopes[indicator]) !== null && _ref8 !== void 0 ? _ref8 : body.grantedOIDCScopes ? scopes.filter(function(scope) {
6557
+ return consentedOIDCScopeSet.has(scope);
6558
+ }) : undefined;
6533
6559
  encounteredResourceScopes = grant.getResourceScopeEncountered(indicator).split(' ').filter(Boolean);
6534
- _resolveEffectiveSubset2 = resolveEffectiveSubset({
6560
+ _resolveEffectiveSubset1 = resolveEffectiveSubset({
6535
6561
  missing: scopes,
6536
6562
  requestedSubset: requestedSubset,
6537
6563
  alreadyEncountered: encounteredResourceScopes
6538
- }), granted2 = _resolveEffectiveSubset2.granted, rejected2 = _resolveEffectiveSubset2.rejected;
6564
+ }), granted2 = _resolveEffectiveSubset1.granted, rejected2 = _resolveEffectiveSubset1.rejected;
6539
6565
  if (granted2.length > 0) {
6540
6566
  grant.addResourceScope(indicator, granted2.join(' '));
6541
6567
  }
@@ -6591,7 +6617,7 @@ var OidcInteractionController_1;
6591
6617
  })
6592
6618
  ];
6593
6619
  case 23:
6594
- _ref8 = _state.sent(), redirectTo4 = _ref8.returnTo;
6620
+ _ref9 = _state.sent(), redirectTo4 = _ref9.returnTo;
6595
6621
  emitOidcAnalyticsEvent(this._analytics, {
6596
6622
  type: 'consent',
6597
6623
  isSuccessful: true,
package/oidc/package.json CHANGED
@@ -1,16 +1,16 @@
1
1
  {
2
2
  "name": "@dereekb/firebase-server/oidc",
3
- "version": "13.32.0",
3
+ "version": "13.34.0",
4
4
  "peerDependencies": {
5
- "@dereekb/analytics": "13.32.0",
6
- "@dereekb/date": "13.32.0",
7
- "@dereekb/firebase": "13.32.0",
8
- "@dereekb/firebase-server": "13.32.0",
9
- "@dereekb/model": "13.32.0",
10
- "@dereekb/nestjs": "13.32.0",
11
- "@dereekb/rxjs": "13.32.0",
12
- "@dereekb/util": "13.32.0",
13
- "@dereekb/zoho": "13.32.0",
5
+ "@dereekb/analytics": "13.34.0",
6
+ "@dereekb/date": "13.34.0",
7
+ "@dereekb/firebase": "13.34.0",
8
+ "@dereekb/firebase-server": "13.34.0",
9
+ "@dereekb/model": "13.34.0",
10
+ "@dereekb/nestjs": "13.34.0",
11
+ "@dereekb/rxjs": "13.34.0",
12
+ "@dereekb/util": "13.34.0",
13
+ "@dereekb/zoho": "13.34.0",
14
14
  "@nestjs/common": "^11.1.19",
15
15
  "@nestjs/config": "^4.0.4",
16
16
  "express": "^5.2.1",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dereekb/firebase-server",
3
- "version": "13.32.0",
3
+ "version": "13.34.0",
4
4
  "sideEffects": false,
5
5
  "exports": {
6
6
  "./test": {
@@ -9,6 +9,18 @@
9
9
  "import": "./test/index.cjs.mjs",
10
10
  "default": "./test/index.cjs.js"
11
11
  },
12
+ "./calcom": {
13
+ "module": "./calcom/index.esm.js",
14
+ "types": "./calcom/index.d.ts",
15
+ "import": "./calcom/index.cjs.mjs",
16
+ "default": "./calcom/index.cjs.js"
17
+ },
18
+ "./discord": {
19
+ "module": "./discord/index.esm.js",
20
+ "types": "./discord/index.d.ts",
21
+ "import": "./discord/index.cjs.mjs",
22
+ "default": "./discord/index.cjs.js"
23
+ },
12
24
  "./mailgun": {
13
25
  "module": "./mailgun/index.esm.js",
14
26
  "types": "./mailgun/index.d.ts",
@@ -57,15 +69,17 @@
57
69
  "types": "./src/index.d.ts",
58
70
  "peerDependencies": {
59
71
  "@cantoo/pdf-lib": "^2.6.5",
60
- "@dereekb/analytics": "13.32.0",
61
- "@dereekb/date": "13.32.0",
62
- "@dereekb/dbx-core": "13.32.0",
63
- "@dereekb/firebase": "13.32.0",
64
- "@dereekb/model": "13.32.0",
65
- "@dereekb/nestjs": "13.32.0",
66
- "@dereekb/rxjs": "13.32.0",
67
- "@dereekb/util": "13.32.0",
68
- "@dereekb/zoho": "13.32.0",
72
+ "@dereekb/analytics": "13.34.0",
73
+ "@dereekb/calcom": "13.34.0",
74
+ "@dereekb/date": "13.34.0",
75
+ "@dereekb/dbx-core": "13.34.0",
76
+ "@dereekb/discord": "13.34.0",
77
+ "@dereekb/firebase": "13.34.0",
78
+ "@dereekb/model": "13.34.0",
79
+ "@dereekb/nestjs": "13.34.0",
80
+ "@dereekb/rxjs": "13.34.0",
81
+ "@dereekb/util": "13.34.0",
82
+ "@dereekb/zoho": "13.34.0",
69
83
  "@google-cloud/firestore": "^7.11.6",
70
84
  "@google-cloud/storage": "^7.19.0",
71
85
  "@modelcontextprotocol/sdk": "1.29.0",
@@ -29,6 +29,21 @@ export interface FirebaseServerEnvironmentConfig extends ServerEnvironmentConfig
29
29
  * `appApiUrl` (or `appUrl`).
30
30
  */
31
31
  readonly appMcpUrl?: Maybe<WebsiteUrlWithPrefix>;
32
+ /**
33
+ * The origin external-connection OAuth endpoints are reachable at (e.g. 'https://example.com').
34
+ *
35
+ * The `/oauth/<providerType>/{authorize,callback}` routes are excluded from the global API route
36
+ * prefix, so they are served from the site origin rather than from `appApiUrl`. The redirect URI
37
+ * registered with each provider is derived from this value, and providers require a byte-identical
38
+ * match.
39
+ *
40
+ * Set explicitly when those routes are not reachable at `appUrl` — in local development `appUrl`
41
+ * is typically the Angular dev server while the `/oauth/**` hosting rewrite is served by the
42
+ * hosting emulator on another port. In production the two normally coincide, so it can be omitted.
43
+ *
44
+ * When omitted, modules fall back to `appUrl`.
45
+ */
46
+ readonly appOAuthUrl?: Maybe<WebsiteUrlWithPrefix>;
32
47
  /**
33
48
  * The webhook URL. When not set explicitly, `buildNestServerRootModule()` computes
34
49
  * it from `appUrl + /webhook`.
@@ -54,6 +54,13 @@ export declare abstract class FirebaseServerEnvService {
54
54
  * URL from `appApiUrl` / `appUrl`.
55
55
  */
56
56
  abstract readonly appMcpUrl: Maybe<WebsiteUrl>;
57
+ /**
58
+ * The origin external-connection OAuth endpoints are reachable at (e.g., 'https://app.example.com').
59
+ *
60
+ * Used to derive each provider's registered redirect URI. When omitted, modules fall back to
61
+ * `appUrl`.
62
+ */
63
+ abstract readonly appOAuthUrl: Maybe<WebsiteUrl>;
57
64
  /**
58
65
  * The full webhook URL (e.g., 'https://app.example.com/webhook').
59
66
  */
@@ -14,6 +14,18 @@ export interface FirestoreEncryptedFieldConfig<T> {
14
14
  * Default value when the field is missing from Firestore.
15
15
  */
16
16
  readonly default: GetterOrValue<T>;
17
+ /**
18
+ * Called when a stored value is present but cannot be decrypted — a wrong or rotated key, a
19
+ * corrupt value, or data written before the field was encrypted. Its return value is used in
20
+ * place of the decrypted value.
21
+ *
22
+ * When omitted the decode throws, which is the correct behavior for a field of record: a
23
+ * credential that silently becomes `undefined` is far worse than a loud failure.
24
+ *
25
+ * Only supply this for data that can be regenerated — a cache. See the Zoho access token cache,
26
+ * where an undecryptable entry is a cache miss and the next call re-mints the token.
27
+ */
28
+ readonly onDecodeFailure?: Maybe<(error: unknown, data: string) => T>;
17
29
  }
18
30
  /**
19
31
  * Configuration for an optional encrypted Firestore field.
@@ -25,13 +37,20 @@ export interface OptionalFirestoreEncryptedFieldConfig<T> {
25
37
  * Secret source for the encryption key.
26
38
  */
27
39
  readonly secret: GetterOrValue<string>;
40
+ /**
41
+ * Called when a stored value is present but cannot be decrypted. See
42
+ * {@link FirestoreEncryptedFieldConfig.onDecodeFailure}. When omitted the decode throws.
43
+ */
44
+ readonly onDecodeFailure?: Maybe<(error: unknown, data: string) => Maybe<T>>;
28
45
  }
29
46
  /**
30
47
  * Creates a Firestore field mapping that encrypts/decrypts a JSON-serializable value
31
48
  * using AES-256-GCM. The value is stored in Firestore as a base64-encoded string.
32
49
  *
33
- * The encryption key is resolved from the configured secret source on each read/write,
34
- * allowing for key rotation via environment variable changes.
50
+ * IMPORTANT: there is NO key rotation. `resolveEncryptionKey()` reads and validates the secret once,
51
+ * here at construction, and closes over the resulting key — changing the secret afterwards orphans
52
+ * every value already written with the old one. Rotation is only survivable for fields that supply
53
+ * {@link FirestoreEncryptedFieldConfig.onDecodeFailure} and hold regenerable data.
35
54
  *
36
55
  * @param config - Encryption field configuration.
37
56
  * @returns A field mapping configuration for encrypted values.
@@ -17,6 +17,7 @@ export declare class DefaultFirebaseServerEnvService extends ServerEnvironmentSe
17
17
  get developmentSchedulerEnabled(): boolean;
18
18
  get appUrlDetails(): Maybe<WebsiteUrlDetails>;
19
19
  get appMcpUrl(): Maybe<WebsiteUrl>;
20
+ get appOAuthUrl(): Maybe<WebsiteUrl>;
20
21
  get appWebhookUrl(): Maybe<WebsiteUrl>;
21
22
  get isApiEnabled(): boolean;
22
23
  get isWebhooksEnabled(): boolean;
package/test/index.cjs.js CHANGED
@@ -707,12 +707,24 @@ function _ts_generator$a(thisArg, body) {
707
707
  });
708
708
  };
709
709
  }
710
+ /**
711
+ * Random component of a generated test uid.
712
+ *
713
+ * The counter and the timestamp alone are not enough to keep uids unique: the counter is per-module, so
714
+ * every spec FILE starts it at 1, and two files whose first user is created in the same millisecond
715
+ * would otherwise be handed the identical uid — which fails as "the user with the provided uid already
716
+ * exists" in whichever file loses. Kept numeric so a uid stays digits-only.
717
+ */ var testUidRandomFactory = util.randomNumberFactory({
718
+ min: 100000,
719
+ max: 1000000,
720
+ round: 'floor'
721
+ });
710
722
  /**
711
723
  * Incrementing number factory for generating test UID values.
712
724
  *
713
- * Has the format 'test-uid-<number>'
725
+ * Has the format '<timestamp>0<counter><random>'
714
726
  */ var testUidFactory = util.mapGetter(util.incrementingNumberFactory(), function(i) {
715
- return "".concat(Date.now(), "0").concat(i);
727
+ return "".concat(Date.now(), "0").concat(i).concat(testUidRandomFactory());
716
728
  });
717
729
  /**
718
730
  * Creates a CallableContextOptions with auth attached corresponding to the input UserRecord.
package/test/index.esm.js CHANGED
@@ -1,4 +1,4 @@
1
- import { randomEmailFactory, randomPhoneNumberFactory, mapGetter, incrementingNumberFactory, asGetter, getValueFromGetter, cachedGetter, asArray, mapObjectMap, unixDateTimeSecondsNumberForNow } from '@dereekb/util';
1
+ import { randomEmailFactory, randomPhoneNumberFactory, randomNumberFactory, mapGetter, incrementingNumberFactory, asGetter, getValueFromGetter, cachedGetter, asArray, mapObjectMap, unixDateTimeSecondsNumberForNow } from '@dereekb/util';
2
2
  import { useTestContextFixture, AbstractChildTestContextFixture, testContextBuilder, AbstractTestContextFixture, useTestFunctionFixture, useTestFunctionMapFixture, ExpectedErrorOfSpecificTypeError } from '@dereekb/util/test';
3
3
  import { decode } from 'jsonwebtoken';
4
4
  import { flatFirestoreModelKey, twoWayFlatFirestoreModelKey, firestoreContextFactory, firebaseStorageContextFactory, adminOnlyScopesForOidcProviderProfiles, assignmentOnlyScopesForOidcProviderProfiles } from '@dereekb/firebase';
@@ -706,12 +706,24 @@ function _ts_generator$a(thisArg, body) {
706
706
  });
707
707
  };
708
708
  }
709
+ /**
710
+ * Random component of a generated test uid.
711
+ *
712
+ * The counter and the timestamp alone are not enough to keep uids unique: the counter is per-module, so
713
+ * every spec FILE starts it at 1, and two files whose first user is created in the same millisecond
714
+ * would otherwise be handed the identical uid — which fails as "the user with the provided uid already
715
+ * exists" in whichever file loses. Kept numeric so a uid stays digits-only.
716
+ */ var testUidRandomFactory = randomNumberFactory({
717
+ min: 100000,
718
+ max: 1000000,
719
+ round: 'floor'
720
+ });
709
721
  /**
710
722
  * Incrementing number factory for generating test UID values.
711
723
  *
712
- * Has the format 'test-uid-<number>'
724
+ * Has the format '<timestamp>0<counter><random>'
713
725
  */ var testUidFactory = mapGetter(incrementingNumberFactory(), function(i) {
714
- return "".concat(Date.now(), "0").concat(i);
726
+ return "".concat(Date.now(), "0").concat(i).concat(testUidRandomFactory());
715
727
  });
716
728
  /**
717
729
  * Creates a CallableContextOptions with auth attached corresponding to the input UserRecord.
package/test/package.json CHANGED
@@ -1,16 +1,16 @@
1
1
  {
2
2
  "name": "@dereekb/firebase-server/test",
3
- "version": "13.32.0",
3
+ "version": "13.34.0",
4
4
  "peerDependencies": {
5
- "@dereekb/analytics": "13.32.0",
6
- "@dereekb/date": "13.32.0",
7
- "@dereekb/firebase": "13.32.0",
8
- "@dereekb/firebase-server": "13.32.0",
9
- "@dereekb/firebase-server/oidc": "13.32.0",
10
- "@dereekb/model": "13.32.0",
11
- "@dereekb/nestjs": "13.32.0",
12
- "@dereekb/rxjs": "13.32.0",
13
- "@dereekb/util": "13.32.0",
5
+ "@dereekb/analytics": "13.34.0",
6
+ "@dereekb/date": "13.34.0",
7
+ "@dereekb/firebase": "13.34.0",
8
+ "@dereekb/firebase-server": "13.34.0",
9
+ "@dereekb/firebase-server/oidc": "13.34.0",
10
+ "@dereekb/model": "13.34.0",
11
+ "@dereekb/nestjs": "13.34.0",
12
+ "@dereekb/rxjs": "13.34.0",
13
+ "@dereekb/util": "13.34.0",
14
14
  "@google-cloud/firestore": "^7.11.6",
15
15
  "@google-cloud/storage": "^7.19.0",
16
16
  "@nestjs/common": "^11.1.19",
@@ -23,7 +23,7 @@
23
23
  "supertest": "^7.2.2"
24
24
  },
25
25
  "devDependencies": {
26
- "@dereekb/nestjs": "13.32.0"
26
+ "@dereekb/nestjs": "13.34.0"
27
27
  },
28
28
  "exports": {
29
29
  "./package.json": "./package.json",
@@ -283,7 +283,7 @@ export declare function authorizedUserContextFactory<PI extends FirebaseAdminTes
283
283
  /**
284
284
  * Incrementing number factory for generating test UID values.
285
285
  *
286
- * Has the format 'test-uid-<number>'
286
+ * Has the format '<timestamp>0<counter><random>'
287
287
  */
288
288
  export declare const testUidFactory: Factory<FirebaseAuthUserId>;
289
289
  /**
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "@dereekb/firebase-server/twilio",
3
- "version": "13.32.0",
3
+ "version": "13.34.0",
4
4
  "peerDependencies": {
5
- "@dereekb/date": "13.32.0",
6
- "@dereekb/firebase": "13.32.0",
7
- "@dereekb/firebase-server": "13.32.0",
8
- "@dereekb/model": "13.32.0",
9
- "@dereekb/nestjs": "13.32.0",
10
- "@dereekb/rxjs": "13.32.0",
11
- "@dereekb/util": "13.32.0"
5
+ "@dereekb/date": "13.34.0",
6
+ "@dereekb/firebase": "13.34.0",
7
+ "@dereekb/firebase-server": "13.34.0",
8
+ "@dereekb/model": "13.34.0",
9
+ "@dereekb/nestjs": "13.34.0",
10
+ "@dereekb/rxjs": "13.34.0",
11
+ "@dereekb/util": "13.34.0"
12
12
  },
13
13
  "exports": {
14
14
  "./package.json": "./package.json",