@dereekb/firebase-server 13.29.0 → 13.31.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.esm.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import { createParamDecorator, Injectable, Inject, Optional, Logger, UnauthorizedException, Get, Param, Req, Res, Post, HttpCode, HttpStatus, Body, Controller, HttpException, All } from '@nestjs/common';
2
2
  import { errors } from 'oidc-provider';
3
- import { SECONDS_IN_MINUTE, SECONDS_IN_DAY, cachedGetter, unixDateTimeSecondsNumberToDate, unixDateTimeSecondsNumberForNow, selectiveFieldEncryptor, filterUndefinedValues, websiteUrlFromPaths, firstValue, hasHttpPrefix, filterMaybeArrayValues } from '@dereekb/util';
3
+ import { SECONDS_IN_MINUTE, SECONDS_IN_DAY, cachedGetter, unixDateTimeSecondsNumberToDate, unixDateTimeSecondsNumberForNow, selectiveFieldEncryptor, filterUndefinedValues, websiteUrlFromPaths, firstValue, hasHttpPrefix } from '@dereekb/util';
4
4
  import { generateKeyPairSync, randomBytes } from 'node:crypto';
5
5
  import { resolveEncryptionKey, encryptValue, decryptValue, createAesStringEncryptionProvider, isValidAES256GCMEncryptionSecret } from '@dereekb/nestjs';
6
- import { where, iterateFirestoreDocumentSnapshotPairs, firestoreModelIdentity, snapshotConverterFunctions, optionalFirestoreDate, firestoreDate, firestoreEnum, firestorePassThroughField, AbstractFirestoreDocument, createOidcClientParamsType, deleteOidcClientParamsType, deleteOidcTokenParamsType, rotateOidcClientSecretParamsType, updateOidcClientParamsType, firestoreModelKey, oidcEntryIdentity, PUBLIC_PKCE_TOKEN_ENDPOINT_AUTH_METHOD, OIDC_ENTRY_CLIENT_TYPE, oidcEntriesByUserCodeQuery, oidcEntriesByUidQuery, oidcEntriesByGrantIdQuery, oidcProviderProfilesForKeys, requiredScopesForOidcProviderProfiles, scopesForOidcProviderProfiles, oidcEntryFirestoreCollection, callModelOidcScopeForCallType, CALL_MODEL_MISSING_OIDC_SCOPE_ERROR_CODE } from '@dereekb/firebase';
7
- import { firestoreEncryptedField, firebaseServerActionsContext, FirebaseServerEnvService, FirebaseServerAnalyticsService, FIREBASE_FIRESTORE_CONTEXT_TOKEN, FirebaseServerFirestoreContextModule, forbiddenError } from '@dereekb/firebase-server';
6
+ import { where, iterateFirestoreDocumentSnapshotPairs, firestoreModelIdentity, snapshotConverterFunctions, optionalFirestoreDate, firestoreDate, firestoreEnum, firestorePassThroughField, AbstractFirestoreDocument, createOidcClientParamsType, deleteOidcClientParamsType, deleteOidcTokenParamsType, rotateOidcClientSecretParamsType, updateOidcClientParamsType, firestoreModelKey, oidcEntryIdentity, PUBLIC_PKCE_TOKEN_ENDPOINT_AUTH_METHOD, OIDC_ENTRY_CLIENT_TYPE, oidcEntriesByUserCodeQuery, oidcEntriesByUidQuery, oidcEntriesByGrantIdQuery, oidcProviderProfilesForClient, requiredScopesForOidcProviderProfiles, scopesForOidcProviderProfiles, oidcScopesFromScopeClaim, adminOnlyScopesForOidcProviderProfiles, oidcEntryFirestoreCollection } from '@dereekb/firebase';
7
+ import { firestoreEncryptedField, firebaseServerActionsContext, FirebaseServerEnvService, FirebaseServerAnalyticsService, FIREBASE_FIRESTORE_CONTEXT_TOKEN, FirebaseServerFirestoreContextModule } from '@dereekb/firebase-server';
8
8
  import { nanoid } from 'nanoid';
9
9
  import { safeToJsDate } from '@dereekb/date';
10
10
  import { makeUrlSearchParamsString } from '@dereekb/util/fetch';
