@dereekb/firebase-server 13.35.0 → 13.37.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
@@ -2819,7 +2819,7 @@ function _define_property$b(obj, key, value) {
2819
2819
  }
2820
2820
  return obj;
2821
2821
  }
2822
- function _instanceof$2(left, right) {
2822
+ function _instanceof$3(left, right) {
2823
2823
  "@swc/helpers - instanceof";
2824
2824
  if (right != null && typeof Symbol !== "undefined" && right[Symbol.hasInstance]) {
2825
2825
  return !!right[Symbol.hasInstance](left);
@@ -2988,7 +2988,7 @@ var GRANTABLE_MODELS = new Set(GRANTABLE_MODEL_NAMES);
2988
2988
  case 0:
2989
2989
  // Set ownership key for Client entries so firestore rules can restrict reads by owner.
2990
2990
  // The firestoreOwnerKey is passed through the payload metadata by OidcClientService.
2991
- o = this.name === firebase.OIDC_ENTRY_CLIENT_TYPE ? payload.firestoreOwnerKey : undefined;
2991
+ o = this.name === firebase.OIDC_ENTRY_CLIENT_TYPE ? payload['firestoreOwnerKey'] : undefined;
2992
2992
  // oidc-provider uses `accountId` as the user identifier on Grant, AccessToken,
2993
2993
  // RefreshToken, etc. — `payload.uid` is only populated on Session/Interaction.
2994
2994
  // Fall back to `accountId` so the indexed `uid` column works for all entry types.
@@ -3002,7 +3002,7 @@ var GRANTABLE_MODELS = new Set(GRANTABLE_MODEL_NAMES);
3002
3002
  // inherit `iat` (epoch seconds) from BaseToken; Client entries use
3003
3003
  // `created_at` (ISO string).
3004
3004
  iat = payload.iat;
3005
- createdAtIso = payload.created_at;
3005
+ createdAtIso = payload['created_at'];
3006
3006
  if (typeof iat === 'number') {
3007
3007
  createdAt = util.unixDateTimeSecondsNumberToDate(iat);
3008
3008
  } else if (createdAtIso) {
@@ -3230,7 +3230,7 @@ var GRANTABLE_MODELS = new Set(GRANTABLE_MODEL_NAMES);
3230
3230
  value: function _toPayload(data) {
3231
3231
  var expiresDate;
3232
3232
  if (data.expiresAt) {
3233
- expiresDate = _instanceof$2(data.expiresAt, Date) ? data.expiresAt : data.expiresAt.toDate();
3233
+ expiresDate = _instanceof$3(data.expiresAt, Date) ? data.expiresAt : data.expiresAt.toDate();
3234
3234
  }
3235
3235
  var isExpired = expiresDate != null && expiresDate < new Date();
3236
3236
  return isExpired ? undefined : encryptionService.decryptAdapterPayload(data.payload);
@@ -3242,13 +3242,13 @@ var GRANTABLE_MODELS = new Set(GRANTABLE_MODEL_NAMES);
3242
3242
  return FirestoreAdapter;
3243
3243
  }
3244
3244
 
3245
- function _array_like_to_array$6(arr, len) {
3245
+ function _array_like_to_array$7(arr, len) {
3246
3246
  if (len == null || len > arr.length) len = arr.length;
3247
3247
  for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
3248
3248
  return arr2;
3249
3249
  }
3250
- function _array_without_holes$6(arr) {
3251
- if (Array.isArray(arr)) return _array_like_to_array$6(arr);
3250
+ function _array_without_holes$7(arr) {
3251
+ if (Array.isArray(arr)) return _array_like_to_array$7(arr);
3252
3252
  }
3253
3253
  function _class_call_check$a(instance, Constructor) {
3254
3254
  if (!(instance instanceof Constructor)) {
@@ -3281,22 +3281,22 @@ function _define_property$a(obj, key, value) {
3281
3281
  }
3282
3282
  return obj;
3283
3283
  }
3284
- function _iterable_to_array$6(iter) {
3284
+ function _iterable_to_array$7(iter) {
3285
3285
  if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
3286
3286
  }
3287
- function _non_iterable_spread$6() {
3287
+ function _non_iterable_spread$7() {
3288
3288
  throw new TypeError("Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
3289
3289
  }
3290
- function _to_consumable_array$6(arr) {
3291
- return _array_without_holes$6(arr) || _iterable_to_array$6(arr) || _unsupported_iterable_to_array$6(arr) || _non_iterable_spread$6();
3290
+ function _to_consumable_array$7(arr) {
3291
+ return _array_without_holes$7(arr) || _iterable_to_array$7(arr) || _unsupported_iterable_to_array$7(arr) || _non_iterable_spread$7();
3292
3292
  }
3293
- function _unsupported_iterable_to_array$6(o, minLen) {
3293
+ function _unsupported_iterable_to_array$7(o, minLen) {
3294
3294
  if (!o) return;
3295
- if (typeof o === "string") return _array_like_to_array$6(o, minLen);
3295
+ if (typeof o === "string") return _array_like_to_array$7(o, minLen);
3296
3296
  var n = Object.prototype.toString.call(o).slice(8, -1);
3297
3297
  if (n === "Object" && o.constructor) n = o.constructor.name;
3298
3298
  if (n === "Map" || n === "Set") return Array.from(n);
3299
- if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array$6(o, minLen);
3299
+ if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array$7(o, minLen);
3300
3300
  }
3301
3301
  // MARK: Encrypted Fields
3302
3302
  /**
@@ -3324,7 +3324,7 @@ function _unsupported_iterable_to_array$6(o, minLen) {
3324
3324
  this.provider = nestjs.createAesStringEncryptionProvider(config.jwksKeyConverterConfig.encryptionSecret);
3325
3325
  this.adapterPayloadEncryptor = util.selectiveFieldEncryptor({
3326
3326
  provider: this.provider,
3327
- fields: _to_consumable_array$6(OIDC_ENCRYPTED_PAYLOAD_FIELDS)
3327
+ fields: _to_consumable_array$7(OIDC_ENCRYPTED_PAYLOAD_FIELDS)
3328
3328
  });
3329
3329
  }
3330
3330
  _create_class$8(OidcEncryptionService, [
@@ -3360,13 +3360,13 @@ exports.OidcEncryptionService = __decorate([
3360
3360
  __param(0, common.Inject(OidcModuleConfig))
3361
3361
  ], exports.OidcEncryptionService);
3362
3362
 
3363
- function _array_like_to_array$5(arr, len) {
3363
+ function _array_like_to_array$6(arr, len) {
3364
3364
  if (len == null || len > arr.length) len = arr.length;
3365
3365
  for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
3366
3366
  return arr2;
3367
3367
  }
3368
- function _array_without_holes$5(arr) {
3369
- if (Array.isArray(arr)) return _array_like_to_array$5(arr);
3368
+ function _array_without_holes$6(arr) {
3369
+ if (Array.isArray(arr)) return _array_like_to_array$6(arr);
3370
3370
  }
3371
3371
  function _class_call_check$9(instance, Constructor) {
3372
3372
  if (!(instance instanceof Constructor)) {
@@ -3399,22 +3399,22 @@ function _define_property$9(obj, key, value) {
3399
3399
  }
3400
3400
  return obj;
3401
3401
  }
3402
- function _iterable_to_array$5(iter) {
3402
+ function _iterable_to_array$6(iter) {
3403
3403
  if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
3404
3404
  }
3405
- function _non_iterable_spread$5() {
3405
+ function _non_iterable_spread$6() {
3406
3406
  throw new TypeError("Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
3407
3407
  }
3408
- function _to_consumable_array$5(arr) {
3409
- return _array_without_holes$5(arr) || _iterable_to_array$5(arr) || _unsupported_iterable_to_array$5(arr) || _non_iterable_spread$5();
3408
+ function _to_consumable_array$6(arr) {
3409
+ return _array_without_holes$6(arr) || _iterable_to_array$6(arr) || _unsupported_iterable_to_array$6(arr) || _non_iterable_spread$6();
3410
3410
  }
3411
- function _unsupported_iterable_to_array$5(o, minLen) {
3411
+ function _unsupported_iterable_to_array$6(o, minLen) {
3412
3412
  if (!o) return;
3413
- if (typeof o === "string") return _array_like_to_array$5(o, minLen);
3413
+ if (typeof o === "string") return _array_like_to_array$6(o, minLen);
3414
3414
  var n = Object.prototype.toString.call(o).slice(8, -1);
3415
3415
  if (n === "Object" && o.constructor) n = o.constructor.name;
3416
3416
  if (n === "Map" || n === "Set") return Array.from(n);
3417
- if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array$5(o, minLen);
3417
+ if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array$6(o, minLen);
3418
3418
  }
3419
3419
  // MARK: Routes
3420
3420
  /**
@@ -3463,7 +3463,7 @@ var DEFAULT_OIDC_CODE_CHALLENGE_METHODS = [
3463
3463
  */ exports.OidcProviderConfigService = /*#__PURE__*/ function() {
3464
3464
  function OidcProviderConfigService(config, accountService, envService) {
3465
3465
  _class_call_check$9(this, OidcProviderConfigService);
3466
- var _this_config_tokenEndpointAuthMethods;
3466
+ var _this_providerConfig_providerProfiles, _this_config_tokenEndpointAuthMethods;
3467
3467
  _define_property$9(this, "config", void 0);
3468
3468
  _define_property$9(this, "routes", DEFAULT_OIDC_ROUTES);
3469
3469
  /**
@@ -3482,6 +3482,19 @@ var DEFAULT_OIDC_CODE_CHALLENGE_METHODS = [
3482
3482
  * Scopes derived from the claims configuration keys.
3483
3483
  */ _define_property$9(this, "scopesSupported", void 0);
3484
3484
  /**
3485
+ * {@link scopesSupported} minus the scopes only an admin-assigned {@link OidcProviderProfile}
3486
+ * unlocks — what an arbitrary client (a dynamically registered one included) may actually put in
3487
+ * an authorization request.
3488
+ *
3489
+ * The issuer's own discovery document keeps advertising the full {@link scopesSupported}: the
3490
+ * provider does support those scopes, for the clients holding the profile. This narrower list is
3491
+ * for metadata a client treats as a request template — notably an MCP protected-resource
3492
+ * document's `scopes_supported`, which dynamic-registration clients (the Claude Code CLI) copy
3493
+ * verbatim onto `/authorize`. An assignment-only scope advertised there ends the flow in
3494
+ * `access_denied`: the consent unlock gate judges the REQUEST, so unlike an admin-only scope
3495
+ * there is no deselect-at-consent way through.
3496
+ */ _define_property$9(this, "clientRequestableScopesSupported", void 0);
3497
+ /**
3485
3498
  * Flat list of all unique claim names from the claims configuration.
3486
3499
  */ _define_property$9(this, "claimsSupported", void 0);
3487
3500
  /**
@@ -3490,8 +3503,12 @@ var DEFAULT_OIDC_CODE_CHALLENGE_METHODS = [
3490
3503
  this.config = config;
3491
3504
  this.providerConfig = accountService.providerConfig;
3492
3505
  this.scopesSupported = Object.keys(this.providerConfig.claims);
3506
+ var assignmentOnlyScopes = firebase.assignmentOnlyScopesForOidcProviderProfiles((_this_providerConfig_providerProfiles = this.providerConfig.providerProfiles) !== null && _this_providerConfig_providerProfiles !== void 0 ? _this_providerConfig_providerProfiles : []);
3507
+ this.clientRequestableScopesSupported = this.scopesSupported.filter(function(scope) {
3508
+ return !assignmentOnlyScopes.has(scope);
3509
+ });
3493
3510
  this.claimsSupported = Array.from(new Set(Object.values(this.providerConfig.claims).flat()));
3494
- this.tokenEndpointAuthMethodsSupported = (_this_config_tokenEndpointAuthMethods = this.config.tokenEndpointAuthMethods) !== null && _this_config_tokenEndpointAuthMethods !== void 0 ? _this_config_tokenEndpointAuthMethods : _to_consumable_array$5(DEFAULT_OIDC_TOKEN_ENDPOINT_AUTH_METHODS);
3511
+ this.tokenEndpointAuthMethodsSupported = (_this_config_tokenEndpointAuthMethods = this.config.tokenEndpointAuthMethods) !== null && _this_config_tokenEndpointAuthMethods !== void 0 ? _this_config_tokenEndpointAuthMethods : _to_consumable_array$6(DEFAULT_OIDC_TOKEN_ENDPOINT_AUTH_METHODS);
3495
3512
  var appUrl = envService.appUrl;
3496
3513
  this.appLoginUrl = util.websiteUrlFromPaths(appUrl, [
3497
3514
  this.config.appOAuthInteractionPath,
@@ -3524,16 +3541,16 @@ var DEFAULT_OIDC_CODE_CHALLENGE_METHODS = [
3524
3541
  jwks_uri: jwksUri !== null && jwksUri !== void 0 ? jwksUri : "".concat(issuer).concat(routes.jwks),
3525
3542
  registration_endpoint: this.oidcRegistrationRouteEnabled ? "".concat(issuer).concat(routes.registration) : undefined,
3526
3543
  scopes_supported: this.scopesSupported,
3527
- response_types_supported: _to_consumable_array$5(providerConfig.responseTypes),
3544
+ response_types_supported: _to_consumable_array$6(providerConfig.responseTypes),
3528
3545
  response_modes_supported: [
3529
3546
  'query'
3530
3547
  ],
3531
- grant_types_supported: _to_consumable_array$5(providerConfig.grantTypes),
3532
- subject_types_supported: _to_consumable_array$5(DEFAULT_OIDC_SUBJECT_TYPES),
3533
- id_token_signing_alg_values_supported: _to_consumable_array$5(DEFAULT_OIDC_ID_TOKEN_SIGNING_ALG_VALUES),
3534
- token_endpoint_auth_methods_supported: _to_consumable_array$5(this.tokenEndpointAuthMethodsSupported),
3548
+ grant_types_supported: _to_consumable_array$6(providerConfig.grantTypes),
3549
+ subject_types_supported: _to_consumable_array$6(DEFAULT_OIDC_SUBJECT_TYPES),
3550
+ id_token_signing_alg_values_supported: _to_consumable_array$6(DEFAULT_OIDC_ID_TOKEN_SIGNING_ALG_VALUES),
3551
+ token_endpoint_auth_methods_supported: _to_consumable_array$6(this.tokenEndpointAuthMethodsSupported),
3535
3552
  claims_supported: this.claimsSupported,
3536
- code_challenge_methods_supported: _to_consumable_array$5(DEFAULT_OIDC_CODE_CHALLENGE_METHODS)
3553
+ code_challenge_methods_supported: _to_consumable_array$6(DEFAULT_OIDC_CODE_CHALLENGE_METHODS)
3537
3554
  };
3538
3555
  }
3539
3556
  }
@@ -3547,6 +3564,31 @@ exports.OidcProviderConfigService = __decorate([
3547
3564
  __param(2, common.Inject(firebaseServer.FirebaseServerEnvService))
3548
3565
  ], exports.OidcProviderConfigService);
3549
3566
 
3567
+ function _array_like_to_array$5(arr, len) {
3568
+ if (len == null || len > arr.length) len = arr.length;
3569
+ for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
3570
+ return arr2;
3571
+ }
3572
+ function _array_without_holes$5(arr) {
3573
+ if (Array.isArray(arr)) return _array_like_to_array$5(arr);
3574
+ }
3575
+ function _iterable_to_array$5(iter) {
3576
+ if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
3577
+ }
3578
+ function _non_iterable_spread$5() {
3579
+ throw new TypeError("Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
3580
+ }
3581
+ function _to_consumable_array$5(arr) {
3582
+ return _array_without_holes$5(arr) || _iterable_to_array$5(arr) || _unsupported_iterable_to_array$5(arr) || _non_iterable_spread$5();
3583
+ }
3584
+ function _unsupported_iterable_to_array$5(o, minLen) {
3585
+ if (!o) return;
3586
+ if (typeof o === "string") return _array_like_to_array$5(o, minLen);
3587
+ var n = Object.prototype.toString.call(o).slice(8, -1);
3588
+ if (n === "Object" && o.constructor) n = o.constructor.name;
3589
+ if (n === "Map" || n === "Set") return Array.from(n);
3590
+ if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array$5(o, minLen);
3591
+ }
3550
3592
  /**
3551
3593
  * Custom oidc-provider client metadata field holding the {@link OidcProviderProfile} keys assigned
3552
3594
  * to a client. Registered via `extraClientMetadata` and persisted on the client's `OidcEntry` payload.
@@ -3572,6 +3614,61 @@ exports.OidcProviderConfigService = __decorate([
3572
3614
  required: firebase.requiredScopesForOidcProviderProfiles(clientProfiles)
3573
3615
  };
3574
3616
  }
3617
+ /**
3618
+ * Resolves every scope restricted to admin users: the provider config's own `adminOnlyScopes`
3619
+ * unioned with the scopes of each profile marked {@link OidcProviderProfile.adminOnly}.
3620
+ *
3621
+ * The single choke point for that union — the consent admin-only gate (which hard-rejects a
3622
+ * non-admin who consented to one) and the consent URL builder (which withholds them from a
3623
+ * non-admin's consent screen in the first place) both read through it, so the set a user is
3624
+ * offered and the set they are judged against cannot drift apart.
3625
+ *
3626
+ * @param providerConfig - The provider config supplying `adminOnlyScopes` and the profile registry.
3627
+ * @returns The union of config-level and profile-level admin-only scopes.
3628
+ */ function adminOnlyScopesForOidcProviderConfig(providerConfig) {
3629
+ var _providerConfig_adminOnlyScopes, _providerConfig_providerProfiles;
3630
+ return new Set(_to_consumable_array$5((_providerConfig_adminOnlyScopes = providerConfig.adminOnlyScopes) !== null && _providerConfig_adminOnlyScopes !== void 0 ? _providerConfig_adminOnlyScopes : []).concat(_to_consumable_array$5(firebase.adminOnlyScopesForOidcProviderProfiles((_providerConfig_providerProfiles = providerConfig.providerProfiles) !== null && _providerConfig_providerProfiles !== void 0 ? _providerConfig_providerProfiles : []))));
3631
+ }
3632
+
3633
+ /**
3634
+ * Reason string of the check {@link buildOidcInteractionPolicy} adds to the `login` prompt.
3635
+ */ var STALE_SESSION_ACCOUNT_CHECK_REASON = 'stale_session_account';
3636
+ /**
3637
+ * Builds the interaction policy: oidc-provider's default prompts, plus a `login` check that
3638
+ * catches a session whose account no longer resolves.
3639
+ *
3640
+ * The default policy crashes on that case. `login`'s `no_session` check passes on
3641
+ * `oidc.session.accountId` alone — the value in the `_session` cookie — while `loadAccount` puts
3642
+ * the result of `findAccount` on `oidc.account`, and `loadGrant` establishes `oidc.grant` ONLY when
3643
+ * that account resolved. So a cookie naming a deleted account skips the login prompt, reaches the
3644
+ * `consent` prompt's `op_scopes_missing` check, and dereferences `oidc.grant.getOIDCScopeEncountered()`
3645
+ * on `undefined` — a `TypeError` that oidc-provider renders as an opaque `server_error`, on every
3646
+ * authorization request, until the user clears the cookie by hand.
3647
+ *
3648
+ * Deleted accounts are ordinary: an emulator database reset in development, an account deletion in
3649
+ * production. Re-authenticating is the correct response, so this requests the `login` prompt and the
3650
+ * user recovers by logging in. The check is added to `login` rather than guarding `consent` so the
3651
+ * prompt loop short-circuits before any consent check runs (it breaks on the first prompt that
3652
+ * fires).
3653
+ *
3654
+ * @param policyNamespace - The `interactionPolicy` namespace from the runtime `oidc-provider` import.
3655
+ * @returns The default policy with the stale-session check installed on the `login` prompt.
3656
+ */ function buildOidcInteractionPolicy(policyNamespace) {
3657
+ var base = policyNamespace.base, Check = policyNamespace.Check;
3658
+ var policy = base();
3659
+ var loginPrompt = policy.get('login');
3660
+ if (loginPrompt) {
3661
+ var staleSessionAccountCheck = new Check(STALE_SESSION_ACCOUNT_CHECK_REASON, 'End-User authentication is required', 'login_required', function(ctx) {
3662
+ var _oidc_session;
3663
+ var oidc = ctx.oidc;
3664
+ return ((_oidc_session = oidc.session) === null || _oidc_session === void 0 ? void 0 : _oidc_session.accountId) && !oidc.account ? Check.REQUEST_PROMPT : Check.NO_NEED_TO_PROMPT;
3665
+ });
3666
+ // Placed after `login_prompt`/`no_session` (appended) — order within a prompt only affects which
3667
+ // reason is reported when several fire, and a stale account is the more specific diagnosis.
3668
+ loginPrompt.checks.add(staleSessionAccountCheck);
3669
+ }
3670
+ return policy;
3671
+ }
3575
3672
 
3576
3673
  function _array_like_to_array$4(arr, len) {
3577
3674
  if (len == null || len > arr.length) len = arr.length;
@@ -3641,6 +3738,14 @@ function _define_property$8(obj, key, value) {
3641
3738
  }
3642
3739
  return obj;
3643
3740
  }
3741
+ function _instanceof$2(left, right) {
3742
+ "@swc/helpers - instanceof";
3743
+ if (right != null && typeof Symbol !== "undefined" && right[Symbol.hasInstance]) {
3744
+ return !!right[Symbol.hasInstance](left);
3745
+ } else {
3746
+ return left instanceof right;
3747
+ }
3748
+ }
3644
3749
  function _iterable_to_array$4(iter) {
3645
3750
  if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
3646
3751
  }
@@ -3827,11 +3932,12 @@ function _ts_generator$5(thisArg, body) {
3827
3932
  };
3828
3933
  }
3829
3934
  }
3935
+ var OidcService_1;
3830
3936
  // MARK: Service
3831
3937
  /**
3832
3938
  * Core OIDC service that wraps the oidc-provider instance and exposes
3833
3939
  * typed methods for interaction handling, provider initialization, and JWKS management.
3834
- */ exports.OidcService = /*#__PURE__*/ function() {
3940
+ */ exports.OidcService = OidcService_1 = /*#__PURE__*/ function() {
3835
3941
  function OidcService(config, providerConfigService, jwksService, accountService, collections, encryptionService) {
3836
3942
  var _this = this;
3837
3943
  _class_call_check$8(this, OidcService);
@@ -3841,6 +3947,7 @@ function _ts_generator$5(thisArg, body) {
3841
3947
  _define_property$8(this, "accountService", void 0);
3842
3948
  _define_property$8(this, "collections", void 0);
3843
3949
  _define_property$8(this, "encryptionService", void 0);
3950
+ _define_property$8(this, "_logger", new common.Logger(OidcService_1.name));
3844
3951
  _define_property$8(this, "_getProvider", util.cachedGetter(function() {
3845
3952
  return _this._buildProvider();
3846
3953
  }));
@@ -4239,11 +4346,11 @@ function _ts_generator$5(thisArg, body) {
4239
4346
  interactions: {
4240
4347
  url: function url(_ctx, interaction) {
4241
4348
  return _async_to_generator$5(function() {
4242
- var baseUrl, client_id, paramsToEncode, client, _client_dbx_provider_profiles, scopes, requestedScopeSet, required, requiredScopes, interactionLoginDetails, paramsString;
4349
+ var baseUrl, client_id, paramsToEncode, client, _client_dbx_provider_profiles, requestedScopes, isAdmin, adminOnlyScopes, offeredScopes, scopes, requestedScopeSet, required, requiredScopes, interactionLoginDetails, paramsString;
4243
4350
  return _ts_generator$5(this, function(_state) {
4244
4351
  switch(_state.label){
4245
4352
  case 0:
4246
- client_id = interaction.params.client_id;
4353
+ client_id = interaction.params['client_id'];
4247
4354
  paramsToEncode = {
4248
4355
  uid: interaction.uid,
4249
4356
  client_id: client_id
@@ -4255,7 +4362,7 @@ function _ts_generator$5(thisArg, body) {
4255
4362
  baseUrl = this.providerConfigService.appLoginUrl;
4256
4363
  return [
4257
4364
  3,
4258
- 3
4365
+ 4
4259
4366
  ];
4260
4367
  case 1:
4261
4368
  baseUrl = this.providerConfigService.appConsentUrl;
@@ -4265,29 +4372,42 @@ function _ts_generator$5(thisArg, body) {
4265
4372
  ];
4266
4373
  case 2:
4267
4374
  client = _state.sent();
4268
- if (client) {
4269
- scopes = interaction.params.scope;
4270
- // Surface the scopes the client's provider profiles force-require so the consent UI can
4271
- // render them as required. Intersect with the actually-requested scopes so only visible
4272
- // scope rows are flagged.
4273
- requestedScopeSet = new Set(scopes.split(' ').filter(Boolean));
4274
- 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;
4275
- requiredScopes = Array.from(required).filter(function(scope) {
4276
- return requestedScopeSet.has(scope);
4277
- });
4278
- interactionLoginDetails = _object_spread$3({
4279
- client_id: client_id,
4280
- client_name: client.client_name,
4281
- client_uri: client.client_uri,
4282
- logo_uri: client.logo_uri,
4283
- scopes: scopes
4284
- }, requiredScopes.length > 0 ? {
4285
- requiredScopes: requiredScopes.join(' ')
4286
- } : {});
4287
- paramsToEncode = _object_spread$3({}, paramsToEncode, interactionLoginDetails);
4288
- }
4289
- _state.label = 3;
4375
+ if (!client) return [
4376
+ 3,
4377
+ 4
4378
+ ];
4379
+ requestedScopes = interaction.params['scope'].split(' ').filter(Boolean);
4380
+ return [
4381
+ 4,
4382
+ this._isAdminInteractionUser(interaction)
4383
+ ];
4290
4384
  case 3:
4385
+ isAdmin = _state.sent();
4386
+ adminOnlyScopes = isAdmin ? undefined : adminOnlyScopesForOidcProviderConfig(providerConfig);
4387
+ offeredScopes = adminOnlyScopes ? requestedScopes.filter(function(scope) {
4388
+ return !adminOnlyScopes.has(scope);
4389
+ }) : requestedScopes;
4390
+ scopes = offeredScopes.join(' ');
4391
+ // Surface the scopes the client's provider profiles force-require so the consent UI can
4392
+ // render them as required. Intersect with the actually-offered scopes so only visible
4393
+ // scope rows are flagged.
4394
+ requestedScopeSet = new Set(offeredScopes);
4395
+ 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;
4396
+ requiredScopes = Array.from(required).filter(function(scope) {
4397
+ return requestedScopeSet.has(scope);
4398
+ });
4399
+ interactionLoginDetails = _object_spread$3({
4400
+ client_id: client_id,
4401
+ client_name: client.client_name,
4402
+ client_uri: client.client_uri,
4403
+ logo_uri: client.logo_uri,
4404
+ scopes: scopes
4405
+ }, requiredScopes.length > 0 ? {
4406
+ requiredScopes: requiredScopes.join(' ')
4407
+ } : {});
4408
+ paramsToEncode = _object_spread$3({}, paramsToEncode, interactionLoginDetails);
4409
+ _state.label = 4;
4410
+ case 4:
4291
4411
  paramsString = fetch.makeUrlSearchParamsString(paramsToEncode, {
4292
4412
  useUrlSearchSpaceHandling: true
4293
4413
  });
@@ -4422,7 +4542,7 @@ function _ts_generator$5(thisArg, body) {
4422
4542
  value: // MARK: Internal
4423
4543
  function _buildProvider() {
4424
4544
  return _async_to_generator$5(function() {
4425
- var _this, _config_trustProxy, config, findAccount, signingKey, result, getEncryptionKey, cookieKey, adapterFactory, providerConfiguration, _ref, ProviderClass, provider;
4545
+ var _this, _config_trustProxy, config, findAccount, signingKey, result, getEncryptionKey, cookieKey, adapterFactory, providerConfiguration, _ref, ProviderClass, interactionPolicy, provider;
4426
4546
  return _ts_generator$5(this, function(_state) {
4427
4547
  switch(_state.label){
4428
4548
  case 0:
@@ -4463,8 +4583,14 @@ function _ts_generator$5(thisArg, body) {
4463
4583
  import('oidc-provider')
4464
4584
  ];
4465
4585
  case 4:
4466
- _ref = _state.sent(), ProviderClass = _ref.default;
4586
+ _ref = _state.sent(), ProviderClass = _ref.default, interactionPolicy = _ref.interactionPolicy;
4467
4587
  provider = new ProviderClass(config.issuer, _object_spread_props(_object_spread$3({}, providerConfiguration), {
4588
+ // The policy is applied here rather than in buildProviderConfiguration() because it needs the
4589
+ // `interactionPolicy` namespace off the dynamic (ESM-only) import, which that sync method has no
4590
+ // access to. Spread the configured `interactions` so its `url` builder survives.
4591
+ interactions: _object_spread_props(_object_spread$3({}, providerConfiguration.interactions), {
4592
+ policy: buildOidcInteractionPolicy(interactionPolicy)
4593
+ }),
4468
4594
  adapter: adapterFactory,
4469
4595
  findAccount: findAccount,
4470
4596
  jwks: {
@@ -4479,6 +4605,7 @@ function _ts_generator$5(thisArg, body) {
4479
4605
  // issuer's canonical host, so cookies set on the issuer host are not sent
4480
4606
  // on resume → "authorization request has expired".
4481
4607
  provider.proxy = (_config_trustProxy = config.trustProxy) !== null && _config_trustProxy !== void 0 ? _config_trustProxy : true;
4608
+ this._attachProviderErrorLogging(provider);
4482
4609
  if (config.suppressBodyParserWarning) ;
4483
4610
  return [
4484
4611
  2,
@@ -4488,11 +4615,66 @@ function _ts_generator$5(thisArg, body) {
4488
4615
  });
4489
4616
  }).call(this);
4490
4617
  }
4618
+ },
4619
+ {
4620
+ key: "_isAdminInteractionUser",
4621
+ value: /**
4622
+ * Whether the user an interaction resolved to is an admin.
4623
+ *
4624
+ * Reads the same `isAdminUser` delegate the consent admin-only gate uses. Fails closed — an
4625
+ * interaction with no resolved account (the login prompt, before authentication) or a delegate
4626
+ * that does not implement the hook is treated as non-admin, so admin-only scopes are withheld
4627
+ * unless admin status is positively established.
4628
+ *
4629
+ * @param interaction - The interaction whose session account should be checked.
4630
+ * @returns True when the interaction's account resolves to an admin user.
4631
+ */ function _isAdminInteractionUser(interaction) {
4632
+ return _async_to_generator$5(function() {
4633
+ var _ref, _interaction_session, _this_accountService_delegate_isAdminUser, _this_accountService_delegate, accountId;
4634
+ return _ts_generator$5(this, function(_state) {
4635
+ accountId = (_interaction_session = interaction.session) === null || _interaction_session === void 0 ? void 0 : _interaction_session.accountId;
4636
+ return [
4637
+ 2,
4638
+ accountId ? (_ref = (_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 && _ref !== void 0 ? _ref : false : false
4639
+ ];
4640
+ });
4641
+ }).call(this);
4642
+ }
4643
+ },
4644
+ {
4645
+ /**
4646
+ * Wires oidc-provider's error events to the Nest logger.
4647
+ *
4648
+ * oidc-provider catches everything thrown inside its koa stack and renders the generic
4649
+ * `{"error":"server_error","error_description":"oops! something went wrong"}` body. The real
4650
+ * cause is only ever handed to the `server_error` event — with no listener it is dropped, and a
4651
+ * failing authorization request is undiagnosable from either the response or the function logs.
4652
+ *
4653
+ * `grant.error` / `introspection.error` / `revocation.error` cover the token-endpoint equivalents.
4654
+ * These are all genuine server-side faults; expected protocol rejections (`invalid_client`,
4655
+ * `access_denied`, …) travel as normal responses and are NOT emitted here, so this stays quiet in
4656
+ * healthy operation.
4657
+ *
4658
+ * @param provider - The constructed oidc-provider instance to attach listeners to.
4659
+ */ key: "_attachProviderErrorLogging",
4660
+ value: function _attachProviderErrorLogging(provider) {
4661
+ var _this = this;
4662
+ var logError = function logError(event) {
4663
+ return function(ctx, err) {
4664
+ var route = ctx ? "".concat(ctx.method, " ").concat(ctx.path) : 'unknown route';
4665
+ _this._logger.error("oidc-provider ".concat(event, " on ").concat(route, ": ").concat(_instanceof$2(err, Error) ? err.stack : String(err)));
4666
+ };
4667
+ };
4668
+ provider.on('server_error', logError('server_error'));
4669
+ provider.on('grant.error', logError('grant.error'));
4670
+ provider.on('introspection.error', logError('introspection.error'));
4671
+ provider.on('revocation.error', logError('revocation.error'));
4672
+ }
4491
4673
  }
4492
4674
  ]);
4493
4675
  return OidcService;
4494
4676
  }();
4495
- exports.OidcService = __decorate([
4677
+ exports.OidcService = OidcService_1 = __decorate([
4496
4678
  common.Injectable(),
4497
4679
  __param(0, common.Inject(OidcModuleConfig)),
4498
4680
  __param(1, common.Inject(exports.OidcProviderConfigService)),
@@ -6267,9 +6449,9 @@ var OidcInteractionController_1;
6267
6449
  case 5:
6268
6450
  interaction = _state.sent();
6269
6451
  prompt = interaction.prompt, params = interaction.params, session = interaction.session;
6270
- clientId = params.client_id;
6452
+ clientId = params['client_id'];
6271
6453
  accountId = (_ref = session === null || session === void 0 ? void 0 : session.accountId) !== null && _ref !== void 0 ? _ref : '';
6272
- missingOIDCScope = (_prompt_details_missingOIDCScope = prompt.details.missingOIDCScope) !== null && _prompt_details_missingOIDCScope !== void 0 ? _prompt_details_missingOIDCScope : [];
6454
+ missingOIDCScope = (_prompt_details_missingOIDCScope = prompt.details['missingOIDCScope']) !== null && _prompt_details_missingOIDCScope !== void 0 ? _prompt_details_missingOIDCScope : [];
6273
6455
  if (!interaction.grantId) return [
6274
6456
  3,
6275
6457
  7
@@ -6407,8 +6589,13 @@ var OidcInteractionController_1;
6407
6589
  // cannot know the caller's admin status when it builds that request. Gating on the request would
6408
6590
  // make an admin-only scope in `scopes_supported` fatal for every non-admin — deselecting it at
6409
6591
  // consent has to be a way through.
6592
+ //
6593
+ // A non-admin should not normally reach this gate at all: the consent URL builder withholds
6594
+ // admin-only scopes from their consent screen, so the scope never enters the consented set (see
6595
+ // `adminOnlyScopesForOidcProviderConfig`, which both sides read). This remains as defense in
6596
+ // depth for a consent submit that names the scope anyway.
6410
6597
  configAdminOnlyScopes = (_this_accountService_providerConfig_adminOnlyScopes = this.accountService.providerConfig.adminOnlyScopes) !== null && _this_accountService_providerConfig_adminOnlyScopes !== void 0 ? _this_accountService_providerConfig_adminOnlyScopes : [];
6411
- adminOnlyScopes = new Set(_to_consumable_array$1(configAdminOnlyScopes).concat(_to_consumable_array$1(firebase.adminOnlyScopesForOidcProviderProfiles(providerProfiles !== null && providerProfiles !== void 0 ? providerProfiles : []))));
6598
+ adminOnlyScopes = adminOnlyScopesForOidcProviderConfig(this.accountService.providerConfig);
6412
6599
  consentedAdminOnlyScopes = Array.from(adminOnlyScopes).filter(function(scope) {
6413
6600
  return consentedOIDCScopeSet.has(scope);
6414
6601
  });
@@ -6528,7 +6715,7 @@ var OidcInteractionController_1;
6528
6715
  }
6529
6716
  }
6530
6717
  encounteredOIDCClaims = grant.getOIDCClaimsEncountered();
6531
- missingOIDCClaims = (_prompt_details_missingOIDCClaims = prompt.details.missingOIDCClaims) !== null && _prompt_details_missingOIDCClaims !== void 0 ? _prompt_details_missingOIDCClaims : [];
6718
+ missingOIDCClaims = (_prompt_details_missingOIDCClaims = prompt.details['missingOIDCClaims']) !== null && _prompt_details_missingOIDCClaims !== void 0 ? _prompt_details_missingOIDCClaims : [];
6532
6719
  if (missingOIDCClaims.length > 0) {
6533
6720
  _resolveEffectiveSubset = resolveEffectiveSubset({
6534
6721
  missing: missingOIDCClaims,
@@ -6542,7 +6729,7 @@ var OidcInteractionController_1;
6542
6729
  grant.rejectOIDCClaims(rejected1);
6543
6730
  }
6544
6731
  }
6545
- missingResourceScopes = (_prompt_details_missingResourceScopes = prompt.details.missingResourceScopes) !== null && _prompt_details_missingResourceScopes !== void 0 ? _prompt_details_missingResourceScopes : {};
6732
+ missingResourceScopes = (_prompt_details_missingResourceScopes = prompt.details['missingResourceScopes']) !== null && _prompt_details_missingResourceScopes !== void 0 ? _prompt_details_missingResourceScopes : {};
6546
6733
  _iteratorNormalCompletion1 = true, _didIteratorError1 = false, _iteratorError1 = undefined;
6547
6734
  try {
6548
6735
  for(_iterator1 = Object.entries(missingResourceScopes)[Symbol.iterator](); !(_iteratorNormalCompletion1 = (_step1 = _iterator1.next()).done); _iteratorNormalCompletion1 = true){
@@ -7860,7 +8047,9 @@ exports.OidcClientService = OidcClientService;
7860
8047
  exports.OidcModelServerActions = OidcModelServerActions;
7861
8048
  exports.OidcModuleConfig = OidcModuleConfig;
7862
8049
  exports.OidcServerFirestoreCollections = OidcServerFirestoreCollections;
8050
+ exports.STALE_SESSION_ACCOUNT_CHECK_REASON = STALE_SESSION_ACCOUNT_CHECK_REASON;
7863
8051
  exports.activeJwksKeysQuery = activeJwksKeysQuery;
8052
+ exports.adminOnlyScopesForOidcProviderConfig = adminOnlyScopesForOidcProviderConfig;
7864
8053
  exports.allOidcScopesStringForProviderConfig = allOidcScopesStringForProviderConfig;
7865
8054
  exports.appOidcAnalyticsModuleMetadata = appOidcAnalyticsModuleMetadata;
7866
8055
  exports.appOidcModelModuleMetadata = appOidcModelModuleMetadata;
@@ -7868,6 +8057,7 @@ exports.applyOidcAuthMiddleware = applyOidcAuthMiddleware;
7868
8057
  exports.applyOidcCorsMiddleware = applyOidcCorsMiddleware;
7869
8058
  exports.buildBearerChallenge = buildBearerChallenge;
7870
8059
  exports.buildFirebaseServerMcpResourceServer = buildFirebaseServerMcpResourceServer;
8060
+ exports.buildOidcInteractionPolicy = buildOidcInteractionPolicy;
7871
8061
  exports.createAdapterFactory = createAdapterFactory;
7872
8062
  exports.createOidcClientFactory = createOidcClientFactory;
7873
8063
  exports.deleteOidcClientFactory = deleteOidcClientFactory;