@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/index.cjs.js +129 -1
- package/index.esm.js +129 -3
- package/mailgun/package.json +9 -9
- package/mcp/index.cjs.js +42 -17
- package/mcp/index.esm.js +43 -18
- package/mcp/package.json +11 -11
- package/mcp/src/lib/mcp.config.d.ts +19 -1
- package/mcp/src/lib/service/mcp.server.factory.d.ts +1 -1
- package/mcp/src/lib/service/mcp.tool-generator.d.ts +12 -1
- package/mcp/src/lib/service/mcp.visibility.d.ts +9 -6
- package/model/package.json +9 -9
- package/oidc/index.cjs.js +30 -65
- package/oidc/index.esm.js +34 -68
- package/oidc/package.json +10 -10
- package/oidc/src/lib/index.d.ts +0 -1
- package/oidc/src/lib/oidc.config.d.ts +11 -0
- package/oidc/src/lib/profile.d.ts +7 -0
- package/oidc/src/lib/service/oidc.auth.d.ts +1 -1
- package/package.json +10 -10
- package/src/lib/nest/controller/model/index.d.ts +1 -0
- package/src/lib/nest/controller/model/model.api.dispatch.d.ts +14 -2
- package/src/lib/nest/controller/model/model.api.get.service.d.ts +14 -0
- package/src/lib/nest/controller/model/model.api.scope.d.ts +102 -0
- package/src/lib/nest/model/api.details.d.ts +13 -8
- package/src/lib/nest/model/crud.assert.function.d.ts +7 -6
- package/test/index.cjs.js +94 -58
- package/test/index.esm.js +95 -59
- package/test/package.json +11 -11
- package/test/src/lib/oidc/oidc.test.fixture.d.ts +9 -1
- package/test/src/lib/oidc/oidc.test.flow.d.ts +13 -3
- package/twilio/package.json +8 -8
- package/zoho/package.json +9 -9
- package/oidc/src/lib/scope.d.ts +0 -24
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
|
|
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,
|
|
7
|
-
import { firestoreEncryptedField, firebaseServerActionsContext, FirebaseServerEnvService, FirebaseServerAnalyticsService, FIREBASE_FIRESTORE_CONTEXT_TOKEN, FirebaseServerFirestoreContextModule
|
|
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 =
|
|
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
|
|
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
|
-
|
|
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,
|
|
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`
|
|
6301
|
-
//
|
|
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
|
-
|
|
6304
|
-
|
|
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 (!(
|
|
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:
|
|
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:
|
|
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
|
|
6360
|
-
// scope may be
|
|
6361
|
-
|
|
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.
|
|
3
|
+
"version": "13.31.0",
|
|
4
4
|
"peerDependencies": {
|
|
5
|
-
"@dereekb/analytics": "13.
|
|
6
|
-
"@dereekb/date": "13.
|
|
7
|
-
"@dereekb/firebase": "13.
|
|
8
|
-
"@dereekb/firebase-server": "13.
|
|
9
|
-
"@dereekb/model": "13.
|
|
10
|
-
"@dereekb/nestjs": "13.
|
|
11
|
-
"@dereekb/rxjs": "13.
|
|
12
|
-
"@dereekb/util": "13.
|
|
13
|
-
"@dereekb/zoho": "13.
|
|
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",
|
package/oidc/src/lib/index.d.ts
CHANGED
|
@@ -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
|
|
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.
|
|
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.
|
|
61
|
-
"@dereekb/date": "13.
|
|
62
|
-
"@dereekb/dbx-core": "13.
|
|
63
|
-
"@dereekb/firebase": "13.
|
|
64
|
-
"@dereekb/model": "13.
|
|
65
|
-
"@dereekb/nestjs": "13.
|
|
66
|
-
"@dereekb/rxjs": "13.
|
|
67
|
-
"@dereekb/util": "13.
|
|
68
|
-
"@dereekb/zoho": "13.
|
|
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,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
|
|
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
|
|
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
|
-
*
|
|
310
|
-
*
|
|
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
|
|
317
|
+
* visibility filter, so a tool the caller cannot satisfy this term for is neither listed nor callable.
|
|
313
318
|
*/
|
|
314
|
-
readonly requiredScope?:
|
|
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<
|
|
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
|
|
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
|
-
*
|
|
41
|
-
* `
|
|
42
|
-
*
|
|
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<
|
|
45
|
+
readonly requiredScope?: Maybe<OidcScopeTerm>;
|
|
45
46
|
}
|
|
46
47
|
/**
|
|
47
48
|
* Pre-assertion hook invoked before a model CRUD handler executes.
|