@@ -3553,11 +3553,18 @@ OidcProviderConfigService = __decorate([
3553
3553
  * Resolves the unlocked + required scope sets for a client from the provider-profile registry and
3554
3554
  * the profile keys assigned to that client.
3555
3555
  *
3556
+ * A client with NO assigned profiles resolves to the registry's default profiles (those marked
3557
+ * `isDefault`), so a registry declaring no default resolves exactly as it would from the assigned
3558
+ * keys alone.
3559
+ *
3560
+ * This is the single choke point for a client's profile resolution — the consent unlock gate, the
3561
+ * consent required gate, and the `requiredScopes` interaction param all read through it.
3562
+ *
3556
3563
  * @param providerProfiles - The provider-profile registry (from {@link OidcProviderConfig.providerProfiles}).
3557
3564
  * @param clientProfileKeys - The profile keys assigned to the client (its `dbx_provider_profiles`).
3558
3565
  * @returns The unlocked and required scope sets for the client.
3559
3566
  */ function oidcClientProviderProfileScopes(providerProfiles, clientProfileKeys) {
3560
- var clientProfiles = oidcProviderProfilesForKeys(providerProfiles !== null && providerProfiles !== void 0 ? providerProfiles : [], clientProfileKeys);
3567
+ var clientProfiles = oidcProviderProfilesForClient(providerProfiles !== null && providerProfiles !== void 0 ? providerProfiles : [], clientProfileKeys);
3561
3568
  return {
3562
3569
  unlocked: scopesForOidcProviderProfiles(clientProfiles),
3563
3570
  required: requiredScopesForOidcProviderProfiles(clientProfiles)
@@ -5393,7 +5400,7 @@ function _unsupported_iterable_to_array$2(o, minLen) {
5393
5400
  * `request.auth.token.scope` for OIDC callers and is `undefined` for non-OIDC
5394
5401
  * (regular Firebase ID-token) callers.
5395
5402
  *
5396
- * Returning `undefined` for non-OIDC callers lets callers (e.g. `oidcCallModelScopePreAssert`)
5403
+ * Returning `undefined` for non-OIDC callers lets consumers (e.g. the MCP tool-visibility filter)
5397
5404
  * skip scope enforcement instead of falsely treating the missing claim as "no scopes granted".
5398
5405
  *
5399
5406
  * The parameter is intentionally typed as `unknown` because `DecodedIdToken` (Firebase
@@ -5405,10 +5412,7 @@ function _unsupported_iterable_to_array$2(o, minLen) {
5405
5412
  */ function getOidcScopesFromRequest(request) {
5406
5413
  var _request_auth_token, _request_auth;
5407
5414
  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;
5408
- var result = typeof scope === 'string' ? new Set(scope.split(' ').filter(function(value) {
5409
- return value.length > 0;
5410
- })) : undefined;
5411
- return result;
5415
+ return oidcScopesFromScopeClaim(scope);
5412
5416
  }
5413
5417
 
5414
5418
  function asyncGeneratorStep$3(gen, resolve, reject, _next, _throw, key, arg) {
@@ -6213,7 +6217,7 @@ var OidcInteractionController_1;
6213
6217
  * @throws {HttpException} HTTP 400 when the consent interaction cannot be completed.
6214
6218
  */ function postConsent(uid, body, res) {
6215
6219
  return _async_to_generator$2(function() {
6216
- 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;
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;
6217
6221
  return _ts_generator$2(this, function(_state) {
6218
6222
  switch(_state.label){
6219
6223
  case 0:
@@ -6297,11 +6301,20 @@ var OidcInteractionController_1;
6297
6301
  clientPayload = _state.sent();
6298
6302
  // Admin-only scope gate. The requested OIDC scope set is `missingOIDCScope ∪ encounteredOIDCScopes`
6299
6303
  // (a fresh consent has everything in `missing`; a re-consent may carry already-granted scopes in
6300
- // `encountered`). If that set intersects the provider's `adminOnlyScopes` and the resolving user is
6301
- // not an admin, hard-reject with `access_denied` rather than silently dropping the scope.
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
6307
  requestedOIDCScopeSet = new Set(_to_consumable_array$1(missingOIDCScope).concat(_to_consumable_array$1(encounteredOIDCScopes)));
6303
- adminOnlyScopes = (_this_accountService_providerConfig_adminOnlyScopes = this.accountService.providerConfig.adminOnlyScopes) !== null && _this_accountService_providerConfig_adminOnlyScopes !== void 0 ? _this_accountService_providerConfig_adminOnlyScopes : [];
6304
- requestsServiceToken = adminOnlyScopes.some(function(scope) {
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) {
6305
6318
  return requestedOIDCScopeSet.has(scope);
6306
6319
  });
6307
6320
  if (!accountId) return [
@@ -6323,7 +6336,7 @@ var OidcInteractionController_1;
6323
6336
  _state.label = 12;
6324
6337
  case 12:
6325
6338
  isAdmin = _tmp1;
6326
- if (!(requestsServiceToken && !isAdmin)) return [
6339
+ if (!(requestedAdminOnlyScopes.length > 0 && !isAdmin)) return [
6327
6340
  3,
6328
6341
  14
6329
6342
  ];
@@ -6331,7 +6344,7 @@ var OidcInteractionController_1;
6331
6344
  4,
6332
6345
  this.oidcInteractionService.finishInteractionByUid(uid, {
6333
6346
  error: 'access_denied',
6334
- error_description: 'token.service is restricted to admins.'
6347
+ error_description: "The following scope(s) are restricted to admins: ".concat(requestedAdminOnlyScopes.join(', '), ".")
6335
6348
  }, {
6336
6349
  mergeWithLastSubmission: true
6337
6350
  })
@@ -6343,7 +6356,7 @@ var OidcInteractionController_1;
6343
6356
  isSuccessful: false,
6344
6357
  uid: accountId,
6345
6358
  clientId: clientId,
6346
- serviceToken: true,
6359
+ serviceToken: requestsServiceToken,
6347
6360
  isAdmin: false,
6348
6361
  reason: 'service_token_non_admin',
6349
6362
  durationMs: Date.now() - startedAt
@@ -6356,9 +6369,9 @@ var OidcInteractionController_1;
6356
6369
  ];
6357
6370
  case 14:
6358
6371
  // Provider-profile scope gate. Any scope referenced by a provider profile is "gated" — available
6359
- // only to clients whose assigned profiles unlock it. Independent of the admin-only gate above; a
6360
- // scope may be subject to both.
6361
- providerProfiles = this.accountService.providerConfig.providerProfiles;
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.
6362
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;
6363
6376
  profileGatedScopes = scopesForOidcProviderProfiles(providerProfiles !== null && providerProfiles !== void 0 ? providerProfiles : []);
6364
6377
  // Unlock gate: reject any requested gated scope this client's profiles do not unlock.
@@ -7779,51 +7792,4 @@ function _unsupported_iterable_to_array(o, minLen) {
7779
7792
  };
7780
7793
  }
7781
7794
 
7782
- /**
7783
- * Builds a {@link AssertModelCrudRequestFunction} that rejects callModel requests
7784
- * lacking the required OIDC scope(s) for the call.
7785
- *
7786
- * Enforcement is additive: an OIDC caller must hold BOTH the per-verb scope mapped
7787
- * from the call type (`model.<call>`) AND any per-function scope a handler declared
7788
- * via `withApiDetails({ requiredScope })` — surfaced on `context.requiredScope`.
7789
- *
7790
- * Bypasses the check (no-op) when the request is not OIDC-authenticated — i.e.
7791
- * when {@link getOidcScopesFromRequest} returns `undefined` because there is no
7792
- * `auth.token.scope` claim on the request. Regular Firebase ID-token callers are
7793
- * unaffected and continue to be gated by auth roles.
7794
- *
7795
- * Bypasses the check for custom (non-CRUD) call types that declare no per-function
7796
- * scope, so app-specific verbs remain unrestricted unless an app wires its own
7797
- * assertion or declares a `requiredScope`.
7798
- *
7799
- * Wire as the `preAssert` of {@link onCallModel}.
7800
- *
7801
- * @returns The pre-assertion function ready to plug into `OnCallModelConfig.preAssert`.
7802
- */ function oidcCallModelScopePreAssert() {
7803
- var fn = function fn(context) {
7804
- var requiredScopes = filterMaybeArrayValues([
7805
- callModelOidcScopeForCallType(context.call),
7806
- context.requiredScope
7807
- ]);
7808
- // No requirement (custom verb + no per-function scope) short-circuits without reading scopes.
7809
- // A non-OIDC caller yields `undefined` scopes below and bypasses just like the per-verb check.
7810
- var scopes = requiredScopes.length === 0 ? undefined : getOidcScopesFromRequest(context.request);
7811
- var missingScopes = scopes == null ? [] : requiredScopes.filter(function(scope) {
7812
- return !scopes.has(scope);
7813
- });
7814
- if (missingScopes.length > 0) {
7815
- throw forbiddenError({
7816
- status: 403,
7817
- code: CALL_MODEL_MISSING_OIDC_SCOPE_ERROR_CODE,
7818
- message: "Missing required OIDC scope for callModel: ".concat(missingScopes.join(', ')),
7819
- data: {
7820
- requiredScopes: missingScopes,
7821
- call: context.call
7822
- }
7823
- });
7824
- }
7825
- };
7826
- return fn;
7827
- }
7828
-
7829
- export { DBX_FIREBASE_SERVER_OIDC_PROVIDER_PROFILES_CLIENT_METADATA, DEFAULT_APP_OAUTH_CONSENT_PATH_PART, DEFAULT_APP_OAUTH_INTERACTION_PATH, DEFAULT_APP_OAUTH_LOGIN_PATH_PART, DEFAULT_MAX_ADMIN_LOGIN_DURATION_SECONDS, DEFAULT_MAX_NONADMIN_LOGIN_DURATION_SECONDS, DEFAULT_MAX_REQUESTED_LOGIN_DURATION_SECONDS, DEFAULT_MAX_SERVICE_TOKEN_LOGIN_DURATION_SECONDS, DEFAULT_MIN_REQUESTED_LOGIN_DURATION_SECONDS, DEFAULT_OIDC_ANALYTICS_EVENT_NAMES, DEFAULT_OIDC_ANALYTICS_EVENT_NAME_PREFIX, DEFAULT_OIDC_CODE_CHALLENGE_METHODS, DEFAULT_OIDC_ID_TOKEN_SIGNING_ALG_VALUES, DEFAULT_OIDC_ISSUER_PATH, DEFAULT_OIDC_ROUTES, DEFAULT_OIDC_SUBJECT_TYPES, DEFAULT_OIDC_TOKEN_ENDPOINT_AUTH_METHODS, DEFAULT_OIDC_TOKEN_LIFETIMES, DEFAULT_ROTATED_KEY_MAX_AGE, FIREBASE_SERVER_OIDC_ANALYTICS_CONFIG, FIREBASE_SERVER_OIDC_ROUTES_FOR_GLOBAL_ROUTE_EXCLUDE, FirebaseServerOidcAnalyticsService, GRANTABLE_MODEL_NAMES, JwksFirestoreCollections, JwksKeyDocument, JwksService, JwksServiceConfig, JwksServiceStorageConfig, OIDC_ANALYTICS_SERVICE, OIDC_ENCRYPTED_PAYLOAD_FIELDS, OIDC_JSON_RENDER_ERROR_FUNCTION, OIDC_JWKS_ENCRYPTION_SECRET_ENV_KEY, OidcAccountService, OidcAccountServiceDelegate, OidcAccountServiceUserContext, OidcAnalyticsService, OidcAuth, OidcAuthBearerTokenMiddleware, OidcAuthMiddlewareConfig, OidcClientService, OidcEncryptionService, OidcInteractionController, OidcInteractionService, OidcModelServerActions, OidcModuleConfig, OidcProviderConfigService, OidcProviderController, OidcServerFirestoreCollections, OidcService, OidcWellKnownController, activeJwksKeysQuery, allOidcScopesStringForProviderConfig, appOidcAnalyticsModuleMetadata, appOidcModelModuleMetadata, applyOidcAuthMiddleware, applyOidcCorsMiddleware, buildBearerChallenge, buildFirebaseServerMcpResourceServer, createAdapterFactory, createOidcClientFactory, deleteOidcClientFactory, deleteOidcTokenFactory, deriveResourceMetadataUrlFromEnv, emitOidcAnalyticsEvent, getOidcScopesFromRequest, jwksKeyCollectionReference, jwksKeyConverter, jwksKeyFirestoreCollection, jwksKeyIdentity, jwksKeysWithStatusQuery, nonRetiredJwksKeysQuery, noopOidcAnalyticsService, oidcCallModelScopePreAssert, oidcClientProviderProfileScopes, oidcCorsOriginDelegate, oidcFirestoreCollectionsFactory, oidcModelServerActions, oidcModelServerActionsFactory, oidcModuleConfigFactory, oidcModuleMetadata, resolveEffectiveSubset, rotateOidcClientSecretFactory, rotatedJwksKeysQuery, updateOidcClientFactory };
7795
+ export { DBX_FIREBASE_SERVER_OIDC_PROVIDER_PROFILES_CLIENT_METADATA, DEFAULT_APP_OAUTH_CONSENT_PATH_PART, DEFAULT_APP_OAUTH_INTERACTION_PATH, DEFAULT_APP_OAUTH_LOGIN_PATH_PART, DEFAULT_MAX_ADMIN_LOGIN_DURATION_SECONDS, DEFAULT_MAX_NONADMIN_LOGIN_DURATION_SECONDS, DEFAULT_MAX_REQUESTED_LOGIN_DURATION_SECONDS, DEFAULT_MAX_SERVICE_TOKEN_LOGIN_DURATION_SECONDS, DEFAULT_MIN_REQUESTED_LOGIN_DURATION_SECONDS, DEFAULT_OIDC_ANALYTICS_EVENT_NAMES, DEFAULT_OIDC_ANALYTICS_EVENT_NAME_PREFIX, DEFAULT_OIDC_CODE_CHALLENGE_METHODS, DEFAULT_OIDC_ID_TOKEN_SIGNING_ALG_VALUES, DEFAULT_OIDC_ISSUER_PATH, DEFAULT_OIDC_ROUTES, DEFAULT_OIDC_SUBJECT_TYPES, DEFAULT_OIDC_TOKEN_ENDPOINT_AUTH_METHODS, DEFAULT_OIDC_TOKEN_LIFETIMES, DEFAULT_ROTATED_KEY_MAX_AGE, FIREBASE_SERVER_OIDC_ANALYTICS_CONFIG, FIREBASE_SERVER_OIDC_ROUTES_FOR_GLOBAL_ROUTE_EXCLUDE, FirebaseServerOidcAnalyticsService, GRANTABLE_MODEL_NAMES, JwksFirestoreCollections, JwksKeyDocument, JwksService, JwksServiceConfig, JwksServiceStorageConfig, OIDC_ANALYTICS_SERVICE, OIDC_ENCRYPTED_PAYLOAD_FIELDS, OIDC_JSON_RENDER_ERROR_FUNCTION, OIDC_JWKS_ENCRYPTION_SECRET_ENV_KEY, OidcAccountService, OidcAccountServiceDelegate, OidcAccountServiceUserContext, OidcAnalyticsService, OidcAuth, OidcAuthBearerTokenMiddleware, OidcAuthMiddlewareConfig, OidcClientService, OidcEncryptionService, OidcInteractionController, OidcInteractionService, OidcModelServerActions, OidcModuleConfig, OidcProviderConfigService, OidcProviderController, OidcServerFirestoreCollections, OidcService, OidcWellKnownController, activeJwksKeysQuery, allOidcScopesStringForProviderConfig, appOidcAnalyticsModuleMetadata, appOidcModelModuleMetadata, applyOidcAuthMiddleware, applyOidcCorsMiddleware, buildBearerChallenge, buildFirebaseServerMcpResourceServer, createAdapterFactory, createOidcClientFactory, deleteOidcClientFactory, deleteOidcTokenFactory, deriveResourceMetadataUrlFromEnv, emitOidcAnalyticsEvent, getOidcScopesFromRequest, jwksKeyCollectionReference, jwksKeyConverter, jwksKeyFirestoreCollection, jwksKeyIdentity, jwksKeysWithStatusQuery, nonRetiredJwksKeysQuery, noopOidcAnalyticsService, oidcClientProviderProfileScopes, oidcCorsOriginDelegate, oidcFirestoreCollectionsFactory, oidcModelServerActions, oidcModelServerActionsFactory, oidcModuleConfigFactory, oidcModuleMetadata, resolveEffectiveSubset, rotateOidcClientSecretFactory, rotatedJwksKeysQuery, updateOidcClientFactory };
package/oidc/package.json CHANGED
@@ -1,16 +1,16 @@
1
1
  {
2
2
  "name": "@dereekb/firebase-server/oidc",
3
- "version": "13.29.0",
3
+ "version": "13.31.0",
4
4
  "peerDependencies": {
5
- "@dereekb/analytics": "13.29.0",
6
- "@dereekb/date": "13.29.0",
7
- "@dereekb/firebase": "13.29.0",
8
- "@dereekb/firebase-server": "13.29.0",
9
- "@dereekb/model": "13.29.0",
10
- "@dereekb/nestjs": "13.29.0",
11
- "@dereekb/rxjs": "13.29.0",
12
- "@dereekb/util": "13.29.0",
13
- "@dereekb/zoho": "13.29.0",
5
+ "@dereekb/analytics": "13.31.0",
6
+ "@dereekb/date": "13.31.0",
7
+ "@dereekb/firebase": "13.31.0",
8
+ "@dereekb/firebase-server": "13.31.0",
9
+ "@dereekb/model": "13.31.0",
10
+ "@dereekb/nestjs": "13.31.0",
11
+ "@dereekb/rxjs": "13.31.0",
12
+ "@dereekb/util": "13.31.0",
13
+ "@dereekb/zoho": "13.31.0",
14
14
  "@nestjs/common": "^11.1.19",
15
15
  "@nestjs/config": "^4.0.4",
16
16
  "express": "^5.2.1",
@@ -4,5 +4,4 @@ export * from './oidc.config';
4
4
  export * from './controller';
5
5
  export * from './oidc.module';
6
6
  export * from './profile';
7
- export * from './scope';
8
7
  export * from './service';
@@ -94,6 +94,11 @@ export interface OidcProviderConfig<S extends OidcScope = OidcScope> {
94
94
  *
95
95
  * Keeps the generic package app-agnostic: apps opt a scope (e.g. `token.service`) into admin-only
96
96
  * behavior by listing it here, rather than the package hard-coding any scope name.
97
+ *
98
+ * The consent gate applies this list UNIONED with the scopes of every {@link providerProfiles} entry
99
+ * marked `adminOnly`. Only the scopes listed HERE additionally select the (highest) service-token
100
+ * login-duration tier — a profile marked `adminOnly` gates by admin status without widening its
101
+ * grant's lifetime.
97
102
  */
98
103
  readonly adminOnlyScopes?: readonly string[];
99
104
  /**
@@ -111,6 +116,12 @@ export interface OidcProviderConfig<S extends OidcScope = OidcScope> {
111
116
  * metadata) unlocks it. A profile scope marked `require: 'required'` is force-required — surfaced
112
117
  * as a required scope at consent and hard-rejected if not granted.
113
118
  *
119
+ * A profile marked `isDefault` applies to a client with NO profiles assigned, so an app can make a
120
+ * coarse, broadly-available scope profile-gated without breaking already-registered clients. The
121
+ * fallback is exclusive — assigning any other profile does not additionally confer the default's
122
+ * scopes. A profile marked `adminOnly` has its scopes unioned into {@link adminOnlyScopes} for the
123
+ * consent gate.
124
+ *
114
125
  * Keeps the generic package app-agnostic: apps declare their profiles statically and list them
115
126
  * here, rather than the package hard-coding any profile or scope name. This gate is independent of
116
127
  * {@link adminOnlyScopes} (which gates by the resolving user's admin status); a scope may be
@@ -22,6 +22,13 @@ export interface OidcClientProviderProfileScopes<S extends OidcScope = OidcScope
22
22
  * Resolves the unlocked + required scope sets for a client from the provider-profile registry and
23
23
  * the profile keys assigned to that client.
24
24
  *
25
+ * A client with NO assigned profiles resolves to the registry's default profiles (those marked
26
+ * `isDefault`), so a registry declaring no default resolves exactly as it would from the assigned
27
+ * keys alone.
28
+ *
29
+ * This is the single choke point for a client's profile resolution — the consent unlock gate, the
30
+ * consent required gate, and the `requiredScopes` interaction param all read through it.
31
+ *
25
32
  * @param providerProfiles - The provider-profile registry (from {@link OidcProviderConfig.providerProfiles}).
26
33
  * @param clientProfileKeys - The profile keys assigned to the client (its `dbx_provider_profiles`).
27
34
  * @returns The unlocked and required scope sets for the client.
@@ -33,7 +33,7 @@ export type OidcAuthenticatedRequest = FirebaseServerAuthenticatedRequest<OidcAu
33
33
  * `request.auth.token.scope` for OIDC callers and is `undefined` for non-OIDC
34
34
  * (regular Firebase ID-token) callers.
35
35
  *
36
- * Returning `undefined` for non-OIDC callers lets callers (e.g. `oidcCallModelScopePreAssert`)
36
+ * Returning `undefined` for non-OIDC callers lets consumers (e.g. the MCP tool-visibility filter)
37
37
  * skip scope enforcement instead of falsely treating the missing claim as "no scopes granted".
38
38
  *
39
39
  * The parameter is intentionally typed as `unknown` because `DecodedIdToken` (Firebase
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dereekb/firebase-server",
3
- "version": "13.29.0",
3
+ "version": "13.31.0",
4
4
  "sideEffects": false,
5
5
  "exports": {
6
6
  "./test": {
@@ -57,15 +57,15 @@
57
57
  "types": "./src/index.d.ts",
58
58
  "peerDependencies": {
59
59
  "@cantoo/pdf-lib": "^2.6.5",
60
- "@dereekb/analytics": "13.29.0",
61
- "@dereekb/date": "13.29.0",
62
- "@dereekb/dbx-core": "13.29.0",
63
- "@dereekb/firebase": "13.29.0",
64
- "@dereekb/model": "13.29.0",
65
- "@dereekb/nestjs": "13.29.0",
66
- "@dereekb/rxjs": "13.29.0",
67
- "@dereekb/util": "13.29.0",
68
- "@dereekb/zoho": "13.29.0",
60
+ "@dereekb/analytics": "13.31.0",
61
+ "@dereekb/date": "13.31.0",
62
+ "@dereekb/dbx-core": "13.31.0",
63
+ "@dereekb/firebase": "13.31.0",
64
+ "@dereekb/model": "13.31.0",
65
+ "@dereekb/nestjs": "13.31.0",
66
+ "@dereekb/rxjs": "13.31.0",
67
+ "@dereekb/util": "13.31.0",
68
+ "@dereekb/zoho": "13.31.0",
69
69
  "@google-cloud/firestore": "^7.11.6",
70
70
  "@google-cloud/storage": "^7.19.0",
71
71
  "@modelcontextprotocol/sdk": "1.29.0",
@@ -1,3 +1,4 @@
1
+ export * from './model.api.scope';
1
2
  export * from './model.api.get.service';
2
3
  export * from './model.api.dispatch';
3
4
  export * from './model.api.controller';
@@ -1,11 +1,12 @@
1
1
  import { type Maybe } from '@dereekb/util';
2
- import { type OnCallTypedModelParams } from '@dereekb/firebase';
2
+ import { type FirestoreModelType, type OidcModelScopeRequirement, type OidcScopeTerm, type OnCallTypedModelParams } from '@dereekb/firebase';
3
3
  import { type INestApplicationContext } from '@nestjs/common';
4
4
  import { type Request } from 'express';
5
5
  import { type OnCallWithNestContext } from '../../function/call';
6
6
  import { type OnCallApiDetailsRef, type ModelApiDetailsResult } from '../../model/api.details';
7
7
  import { type MakeNestContext } from '../../nest.provider';
8
8
  import { type FirebaseServerAuthData } from '../auth.context.server';
9
+ import { type ModelApiOidcScopeConfig } from './model.api.scope';
9
10
  /**
10
11
  * The combined type of the function returned by onCallModel() with _apiDetails attached.
11
12
  */
@@ -27,7 +28,7 @@ export type OnCallModelFnWithApiDetails = OnCallWithNestContext<unknown, OnCallT
27
28
  * }
28
29
  * ```
29
30
  */
30
- export declare abstract class ModelApiDispatchConfig {
31
+ export declare abstract class ModelApiDispatchConfig implements ModelApiOidcScopeConfig {
31
32
  /**
32
33
  * The onCallModel() return value with _apiDetails attached.
33
34
  */
@@ -36,6 +37,17 @@ export declare abstract class ModelApiDispatchConfig {
36
37
  * Factory to create typed nest context from INestApplicationContext.
37
38
  */
38
39
  readonly makeNestContext: MakeNestContext<unknown>;
40
+ /**
41
+ * Optional model-api-layer OIDC group-scope default. See {@link ModelApiOidcScopeConfig.defaultRequiredScope}.
42
+ * This is the home for the callModel group-scope default; providing it here enforces it across
43
+ * dispatch AND the `/get` reads.
44
+ */
45
+ readonly defaultRequiredScope?: OidcScopeTerm;
46
+ /**
47
+ * Optional per-model OIDC group-scope overrides. See {@link ModelApiOidcScopeConfig.modelRequiredScopes}.
48
+ * The only place a plain `/get` read (no per-function handler) can be scope-gated beyond `model.read`.
49
+ */
50
+ readonly modelRequiredScopes?: Record<FirestoreModelType, OidcModelScopeRequirement>;
39
51
  }
40
52
  /**
41
53
  * Injection token for providing the NestJS application context to the dispatch service.
@@ -61,8 +61,22 @@ export declare function modelAccessReadErrorFromUseMultipleModelsFailure(entry:
61
61
  */
62
62
  export declare class ModelApiGetService {
63
63
  private readonly _nestContext;
64
+ private readonly _defaultRequiredScope;
65
+ private readonly _modelRequiredScopes;
64
66
  private _identityByModelType;
65
67
  constructor(config: ModelApiDispatchConfig, nestApplication: INestApplicationContext);
68
+ /**
69
+ * Enforces the OIDC read-scope requirement for a direct document read before it hits Firestore.
70
+ *
71
+ * A direct `/get` read is the `read` verb: it requires the per-verb `model.read` scope AND any
72
+ * effective group term for the target model (per-model requirement > module default). This is the
73
+ * ONLY gate on the direct-read path — it does not touch the callModel dispatch chain — so without it
74
+ * an OIDC client scoped to a subset could read any model through `/get`. Non-OIDC callers bypass.
75
+ *
76
+ * @param modelType - The Firestore model type being read.
77
+ * @param auth - The request's auth data (OIDC scopes are read from it).
78
+ */
79
+ private _assertReadScope;
66
80
  /**
67
81
  * Returns the registered {@link FirestoreModelIdentity} for the given `modelType` string, or
68
82
  * `undefined` when no model of that type is registered.
@@ -0,0 +1,102 @@
1
+ import { type Maybe } from '@dereekb/util';
2
+ import { type FirestoreModelType, type OidcModelScopeRequirement, type OidcScope, type OidcScopeTerm, type OnCallFunctionType } from '@dereekb/firebase';
3
+ import { type FirebaseServerAuthData } from '../auth.context.server';
4
+ /**
5
+ * OIDC scope-grouping configuration for the model-api layer, mixed into {@link ModelApiDispatchConfig}
6
+ * and read by both {@link ModelApiCallModelDispatchService} and {@link ModelApiGetService}.
7
+ *
8
+ * This is the model-api-layer home for the callModel group-scope requirements. Providing them here
9
+ * enforces them uniformly across ALL three `ModelApiController` access patterns — path/direct
10
+ * dispatch AND the `/get` direct reads — and
11
+ * (via the MCP server routing through the same services) the MCP surface, without riding the shared
12
+ * callModel function that also serves the app's first-party Firebase `onCall` path.
13
+ *
14
+ * With NEITHER field supplied, enforcement is byte-for-byte the pre-grouping behavior: an OIDC caller
15
+ * still needs the per-verb `model.<call>` scope (plus any per-function `requiredScope` on dispatch),
16
+ * and non-OIDC callers bypass entirely.
17
+ */
18
+ export interface ModelApiOidcScopeConfig {
19
+ /**
20
+ * Group term required on EVERY model-api op unless a finer term overrides it (a per-function
21
+ * `requiredScope`, then a {@link ModelApiOidcScopeConfig.modelRequiredScopes} entry). Lets an app
22
+ * require e.g. `'hellosubs'` across all models without editing individual handlers. Set this to the
23
+ * SAME value passed to the MCP `McpModuleConfig.defaultRequiredScope` so tool listing == callability.
24
+ */
25
+ readonly defaultRequiredScope?: OidcScopeTerm;
26
+ /**
27
+ * Per-model group-term overrides keyed by {@link FirestoreModelType}. The ONLY place a plain `/get`
28
+ * read (which has no per-function handler) can be scope-gated beyond the per-verb `model.read` scope.
29
+ * A verb-keyed entry can require different terms per verb (e.g. `lms` reads, `hellosubs` writes). Set
30
+ * this to the SAME value passed to the MCP `McpModuleConfig.modelRequiredScopes` so tool listing ==
31
+ * callability.
32
+ */
33
+ readonly modelRequiredScopes?: Record<FirestoreModelType, OidcModelScopeRequirement>;
34
+ }
35
+ /**
36
+ * Reads the set of OIDC scopes carried by a model-api request's auth, or `undefined` for a non-OIDC
37
+ * (regular Firebase ID-token) caller.
38
+ *
39
+ * The OIDC bearer-token middleware attaches the validated access-token claims at
40
+ * `auth.oidcValidatedToken` (with the space-delimited `scope` string); a non-OIDC caller has neither
41
+ * that field nor a `scope` on `auth.token`. Reading is defensive (the auth shape is only typed as
42
+ * {@link FirebaseServerAuthData} here — the OIDC-specific `oidcValidatedToken` lives in the
43
+ * `@dereekb/firebase-server/oidc` sub-package this core layer cannot import), delegating the actual
44
+ * parse to the shared {@link oidcScopesFromScopeClaim} so there is no drift with `getOidcScopesFromRequest`.
45
+ *
46
+ * @param auth - The request auth data, or undefined for unauthenticated requests.
47
+ * @returns The granted scope set, or `undefined` when the request carries no OIDC `scope` claim.
48
+ */
49
+ export declare function oidcScopesFromModelApiAuth(auth: Maybe<FirebaseServerAuthData>): Maybe<Set<OidcScope>>;
50
+ /**
51
+ * Inputs to {@link assertModelApiOidcScope}.
52
+ *
53
+ * Standalone (not extending {@link ModelApiOidcScopeConfig}) because the group fields here receive the
54
+ * services' stored config values, which are {@link Maybe} (nullable) — matching the shipped
55
+ * `ResolveEffectiveOidcScopeTermsInput` — whereas the config surface exposes them as plain optionals.
56
+ */
57
+ export interface AssertModelApiOidcScopeInput {
58
+ /**
59
+ * The call verb being enforced (e.g. `create`, `read`). `/get` direct reads pass `'read'`.
60
+ */
61
+ readonly call: OnCallFunctionType;
62
+ /**
63
+ * The Firestore model type being targeted.
64
+ */
65
+ readonly modelType: FirestoreModelType;
66
+ /**
67
+ * The per-function `requiredScope` declared via `withApiDetails`, if resolvable (dispatch only — a
68
+ * plain `/get` read has no per-function handler). The finest (highest-precedence) group term.
69
+ */
70
+ readonly requiredScope?: Maybe<OidcScopeTerm>;
71
+ /**
72
+ * The module-level default group term (see {@link ModelApiOidcScopeConfig.defaultRequiredScope}).
73
+ */
74
+ readonly defaultRequiredScope?: Maybe<OidcScopeTerm>;
75
+ /**
76
+ * The per-model group-term overrides (see {@link ModelApiOidcScopeConfig.modelRequiredScopes}).
77
+ */
78
+ readonly modelRequiredScopes?: Maybe<Record<FirestoreModelType, OidcModelScopeRequirement>>;
79
+ /**
80
+ * The scopes the caller was granted, or `undefined` for a non-OIDC caller (bypasses enforcement).
81
+ */
82
+ readonly grantedScopes: Maybe<ReadonlySet<OidcScope>>;
83
+ }
84
+ /**
85
+ * Enforces the OIDC scope requirement for a single model-api op, throwing a `403`
86
+ * {@link CALL_MODEL_MISSING_OIDC_SCOPE_ERROR_CODE} error when the caller does not satisfy it.
87
+ *
88
+ * The single home of callModel OIDC scope enforcement. It reuses the shipped composition + evaluation
89
+ * (`resolveEffectiveOidcScopeTerms` / `oidcScopeTermsSatisfied`) so enforcement and the MCP
90
+ * tool-visibility filter never drift. Enforcement is AND-of-ORs across the per-verb `model.<call>`
91
+ * scope and the effective GROUP term (per-function `requiredScope` > per-model requirement >
92
+ * configured default).
93
+ *
94
+ * Bypasses (no-op) when `grantedScopes` is `undefined` — i.e. a non-OIDC caller — and short-circuits
95
+ * without any check when the op resolves no requirement at all (a custom, non-CRUD verb with no
96
+ * per-function/model/default term).
97
+ *
98
+ * @param input - The verb, model type, per-function scope, group config, and the caller's granted scopes.
99
+ * @throws A `403` forbidden error (code {@link CALL_MODEL_MISSING_OIDC_SCOPE_ERROR_CODE}) when an OIDC
100
+ * caller does not satisfy the effective requirement.
101
+ */
102
+ export declare function assertModelApiOidcScope(input: AssertModelApiOidcScopeInput): void;
@@ -1,5 +1,5 @@
1
1
  import { type AuthRole, type Maybe, type PromiseOrValue } from '@dereekb/util';
2
- import { type OnCallFunctionType, type OnCallTypedModelParams, type FirestoreModelType, type ModelFirebaseCrudFunctionSpecifier, type OidcScope } from '@dereekb/firebase';
2
+ import { type OnCallFunctionType, type OnCallTypedModelParams, type FirestoreModelType, type ModelFirebaseCrudFunctionSpecifier, type OidcScopeTerm } from '@dereekb/firebase';
3
3
  import { type FirebaseServerAuthData } from '../controller/auth.context.server';
4
4
  import { type OnCallModelFunctionAnalyticsDetails } from './analytics.details';
5
5
  /**
@@ -74,7 +74,7 @@ export interface McpVisibilityContext {
74
74
  readonly auth?: FirebaseServerAuthData;
75
75
  /**
76
76
  * The caller's OIDC scopes, when the request was authenticated with a `scope`-bearing token.
77
- * `undefined` for non-OIDC callers — treated the same as `oidcCallModelScopePreAssert` bypass.
77
+ * `undefined` for non-OIDC callers — treated the same as the model-api scope gate's non-OIDC bypass.
78
78
  */
79
79
  readonly scopes?: ReadonlySet<string>;
80
80
  /**
@@ -303,15 +303,20 @@ export interface OnCallModelFunctionApiDetails<R = unknown, M = unknown> {
303
303
  */
304
304
  readonly analytics?: OnCallModelFunctionAnalyticsDetails;
305
305
  /**
306
- * Optional per-function OIDC scope an OIDC-authenticated caller must additionally hold to
306
+ * Optional per-function OIDC scope TERM an OIDC-authenticated caller must additionally hold to
307
307
  * invoke this handler, on top of the per-verb `model.<call>` scope.
308
308
  *
309
- * Composes additively with the CRUD-verb scope enforced by `oidcCallModelScopePreAssert`: an
310
- * OIDC caller still needs `model.<call>` AND this scope. Non-OIDC callers (Firebase ID tokens)
309
+ * A single scope string requires that exact scope; a `readonly` array is an OR-group the caller
310
+ * satisfies by holding ANY member (see {@link OidcScopeTerm}). This term is the finest (highest
311
+ * precedence) source for the effective group requirement — it overrides any model-level or default
312
+ * requirement configured on the model-api layer (`ModelApiDispatchConfig`).
313
+ *
314
+ * Composes with the CRUD-verb scope enforced by the model-api scope gate (`assertModelApiOidcScope`) as AND-of-ORs: an
315
+ * OIDC caller still needs `model.<call>` AND this term. Non-OIDC callers (Firebase ID tokens)
311
316
  * bypass scope enforcement entirely and are unaffected. Also folded into the MCP tool-list
312
- * visibility filter, so a tool the caller lacks this scope for is neither listed nor callable.
317
+ * visibility filter, so a tool the caller cannot satisfy this term for is neither listed nor callable.
313
318
  */
314
- readonly requiredScope?: OidcScope;
319
+ readonly requiredScope?: OidcScopeTerm;
315
320
  }
316
321
  /**
317
322
  * Ref interface for objects (functions) that carry handler-level _apiDetails.
@@ -587,7 +592,7 @@ export declare function resolveAnalyticsFromApiDetails(apiDetails: OnCallModelAp
587
592
  * @param specifier - Optional specifier key for variant handlers.
588
593
  * @returns The per-function required scope for the resolved handler, or undefined.
589
594
  */
590
- export declare function resolveRequiredScopeFromApiDetails(apiDetails: OnCallModelApiDetails, call: OnCallFunctionType, modelType: FirestoreModelType, specifier?: ModelFirebaseCrudFunctionSpecifier): Maybe<OidcScope>;
595
+ export declare function resolveRequiredScopeFromApiDetails(apiDetails: OnCallModelApiDetails, call: OnCallFunctionType, modelType: FirestoreModelType, specifier?: ModelFirebaseCrudFunctionSpecifier): Maybe<OidcScopeTerm>;
591
596
  /**
592
597
  * @deprecated Use {@link OnCallModelTypeApiDetails} instead.
593
598
  */
@@ -1,6 +1,6 @@
1
1
  import { type Maybe } from '@dereekb/util';
2
2
  import { type NestContextCallableRequest } from '../function/nest';
3
- import { type OnCallFunctionType, type FirestoreModelType, type OidcScope } from '@dereekb/firebase';
3
+ import { type OnCallFunctionType, type FirestoreModelType, type OidcScopeTerm } from '@dereekb/firebase';
4
4
  /**
5
5
  * Discriminator for the category of CRUD operation being asserted.
6
6
  *
@@ -35,13 +35,14 @@ export interface AssertModelCrudRequestFunctionContext<N, I = unknown> {
35
35
  */
36
36
  readonly specifier: Maybe<string>;
37
37
  /**
38
- * The per-function required OIDC scope declared via `withApiDetails({ requiredScope })`, when present.
38
+ * The per-function required OIDC scope TERM declared via `withApiDetails({ requiredScope })`, when present.
39
39
  *
40
- * Resolved from the handler's `_apiDetails` at dispatch and handed to the pre-assert so
41
- * `oidcCallModelScopePreAssert` can require it additively alongside the per-verb `model.<call>`
42
- * scope. `undefined` when the handler declares no extra scope.
40
+ * A single scope string, or a `readonly` array as an OR-group (see {@link OidcScopeTerm}). Resolved
41
+ * from the handler's `_apiDetails` at dispatch so the model-api scope gate (`assertModelApiOidcScope`)
42
+ * can enforce it as the finest group term (AND-ed with the per-verb `model.<call>` scope).
43
+ * `undefined` when the handler declares no extra scope.
43
44
  */
44
- readonly requiredScope?: Maybe<OidcScope>;
45
+ readonly requiredScope?: Maybe<OidcScopeTerm>;
45
46
  }
46
47
  /**
47
48
  * Pre-assertion hook invoked before a model CRUD handler executes.