@dereekb/firebase-server 13.28.0 → 13.30.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.
package/oidc/index.cjs.js CHANGED
@@ -2431,6 +2431,9 @@ function _ts_generator$7(thisArg, body) {
2431
2431
  if (params.dbx_max_session_ttl != null) {
2432
2432
  properties.dbx_max_session_ttl = params.dbx_max_session_ttl;
2433
2433
  }
2434
+ if (params.dbx_provider_profiles != null) {
2435
+ properties.dbx_provider_profiles = params.dbx_provider_profiles;
2436
+ }
2434
2437
  // Merge any pre-validated metadata (e.g., inline jwks for private_key_jwt in tests)
2435
2438
  if (validatedMetadata === null || validatedMetadata === void 0 ? void 0 : validatedMetadata.jwks) {
2436
2439
  properties.jwks = validatedMetadata.jwks;
@@ -2488,7 +2491,7 @@ function _ts_generator$7(thisArg, body) {
2488
2491
  * @throws {Error} When the client is not found.
2489
2492
  */ function updateClient(clientId, params) {
2490
2493
  return _async_to_generator$7(function() {
2491
- var provider, ProviderClient, existing, updatedMetadata, _params_logo_uri, _params_client_uri, _params_dbx_max_session_ttl, client;
2494
+ var provider, ProviderClient, existing, updatedMetadata, _params_logo_uri, _params_client_uri, _params_dbx_max_session_ttl, _params_dbx_provider_profiles, client;
2492
2495
  return _ts_generator$7(this, function(_state) {
2493
2496
  switch(_state.label){
2494
2497
  case 0:
@@ -2520,6 +2523,12 @@ function _ts_generator$7(thisArg, body) {
2520
2523
  if (params.dbx_max_session_ttl !== undefined) {
2521
2524
  updatedMetadata.dbx_max_session_ttl = (_params_dbx_max_session_ttl = params.dbx_max_session_ttl) !== null && _params_dbx_max_session_ttl !== void 0 ? _params_dbx_max_session_ttl : undefined;
2522
2525
  }
2526
+ // Only overwrite the profile assignment when the field is explicitly provided. An omitted value
2527
+ // (including one stripped for a non-admin caller) preserves the existing assignment; `null`/`[]`
2528
+ // clears it. This is what makes the non-admin strip a no-op rather than a wipe.
2529
+ if (params.dbx_provider_profiles !== undefined) {
2530
+ updatedMetadata.dbx_provider_profiles = (_params_dbx_provider_profiles = params.dbx_provider_profiles) !== null && _params_dbx_provider_profiles !== void 0 ? _params_dbx_provider_profiles : undefined;
2531
+ }
2523
2532
  // Mirrors oidc-provider's lib/helpers/add_client.js: re-validates and persists.
2524
2533
  return [
2525
2534
  4,
@@ -3538,6 +3547,25 @@ exports.OidcProviderConfigService = __decorate([
3538
3547
  __param(2, common.Inject(firebaseServer.FirebaseServerEnvService))
3539
3548
  ], exports.OidcProviderConfigService);
3540
3549
 
3550
+ /**
3551
+ * Custom oidc-provider client metadata field holding the {@link OidcProviderProfile} keys assigned
3552
+ * to a client. Registered via `extraClientMetadata` and persisted on the client's `OidcEntry` payload.
3553
+ */ var DBX_FIREBASE_SERVER_OIDC_PROVIDER_PROFILES_CLIENT_METADATA = 'dbx_provider_profiles';
3554
+ /**
3555
+ * Resolves the unlocked + required scope sets for a client from the provider-profile registry and
3556
+ * the profile keys assigned to that client.
3557
+ *
3558
+ * @param providerProfiles - The provider-profile registry (from {@link OidcProviderConfig.providerProfiles}).
3559
+ * @param clientProfileKeys - The profile keys assigned to the client (its `dbx_provider_profiles`).
3560
+ * @returns The unlocked and required scope sets for the client.
3561
+ */ function oidcClientProviderProfileScopes(providerProfiles, clientProfileKeys) {
3562
+ var clientProfiles = firebase.oidcProviderProfilesForKeys(providerProfiles !== null && providerProfiles !== void 0 ? providerProfiles : [], clientProfileKeys);
3563
+ return {
3564
+ unlocked: firebase.scopesForOidcProviderProfiles(clientProfiles),
3565
+ required: firebase.requiredScopesForOidcProviderProfiles(clientProfiles)
3566
+ };
3567
+ }
3568
+
3541
3569
  function _array_like_to_array$4(arr, len) {
3542
3570
  if (len == null || len > arr.length) len = arr.length;
3543
3571
  for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
@@ -4042,7 +4070,8 @@ function _ts_generator$5(thisArg, body) {
4042
4070
  logo_uri: existing.logo_uri,
4043
4071
  client_uri: existing.client_uri,
4044
4072
  created_at: existing.created_at,
4045
- dbx_max_session_ttl: existing.dbx_max_session_ttl
4073
+ dbx_max_session_ttl: existing.dbx_max_session_ttl,
4074
+ dbx_provider_profiles: existing.dbx_provider_profiles
4046
4075
  };
4047
4076
  }
4048
4077
  return [
@@ -4149,7 +4178,8 @@ function _ts_generator$5(thisArg, body) {
4149
4178
  ],
4150
4179
  extraClientMetadata: {
4151
4180
  properties: [
4152
- DBX_FIREBASE_SERVER_OIDC_MAX_SESSION_TTL_CLIENT_METADATA
4181
+ DBX_FIREBASE_SERVER_OIDC_MAX_SESSION_TTL_CLIENT_METADATA,
4182
+ DBX_FIREBASE_SERVER_OIDC_PROVIDER_PROFILES_CLIENT_METADATA
4153
4183
  ],
4154
4184
  validator: function validator(_ctx, key, value) {
4155
4185
  if (key === DBX_FIREBASE_SERVER_OIDC_MAX_SESSION_TTL_CLIENT_METADATA && value !== undefined && value !== null) {
@@ -4159,6 +4189,22 @@ function _ts_generator$5(thisArg, body) {
4159
4189
  if (value > serverMaxSeconds) {
4160
4190
  throw new oidcProvider.errors.InvalidClientMetadata("".concat(DBX_FIREBASE_SERVER_OIDC_MAX_SESSION_TTL_CLIENT_METADATA, " cannot exceed the server max of ").concat(serverMaxSeconds, " seconds."));
4161
4191
  }
4192
+ } else if (key === DBX_FIREBASE_SERVER_OIDC_PROVIDER_PROFILES_CLIENT_METADATA && value !== undefined && value !== null) {
4193
+ var _providerConfig_providerProfiles;
4194
+ if (!Array.isArray(value) || value.some(function(entry) {
4195
+ return typeof entry !== 'string';
4196
+ })) {
4197
+ throw new oidcProvider.errors.InvalidClientMetadata("".concat(DBX_FIREBASE_SERVER_OIDC_PROVIDER_PROFILES_CLIENT_METADATA, " must be an array of provider profile keys."));
4198
+ }
4199
+ var knownProfileKeys = new Set(((_providerConfig_providerProfiles = providerConfig.providerProfiles) !== null && _providerConfig_providerProfiles !== void 0 ? _providerConfig_providerProfiles : []).map(function(profile) {
4200
+ return profile.key;
4201
+ }));
4202
+ var unknownKey = value.find(function(entry) {
4203
+ return !knownProfileKeys.has(entry);
4204
+ });
4205
+ if (unknownKey !== undefined) {
4206
+ throw new oidcProvider.errors.InvalidClientMetadata("".concat(DBX_FIREBASE_SERVER_OIDC_PROVIDER_PROFILES_CLIENT_METADATA, " contains an unknown provider profile key: ").concat(unknownKey, "."));
4207
+ }
4162
4208
  }
4163
4209
  }
4164
4210
  },
@@ -4186,7 +4232,7 @@ function _ts_generator$5(thisArg, body) {
4186
4232
  interactions: {
4187
4233
  url: function url(_ctx, interaction) {
4188
4234
  return _async_to_generator$5(function() {
4189
- var baseUrl, client_id, paramsToEncode, client, scopes, interactionLoginDetails, paramsString;
4235
+ var baseUrl, client_id, paramsToEncode, client, _client_dbx_provider_profiles, scopes, requestedScopeSet, required, requiredScopes, interactionLoginDetails, paramsString;
4190
4236
  return _ts_generator$5(this, function(_state) {
4191
4237
  switch(_state.label){
4192
4238
  case 0:
@@ -4214,13 +4260,23 @@ function _ts_generator$5(thisArg, body) {
4214
4260
  client = _state.sent();
4215
4261
  if (client) {
4216
4262
  scopes = interaction.params.scope;
4217
- interactionLoginDetails = {
4263
+ // Surface the scopes the client's provider profiles force-require so the consent UI can
4264
+ // render them as required. Intersect with the actually-requested scopes so only visible
4265
+ // scope rows are flagged.
4266
+ requestedScopeSet = new Set(scopes.split(' ').filter(Boolean));
4267
+ required = oidcClientProviderProfileScopes(providerConfig.providerProfiles, (_client_dbx_provider_profiles = client.dbx_provider_profiles) !== null && _client_dbx_provider_profiles !== void 0 ? _client_dbx_provider_profiles : undefined).required;
4268
+ requiredScopes = Array.from(required).filter(function(scope) {
4269
+ return requestedScopeSet.has(scope);
4270
+ });
4271
+ interactionLoginDetails = _object_spread$3({
4218
4272
  client_id: client_id,
4219
4273
  client_name: client.client_name,
4220
4274
  client_uri: client.client_uri,
4221
4275
  logo_uri: client.logo_uri,
4222
4276
  scopes: scopes
4223
- };
4277
+ }, requiredScopes.length > 0 ? {
4278
+ requiredScopes: requiredScopes.join(' ')
4279
+ } : {});
4224
4280
  paramsToEncode = _object_spread$3({}, paramsToEncode, interactionLoginDetails);
4225
4281
  }
4226
4282
  _state.label = 3;
@@ -5339,7 +5395,7 @@ function _unsupported_iterable_to_array$2(o, minLen) {
5339
5395
  * `request.auth.token.scope` for OIDC callers and is `undefined` for non-OIDC
5340
5396
  * (regular Firebase ID-token) callers.
5341
5397
  *
5342
- * Returning `undefined` for non-OIDC callers lets callers (e.g. `oidcCallModelScopePreAssert`)
5398
+ * Returning `undefined` for non-OIDC callers lets consumers (e.g. the MCP tool-visibility filter)
5343
5399
  * skip scope enforcement instead of falsely treating the missing claim as "no scopes granted".
5344
5400
  *
5345
5401
  * The parameter is intentionally typed as `unknown` because `DecodedIdToken` (Firebase
@@ -5351,10 +5407,7 @@ function _unsupported_iterable_to_array$2(o, minLen) {
5351
5407
  */ function getOidcScopesFromRequest(request) {
5352
5408
  var _request_auth_token, _request_auth;
5353
5409
  var scope = request === null || request === void 0 ? void 0 : (_request_auth = request.auth) === null || _request_auth === void 0 ? void 0 : (_request_auth_token = _request_auth.token) === null || _request_auth_token === void 0 ? void 0 : _request_auth_token.scope;
5354
- var result = typeof scope === 'string' ? new Set(scope.split(' ').filter(function(value) {
5355
- return value.length > 0;
5356
- })) : undefined;
5357
- return result;
5410
+ return firebase.oidcScopesFromScopeClaim(scope);
5358
5411
  }
5359
5412
 
5360
5413
  function asyncGeneratorStep$3(gen, resolve, reject, _next, _throw, key, arg) {
@@ -6159,7 +6212,7 @@ var OidcInteractionController_1;
6159
6212
  * @throws {HttpException} HTTP 400 when the consent interaction cannot be completed.
6160
6213
  */ function postConsent(uid, body, res) {
6161
6214
  return _async_to_generator$2(function() {
6162
- var startedAt, clientId, accountId, _ref, _prompt_details_missingOIDCScope, _this_accountService_providerConfig_adminOnlyScopes, _ref1, _ref2, _prompt_details_missingOIDCClaims, _prompt_details_missingResourceScopes, _this_accountService_delegate_isAdminUser, _this_accountService_delegate, _ref3, redirectTo, interaction, prompt, params, session, missingOIDCScope, existingGrant, _tmp, encounteredOIDCScopes, requestedOIDCScopeSet, adminOnlyScopes, requestsServiceToken, isAdmin, _tmp1, _ref4, redirectTo1, requestedRawTtl, clientPayload, 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, _ref5, redirectTo2, err;
6215
+ 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, adminOnlyScopes, requestsServiceToken, isAdmin, _tmp1, _ref5, redirectTo1, providerProfiles, _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;
6163
6216
  return _ts_generator$2(this, function(_state) {
6164
6217
  switch(_state.label){
6165
6218
  case 0:
@@ -6174,9 +6227,9 @@ var OidcInteractionController_1;
6174
6227
  case 2:
6175
6228
  _state.trys.push([
6176
6229
  2,
6177
- 20,
6230
+ 24,
6178
6231
  ,
6179
- 21
6232
+ 25
6180
6233
  ]);
6181
6234
  if (!!body.approved) return [
6182
6235
  3,
@@ -6192,7 +6245,7 @@ var OidcInteractionController_1;
6192
6245
  })
6193
6246
  ];
6194
6247
  case 3:
6195
- _ref3 = _state.sent(), redirectTo = _ref3.returnTo;
6248
+ _ref4 = _state.sent(), redirectTo = _ref4.returnTo;
6196
6249
  res.json({
6197
6250
  redirectTo: redirectTo
6198
6251
  });
@@ -6235,6 +6288,12 @@ var OidcInteractionController_1;
6235
6288
  // and re-submits them all. We accept already-encountered values as silent no-ops so a re-consent on a
6236
6289
  // client the user has previously authorized doesn't fail validation.
6237
6290
  encounteredOIDCScopes = existingGrant ? existingGrant.getOIDCScopeEncountered().split(' ').filter(Boolean) : [];
6291
+ return [
6292
+ 4,
6293
+ this.oidcService.findClientPayload(clientId)
6294
+ ];
6295
+ case 9:
6296
+ clientPayload = _state.sent();
6238
6297
  // Admin-only scope gate. The requested OIDC scope set is `missingOIDCScope ∪ encounteredOIDCScopes`
6239
6298
  // (a fresh consent has everything in `missing`; a re-consent may carry already-granted scopes in
6240
6299
  // `encountered`). If that set intersects the provider's `adminOnlyScopes` and the resolving user is
@@ -6246,26 +6305,26 @@ var OidcInteractionController_1;
6246
6305
  });
6247
6306
  if (!accountId) return [
6248
6307
  3,
6249
- 10
6308
+ 11
6250
6309
  ];
6251
6310
  return [
6252
6311
  4,
6253
6312
  (_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
6254
6313
  ];
6255
- case 9:
6314
+ case 10:
6256
6315
  _tmp1 = _state.sent();
6257
6316
  return [
6258
6317
  3,
6259
- 11
6318
+ 12
6260
6319
  ];
6261
- case 10:
6262
- _tmp1 = false;
6263
- _state.label = 11;
6264
6320
  case 11:
6321
+ _tmp1 = false;
6322
+ _state.label = 12;
6323
+ case 12:
6265
6324
  isAdmin = _tmp1;
6266
6325
  if (!(requestsServiceToken && !isAdmin)) return [
6267
6326
  3,
6268
- 13
6327
+ 14
6269
6328
  ];
6270
6329
  return [
6271
6330
  4,
@@ -6276,8 +6335,8 @@ var OidcInteractionController_1;
6276
6335
  mergeWithLastSubmission: true
6277
6336
  })
6278
6337
  ];
6279
- case 12:
6280
- _ref4 = _state.sent(), redirectTo1 = _ref4.returnTo;
6338
+ case 13:
6339
+ _ref5 = _state.sent(), redirectTo1 = _ref5.returnTo;
6281
6340
  emitOidcAnalyticsEvent(this._analytics, {
6282
6341
  type: 'consent',
6283
6342
  isSuccessful: false,
@@ -6294,20 +6353,89 @@ var OidcInteractionController_1;
6294
6353
  return [
6295
6354
  2
6296
6355
  ];
6297
- case 13:
6356
+ case 14:
6357
+ // Provider-profile scope gate. Any scope referenced by a provider profile is "gated" — available
6358
+ // only to clients whose assigned profiles unlock it. Independent of the admin-only gate above; a
6359
+ // scope may be subject to both.
6360
+ providerProfiles = this.accountService.providerConfig.providerProfiles;
6361
+ _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;
6362
+ profileGatedScopes = firebase.scopesForOidcProviderProfiles(providerProfiles !== null && providerProfiles !== void 0 ? providerProfiles : []);
6363
+ // Unlock gate: reject any requested gated scope this client's profiles do not unlock.
6364
+ disallowedGatedScopes = Array.from(requestedOIDCScopeSet).filter(function(scope) {
6365
+ return profileGatedScopes.has(scope) && !clientUnlockedScopes.has(scope);
6366
+ });
6367
+ if (!(disallowedGatedScopes.length > 0)) return [
6368
+ 3,
6369
+ 16
6370
+ ];
6371
+ return [
6372
+ 4,
6373
+ this.oidcInteractionService.finishInteractionByUid(uid, {
6374
+ error: 'access_denied',
6375
+ error_description: "The following scope(s) are not available to this client: ".concat(disallowedGatedScopes.join(', '), ".")
6376
+ }, {
6377
+ mergeWithLastSubmission: true
6378
+ })
6379
+ ];
6380
+ case 15:
6381
+ _ref6 = _state.sent(), redirectTo2 = _ref6.returnTo;
6382
+ emitOidcAnalyticsEvent(this._analytics, {
6383
+ type: 'consent',
6384
+ isSuccessful: false,
6385
+ uid: accountId,
6386
+ clientId: clientId,
6387
+ reason: 'scope_not_unlocked_for_client',
6388
+ durationMs: Date.now() - startedAt
6389
+ }, this._logger);
6390
+ res.json({
6391
+ redirectTo: redirectTo2
6392
+ });
6393
+ return [
6394
+ 2
6395
+ ];
6396
+ case 16:
6397
+ // Required gate: a scope a client's profiles force-require (`require: 'required'`) must be present
6398
+ // in the request. Reject when the client did not request a required scope.
6399
+ missingRequiredScopes = Array.from(clientRequiredScopes).filter(function(scope) {
6400
+ return !requestedOIDCScopeSet.has(scope);
6401
+ });
6402
+ if (!(missingRequiredScopes.length > 0)) return [
6403
+ 3,
6404
+ 18
6405
+ ];
6406
+ return [
6407
+ 4,
6408
+ this.oidcInteractionService.finishInteractionByUid(uid, {
6409
+ error: 'access_denied',
6410
+ error_description: "This client must request the required scope(s): ".concat(missingRequiredScopes.join(', '), ".")
6411
+ }, {
6412
+ mergeWithLastSubmission: true
6413
+ })
6414
+ ];
6415
+ case 17:
6416
+ _ref7 = _state.sent(), redirectTo3 = _ref7.returnTo;
6417
+ emitOidcAnalyticsEvent(this._analytics, {
6418
+ type: 'consent',
6419
+ isSuccessful: false,
6420
+ uid: accountId,
6421
+ clientId: clientId,
6422
+ reason: 'required_scope_missing',
6423
+ durationMs: Date.now() - startedAt
6424
+ }, this._logger);
6425
+ res.json({
6426
+ redirectTo: redirectTo3
6427
+ });
6428
+ return [
6429
+ 2
6430
+ ];
6431
+ case 18:
6298
6432
  // Resolve the requested login duration up-front. The configured Grant TTL function (in
6299
6433
  // OidcService.buildProviderConfiguration) only fires when oidc-provider's koa middleware
6300
6434
  // drives `grant.save()`, so its `ctx.oidc.params` lookup of `dbx_session_ttl` returns
6301
6435
  // undefined when the consent submit runs in this controller. We pre-set `expiresIn` on
6302
6436
  // newly-created grants so they persist with the correct (tiered) TTL.
6303
6437
  requestedRawTtl = params[DBX_FIREBASE_SERVER_OIDC_SESSION_TTL_PARAM];
6304
- return [
6305
- 4,
6306
- this.oidcService.findClientPayload(clientId)
6307
- ];
6308
- case 14:
6309
- clientPayload = _state.sent();
6310
- clientMaxSessionTtl = (_ref2 = clientPayload === null || clientPayload === void 0 ? void 0 : clientPayload.dbx_max_session_ttl) !== null && _ref2 !== void 0 ? _ref2 : undefined;
6438
+ clientMaxSessionTtl = (_ref3 = clientPayload === null || clientPayload === void 0 ? void 0 : clientPayload.dbx_max_session_ttl) !== null && _ref3 !== void 0 ? _ref3 : undefined;
6311
6439
  expiresInSeconds = this.oidcService.resolveLoginDurationForGrant(requestedRawTtl, {
6312
6440
  dbx_max_session_ttl: clientMaxSessionTtl
6313
6441
  }, {
@@ -6316,28 +6444,30 @@ var OidcInteractionController_1;
6316
6444
  });
6317
6445
  if (!(existingGrant !== null && existingGrant !== void 0)) return [
6318
6446
  3,
6319
- 15
6447
+ 19
6320
6448
  ];
6321
6449
  _tmp2 = existingGrant;
6322
6450
  return [
6323
6451
  3,
6324
- 17
6452
+ 21
6325
6453
  ];
6326
- case 15:
6454
+ case 19:
6327
6455
  return [
6328
6456
  4,
6329
6457
  this.oidcInteractionService.findOrCreateGrant(interaction.grantId, accountId, clientId, expiresInSeconds)
6330
6458
  ];
6331
- case 16:
6459
+ case 20:
6332
6460
  _tmp2 = _state.sent();
6333
- _state.label = 17;
6334
- case 17:
6461
+ _state.label = 21;
6462
+ case 21:
6335
6463
  grant = _tmp2;
6336
6464
  if (missingOIDCScope.length > 0) {
6465
+ // Force-grant the client's required profile scopes (in addition to `openid`) so they cannot be
6466
+ // dropped at consent — the required gate above already guarantees they were requested.
6337
6467
  _resolveEffectiveSubset = resolveEffectiveSubset({
6338
6468
  missing: missingOIDCScope,
6339
6469
  requestedSubset: body.grantedOIDCScopes,
6340
- alwaysGranted: ALWAYS_GRANTED_OIDC_SCOPES,
6470
+ alwaysGranted: _to_consumable_array$1(ALWAYS_GRANTED_OIDC_SCOPES).concat(_to_consumable_array$1(clientRequiredScopes)),
6341
6471
  alreadyEncountered: encounteredOIDCScopes
6342
6472
  }), granted = _resolveEffectiveSubset.granted, rejected = _resolveEffectiveSubset.rejected;
6343
6473
  if (granted.length > 0) {
@@ -6434,7 +6564,7 @@ var OidcInteractionController_1;
6434
6564
  4,
6435
6565
  grant.save()
6436
6566
  ];
6437
- case 18:
6567
+ case 22:
6438
6568
  grantId = _state.sent();
6439
6569
  return [
6440
6570
  4,
@@ -6446,8 +6576,8 @@ var OidcInteractionController_1;
6446
6576
  mergeWithLastSubmission: true
6447
6577
  })
6448
6578
  ];
6449
- case 19:
6450
- _ref5 = _state.sent(), redirectTo2 = _ref5.returnTo;
6579
+ case 23:
6580
+ _ref8 = _state.sent(), redirectTo4 = _ref8.returnTo;
6451
6581
  emitOidcAnalyticsEvent(this._analytics, {
6452
6582
  type: 'consent',
6453
6583
  isSuccessful: true,
@@ -6459,13 +6589,13 @@ var OidcInteractionController_1;
6459
6589
  durationMs: Date.now() - startedAt
6460
6590
  }, this._logger);
6461
6591
  res.json({
6462
- redirectTo: redirectTo2
6592
+ redirectTo: redirectTo4
6463
6593
  });
6464
6594
  return [
6465
6595
  3,
6466
- 21
6596
+ 25
6467
6597
  ];
6468
- case 20:
6598
+ case 24:
6469
6599
  err = _state.sent();
6470
6600
  emitOidcAnalyticsEvent(this._analytics, {
6471
6601
  type: 'consent',
@@ -6480,7 +6610,7 @@ var OidcInteractionController_1;
6480
6610
  throw err;
6481
6611
  }
6482
6612
  throw new common.HttpException('Consent interaction failed', common.HttpStatus.BAD_REQUEST);
6483
- case 21:
6613
+ case 25:
6484
6614
  return [
6485
6615
  2
6486
6616
  ];
@@ -7648,41 +7778,7 @@ function _unsupported_iterable_to_array(o, minLen) {
7648
7778
  };
7649
7779
  }
7650
7780
 
7651
- /**
7652
- * Builds a {@link AssertModelCrudRequestFunction} that rejects callModel requests
7653
- * lacking the OIDC scope mapped from the call type.
7654
- *
7655
- * Bypasses the check (no-op) when the request is not OIDC-authenticated — i.e.
7656
- * when {@link getOidcScopesFromRequest} returns `undefined` because there is no
7657
- * `auth.token.scope` claim on the request. Regular Firebase ID-token callers are
7658
- * unaffected and continue to be gated by auth roles.
7659
- *
7660
- * Bypasses the check for custom (non-CRUD) call types so app-specific verbs
7661
- * remain unrestricted unless an app wires its own assertion.
7662
- *
7663
- * Wire as the `preAssert` of {@link onCallModel}.
7664
- *
7665
- * @returns The pre-assertion function ready to plug into `OnCallModelConfig.preAssert`.
7666
- */ function oidcCallModelScopePreAssert() {
7667
- var fn = function fn(context) {
7668
- var requiredScope = firebase.callModelOidcScopeForCallType(context.call);
7669
- var scopes = requiredScope == null ? undefined : getOidcScopesFromRequest(context.request);
7670
- var isMissingScope = requiredScope != null && scopes != null && !scopes.has(requiredScope);
7671
- if (isMissingScope) {
7672
- throw firebaseServer.forbiddenError({
7673
- status: 403,
7674
- code: firebase.CALL_MODEL_MISSING_OIDC_SCOPE_ERROR_CODE,
7675
- message: "Missing required OIDC scope for callModel: ".concat(requiredScope),
7676
- data: {
7677
- requiredScope: requiredScope,
7678
- call: context.call
7679
- }
7680
- });
7681
- }
7682
- };
7683
- return fn;
7684
- }
7685
-
7781
+ exports.DBX_FIREBASE_SERVER_OIDC_PROVIDER_PROFILES_CLIENT_METADATA = DBX_FIREBASE_SERVER_OIDC_PROVIDER_PROFILES_CLIENT_METADATA;
7686
7782
  exports.DEFAULT_APP_OAUTH_CONSENT_PATH_PART = DEFAULT_APP_OAUTH_CONSENT_PATH_PART;
7687
7783
  exports.DEFAULT_APP_OAUTH_INTERACTION_PATH = DEFAULT_APP_OAUTH_INTERACTION_PATH;
7688
7784
  exports.DEFAULT_APP_OAUTH_LOGIN_PATH_PART = DEFAULT_APP_OAUTH_LOGIN_PATH_PART;
@@ -7744,7 +7840,7 @@ exports.jwksKeyIdentity = jwksKeyIdentity;
7744
7840
  exports.jwksKeysWithStatusQuery = jwksKeysWithStatusQuery;
7745
7841
  exports.nonRetiredJwksKeysQuery = nonRetiredJwksKeysQuery;
7746
7842
  exports.noopOidcAnalyticsService = noopOidcAnalyticsService;
7747
- exports.oidcCallModelScopePreAssert = oidcCallModelScopePreAssert;
7843
+ exports.oidcClientProviderProfileScopes = oidcClientProviderProfileScopes;
7748
7844
  exports.oidcCorsOriginDelegate = oidcCorsOriginDelegate;
7749
7845
  exports.oidcFirestoreCollectionsFactory = oidcFirestoreCollectionsFactory;
7750
7846
  exports.oidcModelServerActions = oidcModelServerActions;