@dereekb/firebase-server 13.29.0 → 13.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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 +2 -53
- package/oidc/index.esm.js +6 -56
- package/oidc/package.json +10 -10
- package/oidc/src/lib/index.d.ts +0 -1
- 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/package.json +11 -11
- package/twilio/package.json +8 -8
- package/zoho/package.json +9 -9
- package/oidc/src/lib/scope.d.ts +0 -24
package/index.cjs.js
CHANGED
|
@@ -7059,6 +7059,78 @@ exports.FirebaseServerAuthModule = __decorate([
|
|
|
7059
7059
|
return claims[firebase.FIREBASE_SERVER_AUTH_CLAIMS_SETUP_PASSWORD_KEY] != null;
|
|
7060
7060
|
}
|
|
7061
7061
|
|
|
7062
|
+
// MARK: Scope Reading
|
|
7063
|
+
/**
|
|
7064
|
+
* Reads the set of OIDC scopes carried by a model-api request's auth, or `undefined` for a non-OIDC
|
|
7065
|
+
* (regular Firebase ID-token) caller.
|
|
7066
|
+
*
|
|
7067
|
+
* The OIDC bearer-token middleware attaches the validated access-token claims at
|
|
7068
|
+
* `auth.oidcValidatedToken` (with the space-delimited `scope` string); a non-OIDC caller has neither
|
|
7069
|
+
* that field nor a `scope` on `auth.token`. Reading is defensive (the auth shape is only typed as
|
|
7070
|
+
* {@link FirebaseServerAuthData} here — the OIDC-specific `oidcValidatedToken` lives in the
|
|
7071
|
+
* `@dereekb/firebase-server/oidc` sub-package this core layer cannot import), delegating the actual
|
|
7072
|
+
* parse to the shared {@link oidcScopesFromScopeClaim} so there is no drift with `getOidcScopesFromRequest`.
|
|
7073
|
+
*
|
|
7074
|
+
* @param auth - The request auth data, or undefined for unauthenticated requests.
|
|
7075
|
+
* @returns The granted scope set, or `undefined` when the request carries no OIDC `scope` claim.
|
|
7076
|
+
*/ function oidcScopesFromModelApiAuth(auth) {
|
|
7077
|
+
var _auth_oidcValidatedToken, _auth_token;
|
|
7078
|
+
var oidcScope = auth === null || auth === void 0 ? void 0 : (_auth_oidcValidatedToken = auth.oidcValidatedToken) === null || _auth_oidcValidatedToken === void 0 ? void 0 : _auth_oidcValidatedToken.scope;
|
|
7079
|
+
var tokenScope = auth === null || auth === void 0 ? void 0 : (_auth_token = auth.token) === null || _auth_token === void 0 ? void 0 : _auth_token.scope;
|
|
7080
|
+
var scope = oidcScope !== null && oidcScope !== void 0 ? oidcScope : tokenScope;
|
|
7081
|
+
return firebase.oidcScopesFromScopeClaim(scope);
|
|
7082
|
+
}
|
|
7083
|
+
/**
|
|
7084
|
+
* Enforces the OIDC scope requirement for a single model-api op, throwing a `403`
|
|
7085
|
+
* {@link CALL_MODEL_MISSING_OIDC_SCOPE_ERROR_CODE} error when the caller does not satisfy it.
|
|
7086
|
+
*
|
|
7087
|
+
* The single home of callModel OIDC scope enforcement. It reuses the shipped composition + evaluation
|
|
7088
|
+
* (`resolveEffectiveOidcScopeTerms` / `oidcScopeTermsSatisfied`) so enforcement and the MCP
|
|
7089
|
+
* tool-visibility filter never drift. Enforcement is AND-of-ORs across the per-verb `model.<call>`
|
|
7090
|
+
* scope and the effective GROUP term (per-function `requiredScope` > per-model requirement >
|
|
7091
|
+
* configured default).
|
|
7092
|
+
*
|
|
7093
|
+
* Bypasses (no-op) when `grantedScopes` is `undefined` — i.e. a non-OIDC caller — and short-circuits
|
|
7094
|
+
* without any check when the op resolves no requirement at all (a custom, non-CRUD verb with no
|
|
7095
|
+
* per-function/model/default term).
|
|
7096
|
+
*
|
|
7097
|
+
* @param input - The verb, model type, per-function scope, group config, and the caller's granted scopes.
|
|
7098
|
+
* @throws A `403` forbidden error (code {@link CALL_MODEL_MISSING_OIDC_SCOPE_ERROR_CODE}) when an OIDC
|
|
7099
|
+
* caller does not satisfy the effective requirement.
|
|
7100
|
+
*/ function assertModelApiOidcScope(input) {
|
|
7101
|
+
var call = input.call, modelType = input.modelType, requiredScope = input.requiredScope, defaultRequiredScope = input.defaultRequiredScope, modelRequiredScopes = input.modelRequiredScopes, grantedScopes = input.grantedScopes;
|
|
7102
|
+
var terms = firebase.resolveEffectiveOidcScopeTerms({
|
|
7103
|
+
perVerbScope: firebase.callModelOidcScopeForCallType(call),
|
|
7104
|
+
requiredScope: requiredScope,
|
|
7105
|
+
modelRequirement: modelRequiredScopes === null || modelRequiredScopes === void 0 ? void 0 : modelRequiredScopes[modelType],
|
|
7106
|
+
call: call,
|
|
7107
|
+
defaultRequiredScope: defaultRequiredScope
|
|
7108
|
+
});
|
|
7109
|
+
if (terms.length > 0 && grantedScopes != null && !firebase.oidcScopeTermsSatisfied(terms, grantedScopes)) {
|
|
7110
|
+
var missingTerms = terms.filter(function(term) {
|
|
7111
|
+
return !firebase.oidcScopeTermSatisfied(term, grantedScopes);
|
|
7112
|
+
});
|
|
7113
|
+
throw forbiddenError({
|
|
7114
|
+
status: 403,
|
|
7115
|
+
code: firebase.CALL_MODEL_MISSING_OIDC_SCOPE_ERROR_CODE,
|
|
7116
|
+
message: "Missing required OIDC scope for callModel: ".concat(missingTerms.map(formatModelApiScopeTerm).join(', ')),
|
|
7117
|
+
data: {
|
|
7118
|
+
requiredScopes: missingTerms,
|
|
7119
|
+
call: call
|
|
7120
|
+
}
|
|
7121
|
+
});
|
|
7122
|
+
}
|
|
7123
|
+
}
|
|
7124
|
+
/**
|
|
7125
|
+
* Renders a scope term for the human-readable error message: a single scope as-is, an OR-group as its
|
|
7126
|
+
* alternatives joined by `|` (so a single-scope term reads as itself).
|
|
7127
|
+
*
|
|
7128
|
+
* @param term - The unsatisfied scope term.
|
|
7129
|
+
* @returns The display string for the term.
|
|
7130
|
+
*/ function formatModelApiScopeTerm(term) {
|
|
7131
|
+
return typeof term === 'string' ? term : term.join('|');
|
|
7132
|
+
}
|
|
7133
|
+
|
|
7062
7134
|
function _define_property$n(obj, key, value) {
|
|
7063
7135
|
if (key in obj) {
|
|
7064
7136
|
Object.defineProperty(obj, key, {
|
|
@@ -7335,6 +7407,15 @@ function _ts_generator$a(thisArg, body) {
|
|
|
7335
7407
|
/**
|
|
7336
7408
|
* Factory to create typed nest context from INestApplicationContext.
|
|
7337
7409
|
*/ _define_property$m(this, "makeNestContext", void 0);
|
|
7410
|
+
/**
|
|
7411
|
+
* Optional model-api-layer OIDC group-scope default. See {@link ModelApiOidcScopeConfig.defaultRequiredScope}.
|
|
7412
|
+
* This is the home for the callModel group-scope default; providing it here enforces it across
|
|
7413
|
+
* dispatch AND the `/get` reads.
|
|
7414
|
+
*/ _define_property$m(this, "defaultRequiredScope", void 0);
|
|
7415
|
+
/**
|
|
7416
|
+
* Optional per-model OIDC group-scope overrides. See {@link ModelApiOidcScopeConfig.modelRequiredScopes}.
|
|
7417
|
+
* The only place a plain `/get` read (no per-function handler) can be scope-gated beyond `model.read`.
|
|
7418
|
+
*/ _define_property$m(this, "modelRequiredScopes", void 0);
|
|
7338
7419
|
};
|
|
7339
7420
|
/**
|
|
7340
7421
|
* Injection token for providing the NestJS application context to the dispatch service.
|
|
@@ -7369,8 +7450,25 @@ function _ts_generator$a(thisArg, body) {
|
|
|
7369
7450
|
* @returns The handler's return value.
|
|
7370
7451
|
*/ function dispatch(params, auth, rawRequest) {
|
|
7371
7452
|
return _async_to_generator$a(function() {
|
|
7372
|
-
var callableRequest, appRequest, contextRequest;
|
|
7453
|
+
var call, modelType, specifier, apiDetails, requiredScope, callableRequest, appRequest, contextRequest;
|
|
7373
7454
|
return _ts_generator$a(this, function(_state) {
|
|
7455
|
+
// Enforce OIDC scope BEFORE dispatching — the relocated home of the callModel scope check.
|
|
7456
|
+
// AND-of-ORs across the per-verb `model.<call>` scope and the effective group term (per-function
|
|
7457
|
+
// `requiredScope` > per-model requirement > module default), bypassing non-OIDC callers. A nullish
|
|
7458
|
+
// `call` is a malformed request the callModel chain rejects downstream (no model op to authorize).
|
|
7459
|
+
call = params.call, modelType = params.modelType, specifier = params.specifier;
|
|
7460
|
+
if (call != null) {
|
|
7461
|
+
apiDetails = this.config.callModelFn._apiDetails;
|
|
7462
|
+
requiredScope = apiDetails == null ? undefined : resolveRequiredScopeFromApiDetails(apiDetails, call, modelType, specifier);
|
|
7463
|
+
assertModelApiOidcScope({
|
|
7464
|
+
call: call,
|
|
7465
|
+
modelType: modelType,
|
|
7466
|
+
requiredScope: requiredScope,
|
|
7467
|
+
defaultRequiredScope: this.config.defaultRequiredScope,
|
|
7468
|
+
modelRequiredScopes: this.config.modelRequiredScopes,
|
|
7469
|
+
grantedScopes: oidcScopesFromModelApiAuth(auth)
|
|
7470
|
+
});
|
|
7471
|
+
}
|
|
7374
7472
|
// Build a synthetic CallableRequest that the dispatch chain expects. Layer the
|
|
7375
7473
|
// OIDC-validated claim subset over the base token so standard JWT claims
|
|
7376
7474
|
// (`iat`, `auth_time`, `email`, …) survive — the callModel chain and any
|
|
@@ -7657,12 +7755,38 @@ function _ts_generator$9(thisArg, body) {
|
|
|
7657
7755
|
function ModelApiGetService(config, nestApplication) {
|
|
7658
7756
|
_class_call_check$e(this, ModelApiGetService);
|
|
7659
7757
|
_define_property$l(this, "_nestContext", void 0);
|
|
7758
|
+
_define_property$l(this, "_defaultRequiredScope", void 0);
|
|
7759
|
+
_define_property$l(this, "_modelRequiredScopes", void 0);
|
|
7660
7760
|
_define_property$l(this, "_identityByModelType", void 0);
|
|
7661
7761
|
this._nestContext = config.makeNestContext(nestApplication);
|
|
7762
|
+
this._defaultRequiredScope = config.defaultRequiredScope;
|
|
7763
|
+
this._modelRequiredScopes = config.modelRequiredScopes;
|
|
7662
7764
|
}
|
|
7663
7765
|
_create_class$9(ModelApiGetService, [
|
|
7664
7766
|
{
|
|
7665
7767
|
/**
|
|
7768
|
+
* Enforces the OIDC read-scope requirement for a direct document read before it hits Firestore.
|
|
7769
|
+
*
|
|
7770
|
+
* A direct `/get` read is the `read` verb: it requires the per-verb `model.read` scope AND any
|
|
7771
|
+
* effective group term for the target model (per-model requirement > module default). This is the
|
|
7772
|
+
* ONLY gate on the direct-read path — it does not touch the callModel dispatch chain — so without it
|
|
7773
|
+
* an OIDC client scoped to a subset could read any model through `/get`. Non-OIDC callers bypass.
|
|
7774
|
+
*
|
|
7775
|
+
* @param modelType - The Firestore model type being read.
|
|
7776
|
+
* @param auth - The request's auth data (OIDC scopes are read from it).
|
|
7777
|
+
*/ key: "_assertReadScope",
|
|
7778
|
+
value: function _assertReadScope(modelType, auth) {
|
|
7779
|
+
assertModelApiOidcScope({
|
|
7780
|
+
call: 'read',
|
|
7781
|
+
modelType: modelType,
|
|
7782
|
+
defaultRequiredScope: this._defaultRequiredScope,
|
|
7783
|
+
modelRequiredScopes: this._modelRequiredScopes,
|
|
7784
|
+
grantedScopes: oidcScopesFromModelApiAuth(auth)
|
|
7785
|
+
});
|
|
7786
|
+
}
|
|
7787
|
+
},
|
|
7788
|
+
{
|
|
7789
|
+
/**
|
|
7666
7790
|
* Returns the registered {@link FirestoreModelIdentity} for the given `modelType` string, or
|
|
7667
7791
|
* `undefined` when no model of that type is registered.
|
|
7668
7792
|
*
|
|
@@ -7734,6 +7858,7 @@ function _ts_generator$9(thisArg, body) {
|
|
|
7734
7858
|
return _ts_generator$9(this, function(_state) {
|
|
7735
7859
|
switch(_state.label){
|
|
7736
7860
|
case 0:
|
|
7861
|
+
this._assertReadScope(modelType, auth);
|
|
7737
7862
|
authRef = this._makeAuthRef(auth);
|
|
7738
7863
|
return [
|
|
7739
7864
|
4,
|
|
@@ -7782,6 +7907,7 @@ function _ts_generator$9(thisArg, body) {
|
|
|
7782
7907
|
return _async_to_generator$9(function() {
|
|
7783
7908
|
var authRef;
|
|
7784
7909
|
return _ts_generator$9(this, function(_state) {
|
|
7910
|
+
this._assertReadScope(modelType, auth);
|
|
7785
7911
|
authRef = this._makeAuthRef(auth);
|
|
7786
7912
|
return [
|
|
7787
7913
|
2,
|
|
@@ -13185,6 +13311,7 @@ exports.assertHasSignedTosInRequest = assertHasSignedTosInRequest;
|
|
|
13185
13311
|
exports.assertIsAdminInRequest = assertIsAdminInRequest;
|
|
13186
13312
|
exports.assertIsAdminOrTargetUserInRequestData = assertIsAdminOrTargetUserInRequestData;
|
|
13187
13313
|
exports.assertIsContextWithAuthData = assertIsContextWithAuthData;
|
|
13314
|
+
exports.assertModelApiOidcScope = assertModelApiOidcScope;
|
|
13188
13315
|
exports.assertRequestRequiresAuthForFunction = assertRequestRequiresAuthForFunction;
|
|
13189
13316
|
exports.assertSnapshotData = assertSnapshotData;
|
|
13190
13317
|
exports.assertSnapshotDataWithKey = assertSnapshotDataWithKey;
|
|
@@ -13282,6 +13409,7 @@ exports.noRunNameSpecifiedForScheduledFunctionDevelopmentFunction = noRunNameSpe
|
|
|
13282
13409
|
exports.noopFirebaseServerAnalyticsServiceListener = noopFirebaseServerAnalyticsServiceListener;
|
|
13283
13410
|
exports.noopOnCallModelAnalyticsService = noopOnCallModelAnalyticsService;
|
|
13284
13411
|
exports.notFoundError = notFoundError;
|
|
13412
|
+
exports.oidcScopesFromModelApiAuth = oidcScopesFromModelApiAuth;
|
|
13285
13413
|
exports.onCallAnalyticsEmitterInstance = onCallAnalyticsEmitterInstance;
|
|
13286
13414
|
exports.onCallCreateModel = onCallCreateModel;
|
|
13287
13415
|
exports.onCallDeleteModel = onCallDeleteModel;
|
package/index.esm.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { timingSafeEqual } from 'node:crypto';
|
|
2
|
-
import { DBX_FIREBASE_SERVER_NO_AUTH_ERROR_CODE, UNAUTHENTICATED_ERROR_CODE, FORBIDDEN_ERROR_CODE, PERMISSION_DENIED_ERROR_CODE, NOT_FOUND_ERROR_CODE, MODEL_NOT_AVAILABLE_ERROR_CODE, BAD_REQUEST_ERROR_CODE, CONFLICT_ERROR_CODE, ALREADY_EXISTS_ERROR_CODE, UNAVAILABLE_ERROR_CODE, UNAVAILABLE_OR_DEACTIVATED_FUNCTION_ERROR_CODE, INTERNAL_SERVER_ERROR_CODE, FIREBASE_AUTH_USER_NOT_FOUND_ERROR, FIREBASE_SERVER_AUTH_CLAIMS_RESET_PASSWORD_KEY, FIREBASE_SERVER_AUTH_CLAIMS_RESET_LAST_COM_DATE_KEY, FIREBASE_SERVER_AUTH_CLAIMS_RESET_EXPIRES_AT_KEY, FIREBASE_AUTH_PHONE_NUMBER_ALREADY_EXISTS_ERROR, FIREBASE_AUTH_EMAIL_ALREADY_EXISTS_ERROR, FIREBASE_AUTH_INVALID_PHONE_NUMBER_ERROR, FIREBASE_SERVER_AUTH_CLAIMS_SETUP_PASSWORD_KEY, FIREBASE_SERVER_AUTH_CLAIMS_SETUP_LAST_COM_DATE_KEY, setIdAndKeyFromKeyIdRefOnDocumentData, DBX_FIREBASE_SERVER_PASSWORD_RESET_INVALID_CODE_ERROR_CODE, DBX_FIREBASE_SERVER_PASSWORD_RESET_NO_CONFIG_ERROR_CODE, DBX_FIREBASE_SERVER_PASSWORD_RESET_SEND_ONCE_ERROR_CODE, DBX_FIREBASE_SERVER_PASSWORD_RESET_THROTTLE_ERROR_CODE, FirestoreDocumentContextType, streamFromOnSnapshot, FIRESTORE_LIMIT_QUERY_CONSTRAINT_TYPE, FIRESTORE_LIMIT_TO_LAST_QUERY_CONSTRAINT_TYPE, FIRESTORE_ORDER_BY_QUERY_CONSTRAINT_TYPE, FIRESTORE_ORDER_BY_DOCUMENT_ID_QUERY_CONSTRAINT_TYPE, FIRESTORE_WHERE_QUERY_CONSTRAINT_TYPE, FIRESTORE_WHERE_DOCUMENT_ID_QUERY_CONSTRAINT_TYPE, FIRESTORE_OFFSET_QUERY_CONSTRAINT_TYPE, FIRESTORE_START_AT_QUERY_CONSTRAINT_TYPE, FIRESTORE_START_AT_VALUE_QUERY_CONSTRAINT_TYPE, FIRESTORE_START_AFTER_QUERY_CONSTRAINT_TYPE, FIRESTORE_END_AT_QUERY_CONSTRAINT_TYPE, FIRESTORE_END_AT_VALUE_QUERY_CONSTRAINT_TYPE, FIRESTORE_END_BEFORE_QUERY_CONSTRAINT_TYPE, makeFirestoreQueryConstraintFunctionsDriver, firestoreContextFactory, firestoreField, optionalFirestoreField, DEFAULT_MODEL_FUNCTION_FIREBASE_CRUD_FUNCTION_SPECIFIER, UNKNOWN_MODEL_TYPE_ERROR_CODE, BAD_DOCUMENT_QUERY_CURSOR_ERROR_CODE, limit, startAfter, DEFAULT_ON_CALL_QUERY_MODEL_LIMIT, MAX_ON_CALL_QUERY_MODEL_LIMIT, ScheduledFunctionDevelopmentFunctionTypeEnum, SCHEDULED_FUNCTION_DEV_FUNCTION_SPECIFIER, storageListFilesResultFactory, assertStorageUploadOptionsStringFormat, firebaseStorageContextFactory, inContextFirebaseModelsServiceFactory, useFirebaseModelsService } from '@dereekb/firebase';
|
|
2
|
+
import { DBX_FIREBASE_SERVER_NO_AUTH_ERROR_CODE, UNAUTHENTICATED_ERROR_CODE, FORBIDDEN_ERROR_CODE, PERMISSION_DENIED_ERROR_CODE, NOT_FOUND_ERROR_CODE, MODEL_NOT_AVAILABLE_ERROR_CODE, BAD_REQUEST_ERROR_CODE, CONFLICT_ERROR_CODE, ALREADY_EXISTS_ERROR_CODE, UNAVAILABLE_ERROR_CODE, UNAVAILABLE_OR_DEACTIVATED_FUNCTION_ERROR_CODE, INTERNAL_SERVER_ERROR_CODE, FIREBASE_AUTH_USER_NOT_FOUND_ERROR, FIREBASE_SERVER_AUTH_CLAIMS_RESET_PASSWORD_KEY, FIREBASE_SERVER_AUTH_CLAIMS_RESET_LAST_COM_DATE_KEY, FIREBASE_SERVER_AUTH_CLAIMS_RESET_EXPIRES_AT_KEY, FIREBASE_AUTH_PHONE_NUMBER_ALREADY_EXISTS_ERROR, FIREBASE_AUTH_EMAIL_ALREADY_EXISTS_ERROR, FIREBASE_AUTH_INVALID_PHONE_NUMBER_ERROR, FIREBASE_SERVER_AUTH_CLAIMS_SETUP_PASSWORD_KEY, FIREBASE_SERVER_AUTH_CLAIMS_SETUP_LAST_COM_DATE_KEY, setIdAndKeyFromKeyIdRefOnDocumentData, DBX_FIREBASE_SERVER_PASSWORD_RESET_INVALID_CODE_ERROR_CODE, DBX_FIREBASE_SERVER_PASSWORD_RESET_NO_CONFIG_ERROR_CODE, DBX_FIREBASE_SERVER_PASSWORD_RESET_SEND_ONCE_ERROR_CODE, DBX_FIREBASE_SERVER_PASSWORD_RESET_THROTTLE_ERROR_CODE, FirestoreDocumentContextType, streamFromOnSnapshot, FIRESTORE_LIMIT_QUERY_CONSTRAINT_TYPE, FIRESTORE_LIMIT_TO_LAST_QUERY_CONSTRAINT_TYPE, FIRESTORE_ORDER_BY_QUERY_CONSTRAINT_TYPE, FIRESTORE_ORDER_BY_DOCUMENT_ID_QUERY_CONSTRAINT_TYPE, FIRESTORE_WHERE_QUERY_CONSTRAINT_TYPE, FIRESTORE_WHERE_DOCUMENT_ID_QUERY_CONSTRAINT_TYPE, FIRESTORE_OFFSET_QUERY_CONSTRAINT_TYPE, FIRESTORE_START_AT_QUERY_CONSTRAINT_TYPE, FIRESTORE_START_AT_VALUE_QUERY_CONSTRAINT_TYPE, FIRESTORE_START_AFTER_QUERY_CONSTRAINT_TYPE, FIRESTORE_END_AT_QUERY_CONSTRAINT_TYPE, FIRESTORE_END_AT_VALUE_QUERY_CONSTRAINT_TYPE, FIRESTORE_END_BEFORE_QUERY_CONSTRAINT_TYPE, makeFirestoreQueryConstraintFunctionsDriver, firestoreContextFactory, firestoreField, optionalFirestoreField, DEFAULT_MODEL_FUNCTION_FIREBASE_CRUD_FUNCTION_SPECIFIER, UNKNOWN_MODEL_TYPE_ERROR_CODE, BAD_DOCUMENT_QUERY_CURSOR_ERROR_CODE, limit, startAfter, DEFAULT_ON_CALL_QUERY_MODEL_LIMIT, MAX_ON_CALL_QUERY_MODEL_LIMIT, resolveEffectiveOidcScopeTerms, callModelOidcScopeForCallType, oidcScopeTermsSatisfied, oidcScopeTermSatisfied, CALL_MODEL_MISSING_OIDC_SCOPE_ERROR_CODE, oidcScopesFromScopeClaim, ScheduledFunctionDevelopmentFunctionTypeEnum, SCHEDULED_FUNCTION_DEV_FUNCTION_SPECIFIER, storageListFilesResultFactory, assertStorageUploadOptionsStringFormat, firebaseStorageContextFactory, inContextFirebaseModelsServiceFactory, useFirebaseModelsService } from '@dereekb/firebase';
|
|
3
3
|
import { partialServerError, isServerError, randomNumberFactory, cachedGetter, filterNullAndUndefinedValues, asSet, AUTH_ADMIN_ROLE, AUTH_TOS_SIGNED_ROLE, forEachKeyValue, KeyValueTypleValueFilter, filterUndefinedValues, isThrottled, mapObjectMap, batch, objectToMap, serverError, asArray, containsAllValues, websiteUrlDetails, mergeObjects, cronExpressionRepeatingEveryNMinutes, mapIdentityFunction, slashPathName, toRelativeSlashPathStartType, fixMultiSlashesInSlashPath, SLASH_PATH_SEPARATOR, objectHasNoKeys, websiteUrlFromPaths, pushItemOrArrayItemsIntoArray, makeGetter, asGetter, build, performAsyncTasks } from '@dereekb/util';
|
|
4
4
|
import { HttpsError } from 'firebase-functions/https';
|
|
5
5
|
import { hoursToMs, minutesToMs, toISODateString } from '@dereekb/date';
|
|
@@ -7057,6 +7057,78 @@ FirebaseServerAuthModule = __decorate([
|
|
|
7057
7057
|
return claims[FIREBASE_SERVER_AUTH_CLAIMS_SETUP_PASSWORD_KEY] != null;
|
|
7058
7058
|
}
|
|
7059
7059
|
|
|
7060
|
+
// MARK: Scope Reading
|
|
7061
|
+
/**
|
|
7062
|
+
* Reads the set of OIDC scopes carried by a model-api request's auth, or `undefined` for a non-OIDC
|
|
7063
|
+
* (regular Firebase ID-token) caller.
|
|
7064
|
+
*
|
|
7065
|
+
* The OIDC bearer-token middleware attaches the validated access-token claims at
|
|
7066
|
+
* `auth.oidcValidatedToken` (with the space-delimited `scope` string); a non-OIDC caller has neither
|
|
7067
|
+
* that field nor a `scope` on `auth.token`. Reading is defensive (the auth shape is only typed as
|
|
7068
|
+
* {@link FirebaseServerAuthData} here — the OIDC-specific `oidcValidatedToken` lives in the
|
|
7069
|
+
* `@dereekb/firebase-server/oidc` sub-package this core layer cannot import), delegating the actual
|
|
7070
|
+
* parse to the shared {@link oidcScopesFromScopeClaim} so there is no drift with `getOidcScopesFromRequest`.
|
|
7071
|
+
*
|
|
7072
|
+
* @param auth - The request auth data, or undefined for unauthenticated requests.
|
|
7073
|
+
* @returns The granted scope set, or `undefined` when the request carries no OIDC `scope` claim.
|
|
7074
|
+
*/ function oidcScopesFromModelApiAuth(auth) {
|
|
7075
|
+
var _auth_oidcValidatedToken, _auth_token;
|
|
7076
|
+
var oidcScope = auth === null || auth === void 0 ? void 0 : (_auth_oidcValidatedToken = auth.oidcValidatedToken) === null || _auth_oidcValidatedToken === void 0 ? void 0 : _auth_oidcValidatedToken.scope;
|
|
7077
|
+
var tokenScope = auth === null || auth === void 0 ? void 0 : (_auth_token = auth.token) === null || _auth_token === void 0 ? void 0 : _auth_token.scope;
|
|
7078
|
+
var scope = oidcScope !== null && oidcScope !== void 0 ? oidcScope : tokenScope;
|
|
7079
|
+
return oidcScopesFromScopeClaim(scope);
|
|
7080
|
+
}
|
|
7081
|
+
/**
|
|
7082
|
+
* Enforces the OIDC scope requirement for a single model-api op, throwing a `403`
|
|
7083
|
+
* {@link CALL_MODEL_MISSING_OIDC_SCOPE_ERROR_CODE} error when the caller does not satisfy it.
|
|
7084
|
+
*
|
|
7085
|
+
* The single home of callModel OIDC scope enforcement. It reuses the shipped composition + evaluation
|
|
7086
|
+
* (`resolveEffectiveOidcScopeTerms` / `oidcScopeTermsSatisfied`) so enforcement and the MCP
|
|
7087
|
+
* tool-visibility filter never drift. Enforcement is AND-of-ORs across the per-verb `model.<call>`
|
|
7088
|
+
* scope and the effective GROUP term (per-function `requiredScope` > per-model requirement >
|
|
7089
|
+
* configured default).
|
|
7090
|
+
*
|
|
7091
|
+
* Bypasses (no-op) when `grantedScopes` is `undefined` — i.e. a non-OIDC caller — and short-circuits
|
|
7092
|
+
* without any check when the op resolves no requirement at all (a custom, non-CRUD verb with no
|
|
7093
|
+
* per-function/model/default term).
|
|
7094
|
+
*
|
|
7095
|
+
* @param input - The verb, model type, per-function scope, group config, and the caller's granted scopes.
|
|
7096
|
+
* @throws A `403` forbidden error (code {@link CALL_MODEL_MISSING_OIDC_SCOPE_ERROR_CODE}) when an OIDC
|
|
7097
|
+
* caller does not satisfy the effective requirement.
|
|
7098
|
+
*/ function assertModelApiOidcScope(input) {
|
|
7099
|
+
var call = input.call, modelType = input.modelType, requiredScope = input.requiredScope, defaultRequiredScope = input.defaultRequiredScope, modelRequiredScopes = input.modelRequiredScopes, grantedScopes = input.grantedScopes;
|
|
7100
|
+
var terms = resolveEffectiveOidcScopeTerms({
|
|
7101
|
+
perVerbScope: callModelOidcScopeForCallType(call),
|
|
7102
|
+
requiredScope: requiredScope,
|
|
7103
|
+
modelRequirement: modelRequiredScopes === null || modelRequiredScopes === void 0 ? void 0 : modelRequiredScopes[modelType],
|
|
7104
|
+
call: call,
|
|
7105
|
+
defaultRequiredScope: defaultRequiredScope
|
|
7106
|
+
});
|
|
7107
|
+
if (terms.length > 0 && grantedScopes != null && !oidcScopeTermsSatisfied(terms, grantedScopes)) {
|
|
7108
|
+
var missingTerms = terms.filter(function(term) {
|
|
7109
|
+
return !oidcScopeTermSatisfied(term, grantedScopes);
|
|
7110
|
+
});
|
|
7111
|
+
throw forbiddenError({
|
|
7112
|
+
status: 403,
|
|
7113
|
+
code: CALL_MODEL_MISSING_OIDC_SCOPE_ERROR_CODE,
|
|
7114
|
+
message: "Missing required OIDC scope for callModel: ".concat(missingTerms.map(formatModelApiScopeTerm).join(', ')),
|
|
7115
|
+
data: {
|
|
7116
|
+
requiredScopes: missingTerms,
|
|
7117
|
+
call: call
|
|
7118
|
+
}
|
|
7119
|
+
});
|
|
7120
|
+
}
|
|
7121
|
+
}
|
|
7122
|
+
/**
|
|
7123
|
+
* Renders a scope term for the human-readable error message: a single scope as-is, an OR-group as its
|
|
7124
|
+
* alternatives joined by `|` (so a single-scope term reads as itself).
|
|
7125
|
+
*
|
|
7126
|
+
* @param term - The unsatisfied scope term.
|
|
7127
|
+
* @returns The display string for the term.
|
|
7128
|
+
*/ function formatModelApiScopeTerm(term) {
|
|
7129
|
+
return typeof term === 'string' ? term : term.join('|');
|
|
7130
|
+
}
|
|
7131
|
+
|
|
7060
7132
|
function _define_property$n(obj, key, value) {
|
|
7061
7133
|
if (key in obj) {
|
|
7062
7134
|
Object.defineProperty(obj, key, {
|
|
@@ -7333,6 +7405,15 @@ function _ts_generator$a(thisArg, body) {
|
|
|
7333
7405
|
/**
|
|
7334
7406
|
* Factory to create typed nest context from INestApplicationContext.
|
|
7335
7407
|
*/ _define_property$m(this, "makeNestContext", void 0);
|
|
7408
|
+
/**
|
|
7409
|
+
* Optional model-api-layer OIDC group-scope default. See {@link ModelApiOidcScopeConfig.defaultRequiredScope}.
|
|
7410
|
+
* This is the home for the callModel group-scope default; providing it here enforces it across
|
|
7411
|
+
* dispatch AND the `/get` reads.
|
|
7412
|
+
*/ _define_property$m(this, "defaultRequiredScope", void 0);
|
|
7413
|
+
/**
|
|
7414
|
+
* Optional per-model OIDC group-scope overrides. See {@link ModelApiOidcScopeConfig.modelRequiredScopes}.
|
|
7415
|
+
* The only place a plain `/get` read (no per-function handler) can be scope-gated beyond `model.read`.
|
|
7416
|
+
*/ _define_property$m(this, "modelRequiredScopes", void 0);
|
|
7336
7417
|
};
|
|
7337
7418
|
/**
|
|
7338
7419
|
* Injection token for providing the NestJS application context to the dispatch service.
|
|
@@ -7367,8 +7448,25 @@ function _ts_generator$a(thisArg, body) {
|
|
|
7367
7448
|
* @returns The handler's return value.
|
|
7368
7449
|
*/ function dispatch(params, auth, rawRequest) {
|
|
7369
7450
|
return _async_to_generator$a(function() {
|
|
7370
|
-
var callableRequest, appRequest, contextRequest;
|
|
7451
|
+
var call, modelType, specifier, apiDetails, requiredScope, callableRequest, appRequest, contextRequest;
|
|
7371
7452
|
return _ts_generator$a(this, function(_state) {
|
|
7453
|
+
// Enforce OIDC scope BEFORE dispatching — the relocated home of the callModel scope check.
|
|
7454
|
+
// AND-of-ORs across the per-verb `model.<call>` scope and the effective group term (per-function
|
|
7455
|
+
// `requiredScope` > per-model requirement > module default), bypassing non-OIDC callers. A nullish
|
|
7456
|
+
// `call` is a malformed request the callModel chain rejects downstream (no model op to authorize).
|
|
7457
|
+
call = params.call, modelType = params.modelType, specifier = params.specifier;
|
|
7458
|
+
if (call != null) {
|
|
7459
|
+
apiDetails = this.config.callModelFn._apiDetails;
|
|
7460
|
+
requiredScope = apiDetails == null ? undefined : resolveRequiredScopeFromApiDetails(apiDetails, call, modelType, specifier);
|
|
7461
|
+
assertModelApiOidcScope({
|
|
7462
|
+
call: call,
|
|
7463
|
+
modelType: modelType,
|
|
7464
|
+
requiredScope: requiredScope,
|
|
7465
|
+
defaultRequiredScope: this.config.defaultRequiredScope,
|
|
7466
|
+
modelRequiredScopes: this.config.modelRequiredScopes,
|
|
7467
|
+
grantedScopes: oidcScopesFromModelApiAuth(auth)
|
|
7468
|
+
});
|
|
7469
|
+
}
|
|
7372
7470
|
// Build a synthetic CallableRequest that the dispatch chain expects. Layer the
|
|
7373
7471
|
// OIDC-validated claim subset over the base token so standard JWT claims
|
|
7374
7472
|
// (`iat`, `auth_time`, `email`, …) survive — the callModel chain and any
|
|
@@ -7655,12 +7753,38 @@ function _ts_generator$9(thisArg, body) {
|
|
|
7655
7753
|
function ModelApiGetService(config, nestApplication) {
|
|
7656
7754
|
_class_call_check$e(this, ModelApiGetService);
|
|
7657
7755
|
_define_property$l(this, "_nestContext", void 0);
|
|
7756
|
+
_define_property$l(this, "_defaultRequiredScope", void 0);
|
|
7757
|
+
_define_property$l(this, "_modelRequiredScopes", void 0);
|
|
7658
7758
|
_define_property$l(this, "_identityByModelType", void 0);
|
|
7659
7759
|
this._nestContext = config.makeNestContext(nestApplication);
|
|
7760
|
+
this._defaultRequiredScope = config.defaultRequiredScope;
|
|
7761
|
+
this._modelRequiredScopes = config.modelRequiredScopes;
|
|
7660
7762
|
}
|
|
7661
7763
|
_create_class$9(ModelApiGetService, [
|
|
7662
7764
|
{
|
|
7663
7765
|
/**
|
|
7766
|
+
* Enforces the OIDC read-scope requirement for a direct document read before it hits Firestore.
|
|
7767
|
+
*
|
|
7768
|
+
* A direct `/get` read is the `read` verb: it requires the per-verb `model.read` scope AND any
|
|
7769
|
+
* effective group term for the target model (per-model requirement > module default). This is the
|
|
7770
|
+
* ONLY gate on the direct-read path — it does not touch the callModel dispatch chain — so without it
|
|
7771
|
+
* an OIDC client scoped to a subset could read any model through `/get`. Non-OIDC callers bypass.
|
|
7772
|
+
*
|
|
7773
|
+
* @param modelType - The Firestore model type being read.
|
|
7774
|
+
* @param auth - The request's auth data (OIDC scopes are read from it).
|
|
7775
|
+
*/ key: "_assertReadScope",
|
|
7776
|
+
value: function _assertReadScope(modelType, auth) {
|
|
7777
|
+
assertModelApiOidcScope({
|
|
7778
|
+
call: 'read',
|
|
7779
|
+
modelType: modelType,
|
|
7780
|
+
defaultRequiredScope: this._defaultRequiredScope,
|
|
7781
|
+
modelRequiredScopes: this._modelRequiredScopes,
|
|
7782
|
+
grantedScopes: oidcScopesFromModelApiAuth(auth)
|
|
7783
|
+
});
|
|
7784
|
+
}
|
|
7785
|
+
},
|
|
7786
|
+
{
|
|
7787
|
+
/**
|
|
7664
7788
|
* Returns the registered {@link FirestoreModelIdentity} for the given `modelType` string, or
|
|
7665
7789
|
* `undefined` when no model of that type is registered.
|
|
7666
7790
|
*
|
|
@@ -7732,6 +7856,7 @@ function _ts_generator$9(thisArg, body) {
|
|
|
7732
7856
|
return _ts_generator$9(this, function(_state) {
|
|
7733
7857
|
switch(_state.label){
|
|
7734
7858
|
case 0:
|
|
7859
|
+
this._assertReadScope(modelType, auth);
|
|
7735
7860
|
authRef = this._makeAuthRef(auth);
|
|
7736
7861
|
return [
|
|
7737
7862
|
4,
|
|
@@ -7780,6 +7905,7 @@ function _ts_generator$9(thisArg, body) {
|
|
|
7780
7905
|
return _async_to_generator$9(function() {
|
|
7781
7906
|
var authRef;
|
|
7782
7907
|
return _ts_generator$9(this, function(_state) {
|
|
7908
|
+
this._assertReadScope(modelType, auth);
|
|
7783
7909
|
authRef = this._makeAuthRef(auth);
|
|
7784
7910
|
return [
|
|
7785
7911
|
2,
|
|
@@ -13116,4 +13242,4 @@ function _define_property(obj, key, value) {
|
|
|
13116
13242
|
}
|
|
13117
13243
|
();
|
|
13118
13244
|
|
|
13119
|
-
export { AbstractFirebaseNestContext, AbstractFirebaseServerActionsContext, AbstractFirebaseServerAuthContext, AbstractFirebaseServerAuthService, AbstractFirebaseServerAuthUserContext, AbstractFirebaseServerNewUserService, AbstractFirebaseServerUserPasswordResetService, AbstractNestContext, AbstractServerFirebaseNestContext, ConfigureFirebaseAppCheckMiddlewareModule, ConfigureFirebaseWebhookMiddlewareModule, DEFAULT_FIREBASE_PASSWORD_NUMBER_GENERATOR, DEFAULT_RESET_CODE_EXPIRES_IN, DEFAULT_RESET_COM_THROTTLE_TIME, DEFAULT_SERVER_ASSETS_BASE_PATH, DEFAULT_SETUP_COM_THROTTLE_TIME, DefaultFirebaseServerEnvService, FIREBASE_APP_TOKEN, FIREBASE_AUTH_TOKEN, FIREBASE_FIRESTORE_CONTEXT_TOKEN, FIREBASE_FIRESTORE_TOKEN, FIREBASE_SERVER_ENV_TOKEN, FIREBASE_SERVER_VALIDATION_ERROR_CODE, FIREBASE_STORAGE_CONTEXT_FACTORY_CONFIG_TOKEN, FIREBASE_STORAGE_CONTEXT_TOKEN, FIREBASE_STORAGE_TOKEN, FIRESTORE_CLIENT_QUERY_CONSTRAINT_HANDLER_MAPPING, FirebaseAppCheckMiddleware, FirebaseAppCheckMiddlewareConfig, FirebaseNestServerRootModule, FirebaseRawBodyMiddleware, FirebaseServerAnalyticsSegmentListenerService, FirebaseServerAnalyticsSegmentModule, FirebaseServerAnalyticsService, FirebaseServerAnalyticsServiceListener, FirebaseServerAuthModule, FirebaseServerAuthNewUserSendSetupDetailsNoSetupConfigError, FirebaseServerAuthNewUserSendSetupDetailsSendOnceError, FirebaseServerAuthNewUserSendSetupDetailsThrottleError, FirebaseServerAuthPasswordResetInvalidCodeError, FirebaseServerAuthPasswordResetNoResetConfigError, FirebaseServerAuthPasswordResetSendOnceError, FirebaseServerAuthPasswordResetThrottleError, FirebaseServerAuthService, FirebaseServerAuthUserBadInputError, FirebaseServerAuthUserExistsError, FirebaseServerEnvService, FirebaseServerFirestoreContextModule, FirebaseServerFirestoreModule, FirebaseServerStorageContextModule, FirebaseServerStorageModule, FirebaseServerStorageService, GlobalRoutePrefixConfig, MAX_MODEL_ACCESS_MULTI_READ_KEYS, MODEL_API_NEST_APPLICATION_CONTEXT, ModelApiCallModelDispatchService, ModelApiController, ModelApiDispatchConfig, ModelApiGetService, NO_RUN_NAME_SPECIFIED_FOR_SCHEDULED_FUNCTION_DEVELOPMENT_FUNCTION_CODE, NoContentFirebaseServerUserPasswordResetService, NoSetupContentFirebaseServerNewUserService, ON_CALL_MODEL_ANALYTICS_HANDLER, ON_CALL_MODEL_ANALYTICS_SERVICE, OnCallModelAnalyticsService, PHONE_NUMBER_ALREADY_EXISTS_ERROR_CODE, SkipAppCheck, UNKNOWN_SCHEDULED_FUNCTION_DEVELOPMENT_FUNCTION_NAME_CODE, UNKNOWN_SCHEDULED_FUNCTION_DEVELOPMENT_FUNCTION_TYPE_CODE, _onCallWithCallTypeFunction, aggregateCrudModelApiDetails, aggregateModelApiDetails, aggregateSpecifierApiDetails, alreadyExistsError, appAnalyticsModuleMetadata, appFirestoreModuleMetadata, assertContextHasAuth, assertDocumentExists, assertHasRolesInRequest, assertHasSignedTosInRequest, assertIsAdminInRequest, assertIsAdminOrTargetUserInRequestData, assertIsContextWithAuthData, assertRequestRequiresAuthForFunction, assertSnapshotData, assertSnapshotDataWithKey, authServicePasswordResetInvalidCodeError, authServicePasswordResetNoConfigError, authServicePasswordResetSendOnceError, authServicePasswordResetThrottleError, badRequestError, blockingFunctionHandlerWithNestContextFactory, buildNestServerRootModule, callWithAnalytics, catchAndThrowPasswordResetServerErrors, cloudEventHandlerWithNestContextFactory, collectionRefForPath, createModelUnknownModelTypeError, decodeFirebaseServerUserPasswordResetOobCode, defaultFirebaseServerActionsTransformFactoryLogErrorFunction, defaultProvideFirebaseServerStorageServiceSimple, deleteModelUnknownModelTypeError, developmentUnknownSpecifierError, docRefForPath, documentModelNotAvailableError, encodeFirebaseServerUserPasswordResetOobCode, executeOnCallQuery, firebaseAuthTokenFromDecodedIdToken, firebaseServerActionsContext, firebaseServerActionsTransformContext, firebaseServerActionsTransformFactory, firebaseServerAppTokenProvider, firebaseServerAuthModuleMetadata, firebaseServerDevFunctions, firebaseServerEnvTokenProvider, firebaseServerEnvTokenProviders, firebaseServerErrorInfo, firebaseServerErrorInfoCodePair, firebaseServerErrorInfoServerErrorCodePair, firebaseServerErrorInfoServerErrorPair, firebaseServerFirestoreContextModuleMetadata, firebaseServerStorageDefaultBucketIdTokenProvider, firebaseServerStorageModuleMetadata, firebaseServerValidationError, firebaseServerValidationServerError, firestoreClientQueryConstraintFunctionsDriver, firestoreEncryptedField, firestoreServerIncrementUpdateToUpdateData, forbiddenError, getAuthUserOrUndefined, getModelApiDetails, googleCloudFileMetadataToStorageMetadata, googleCloudFirebaseStorageContextFactory, googleCloudFirebaseStorageDrivers, googleCloudFirestoreAccessorDriver, googleCloudFirestoreContextFactory, googleCloudFirestoreDrivers, googleCloudFirestoreQueryDriver, googleCloudStorageAccessorFile, googleCloudStorageAccessorFolder, googleCloudStorageBucketForStorageFilePath, googleCloudStorageFileForStorageFilePath, googleCloudStorageFirebaseStorageAccessorDriver, googleCloudStorageFromFirebaseAdminStorage, googleCloudStorageListFilesResultFactory, handleFirebaseAuthError, handleFirebaseError, hasAuthRolesInRequest, hasNewUserSetupPasswordInRequest, hasSignedTosInRequest, inAuthContext, injectNestApplicationContextIntoRequest, injectNestIntoRequest, internalServerError, invokeModelUnknownModelTypeError, isActualSpecifier, isAdminInRequest, isAdminOrTargetUserInRequestData, isContextWithAuthData, isFirebaseError, isFirebaseHttpsError, isOnCallCrudModelApiDetails, isOnCallHandlerApiDetails, isOnCallModelTypeApiDetails, isOnCallSpecifierApiDetails, makeBlockingFunctionWithHandler, makeOnScheduleHandlerWithNestApplicationRequest, makeScheduledFunctionDevelopmentFunction, modelAccessReadErrorFromUseMultipleModelsFailure, modelApiModuleMetadata, modelNotAvailableError, nestAppHasDevelopmentSchedulerEnabled, nestAppIsProductionEnvironment, nestFirebaseDoesNotExistError, nestFirebaseForbiddenPermissionError, nestServerInstance, noRunNameSpecifiedForScheduledFunctionDevelopmentFunction, noopFirebaseServerAnalyticsServiceListener, noopOnCallModelAnalyticsService, notFoundError, onCallAnalyticsEmitterInstance, onCallCreateModel, onCallDeleteModel, onCallDevelopmentFunction, onCallHandlerWithNestApplicationFactory, onCallHandlerWithNestContextFactory, onCallInvokeModel, onCallModel, onCallModelMissingCallTypeError, onCallModelUnknownCallTypeError, onCallQueryModel, onCallReadModel, onCallSpecifierHandler, onCallUpdateModel, onScheduleHandlerWithNestApplicationFactory, onScheduleHandlerWithNestContextFactory, optionalAuthContext, optionalFirestoreEncryptedField, permissionDeniedError, phoneNumberAlreadyExistsError, preconditionConflictError, provideAppFirestoreCollections, provideFirebaseServerAuthService, provideFirebaseServerStorageService, queryModelBadCursorError, queryModelUnknownModelTypeError, readApiDetails, readModelUnknownModelTypeError, resolveAdminOnlyValue, resolveAnalyticsFromApiDetails, resolveRequiredScopeFromApiDetails, setNestContextOnRequest, setNestContextOnScheduleRequest, taskQueueFunctionHandlerWithNestContextFactory, unauthenticatedContextHasNoAuthData, unauthenticatedContextHasNoUidError, unauthenticatedError, unavailableError, unavailableOrDeactivatedFunctionError, unknownModelCrudFunctionSpecifierError, unknownScheduledFunctionDevelopmentFunctionName, unknownScheduledFunctionDevelopmentFunctionType, updateModelUnknownModelTypeError, userContextFromUid, verifyAppCheckInRequest, withApiDetails };
|
|
13245
|
+
export { AbstractFirebaseNestContext, AbstractFirebaseServerActionsContext, AbstractFirebaseServerAuthContext, AbstractFirebaseServerAuthService, AbstractFirebaseServerAuthUserContext, AbstractFirebaseServerNewUserService, AbstractFirebaseServerUserPasswordResetService, AbstractNestContext, AbstractServerFirebaseNestContext, ConfigureFirebaseAppCheckMiddlewareModule, ConfigureFirebaseWebhookMiddlewareModule, DEFAULT_FIREBASE_PASSWORD_NUMBER_GENERATOR, DEFAULT_RESET_CODE_EXPIRES_IN, DEFAULT_RESET_COM_THROTTLE_TIME, DEFAULT_SERVER_ASSETS_BASE_PATH, DEFAULT_SETUP_COM_THROTTLE_TIME, DefaultFirebaseServerEnvService, FIREBASE_APP_TOKEN, FIREBASE_AUTH_TOKEN, FIREBASE_FIRESTORE_CONTEXT_TOKEN, FIREBASE_FIRESTORE_TOKEN, FIREBASE_SERVER_ENV_TOKEN, FIREBASE_SERVER_VALIDATION_ERROR_CODE, FIREBASE_STORAGE_CONTEXT_FACTORY_CONFIG_TOKEN, FIREBASE_STORAGE_CONTEXT_TOKEN, FIREBASE_STORAGE_TOKEN, FIRESTORE_CLIENT_QUERY_CONSTRAINT_HANDLER_MAPPING, FirebaseAppCheckMiddleware, FirebaseAppCheckMiddlewareConfig, FirebaseNestServerRootModule, FirebaseRawBodyMiddleware, FirebaseServerAnalyticsSegmentListenerService, FirebaseServerAnalyticsSegmentModule, FirebaseServerAnalyticsService, FirebaseServerAnalyticsServiceListener, FirebaseServerAuthModule, FirebaseServerAuthNewUserSendSetupDetailsNoSetupConfigError, FirebaseServerAuthNewUserSendSetupDetailsSendOnceError, FirebaseServerAuthNewUserSendSetupDetailsThrottleError, FirebaseServerAuthPasswordResetInvalidCodeError, FirebaseServerAuthPasswordResetNoResetConfigError, FirebaseServerAuthPasswordResetSendOnceError, FirebaseServerAuthPasswordResetThrottleError, FirebaseServerAuthService, FirebaseServerAuthUserBadInputError, FirebaseServerAuthUserExistsError, FirebaseServerEnvService, FirebaseServerFirestoreContextModule, FirebaseServerFirestoreModule, FirebaseServerStorageContextModule, FirebaseServerStorageModule, FirebaseServerStorageService, GlobalRoutePrefixConfig, MAX_MODEL_ACCESS_MULTI_READ_KEYS, MODEL_API_NEST_APPLICATION_CONTEXT, ModelApiCallModelDispatchService, ModelApiController, ModelApiDispatchConfig, ModelApiGetService, NO_RUN_NAME_SPECIFIED_FOR_SCHEDULED_FUNCTION_DEVELOPMENT_FUNCTION_CODE, NoContentFirebaseServerUserPasswordResetService, NoSetupContentFirebaseServerNewUserService, ON_CALL_MODEL_ANALYTICS_HANDLER, ON_CALL_MODEL_ANALYTICS_SERVICE, OnCallModelAnalyticsService, PHONE_NUMBER_ALREADY_EXISTS_ERROR_CODE, SkipAppCheck, UNKNOWN_SCHEDULED_FUNCTION_DEVELOPMENT_FUNCTION_NAME_CODE, UNKNOWN_SCHEDULED_FUNCTION_DEVELOPMENT_FUNCTION_TYPE_CODE, _onCallWithCallTypeFunction, aggregateCrudModelApiDetails, aggregateModelApiDetails, aggregateSpecifierApiDetails, alreadyExistsError, appAnalyticsModuleMetadata, appFirestoreModuleMetadata, assertContextHasAuth, assertDocumentExists, assertHasRolesInRequest, assertHasSignedTosInRequest, assertIsAdminInRequest, assertIsAdminOrTargetUserInRequestData, assertIsContextWithAuthData, assertModelApiOidcScope, assertRequestRequiresAuthForFunction, assertSnapshotData, assertSnapshotDataWithKey, authServicePasswordResetInvalidCodeError, authServicePasswordResetNoConfigError, authServicePasswordResetSendOnceError, authServicePasswordResetThrottleError, badRequestError, blockingFunctionHandlerWithNestContextFactory, buildNestServerRootModule, callWithAnalytics, catchAndThrowPasswordResetServerErrors, cloudEventHandlerWithNestContextFactory, collectionRefForPath, createModelUnknownModelTypeError, decodeFirebaseServerUserPasswordResetOobCode, defaultFirebaseServerActionsTransformFactoryLogErrorFunction, defaultProvideFirebaseServerStorageServiceSimple, deleteModelUnknownModelTypeError, developmentUnknownSpecifierError, docRefForPath, documentModelNotAvailableError, encodeFirebaseServerUserPasswordResetOobCode, executeOnCallQuery, firebaseAuthTokenFromDecodedIdToken, firebaseServerActionsContext, firebaseServerActionsTransformContext, firebaseServerActionsTransformFactory, firebaseServerAppTokenProvider, firebaseServerAuthModuleMetadata, firebaseServerDevFunctions, firebaseServerEnvTokenProvider, firebaseServerEnvTokenProviders, firebaseServerErrorInfo, firebaseServerErrorInfoCodePair, firebaseServerErrorInfoServerErrorCodePair, firebaseServerErrorInfoServerErrorPair, firebaseServerFirestoreContextModuleMetadata, firebaseServerStorageDefaultBucketIdTokenProvider, firebaseServerStorageModuleMetadata, firebaseServerValidationError, firebaseServerValidationServerError, firestoreClientQueryConstraintFunctionsDriver, firestoreEncryptedField, firestoreServerIncrementUpdateToUpdateData, forbiddenError, getAuthUserOrUndefined, getModelApiDetails, googleCloudFileMetadataToStorageMetadata, googleCloudFirebaseStorageContextFactory, googleCloudFirebaseStorageDrivers, googleCloudFirestoreAccessorDriver, googleCloudFirestoreContextFactory, googleCloudFirestoreDrivers, googleCloudFirestoreQueryDriver, googleCloudStorageAccessorFile, googleCloudStorageAccessorFolder, googleCloudStorageBucketForStorageFilePath, googleCloudStorageFileForStorageFilePath, googleCloudStorageFirebaseStorageAccessorDriver, googleCloudStorageFromFirebaseAdminStorage, googleCloudStorageListFilesResultFactory, handleFirebaseAuthError, handleFirebaseError, hasAuthRolesInRequest, hasNewUserSetupPasswordInRequest, hasSignedTosInRequest, inAuthContext, injectNestApplicationContextIntoRequest, injectNestIntoRequest, internalServerError, invokeModelUnknownModelTypeError, isActualSpecifier, isAdminInRequest, isAdminOrTargetUserInRequestData, isContextWithAuthData, isFirebaseError, isFirebaseHttpsError, isOnCallCrudModelApiDetails, isOnCallHandlerApiDetails, isOnCallModelTypeApiDetails, isOnCallSpecifierApiDetails, makeBlockingFunctionWithHandler, makeOnScheduleHandlerWithNestApplicationRequest, makeScheduledFunctionDevelopmentFunction, modelAccessReadErrorFromUseMultipleModelsFailure, modelApiModuleMetadata, modelNotAvailableError, nestAppHasDevelopmentSchedulerEnabled, nestAppIsProductionEnvironment, nestFirebaseDoesNotExistError, nestFirebaseForbiddenPermissionError, nestServerInstance, noRunNameSpecifiedForScheduledFunctionDevelopmentFunction, noopFirebaseServerAnalyticsServiceListener, noopOnCallModelAnalyticsService, notFoundError, oidcScopesFromModelApiAuth, onCallAnalyticsEmitterInstance, onCallCreateModel, onCallDeleteModel, onCallDevelopmentFunction, onCallHandlerWithNestApplicationFactory, onCallHandlerWithNestContextFactory, onCallInvokeModel, onCallModel, onCallModelMissingCallTypeError, onCallModelUnknownCallTypeError, onCallQueryModel, onCallReadModel, onCallSpecifierHandler, onCallUpdateModel, onScheduleHandlerWithNestApplicationFactory, onScheduleHandlerWithNestContextFactory, optionalAuthContext, optionalFirestoreEncryptedField, permissionDeniedError, phoneNumberAlreadyExistsError, preconditionConflictError, provideAppFirestoreCollections, provideFirebaseServerAuthService, provideFirebaseServerStorageService, queryModelBadCursorError, queryModelUnknownModelTypeError, readApiDetails, readModelUnknownModelTypeError, resolveAdminOnlyValue, resolveAnalyticsFromApiDetails, resolveRequiredScopeFromApiDetails, setNestContextOnRequest, setNestContextOnScheduleRequest, taskQueueFunctionHandlerWithNestContextFactory, unauthenticatedContextHasNoAuthData, unauthenticatedContextHasNoUidError, unauthenticatedError, unavailableError, unavailableOrDeactivatedFunctionError, unknownModelCrudFunctionSpecifierError, unknownScheduledFunctionDevelopmentFunctionName, unknownScheduledFunctionDevelopmentFunctionType, updateModelUnknownModelTypeError, userContextFromUid, verifyAppCheckInRequest, withApiDetails };
|
package/mailgun/package.json
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dereekb/firebase-server/mailgun",
|
|
3
|
-
"version": "13.
|
|
3
|
+
"version": "13.30.0",
|
|
4
4
|
"peerDependencies": {
|
|
5
|
-
"@dereekb/analytics": "13.
|
|
6
|
-
"@dereekb/firebase": "13.
|
|
7
|
-
"@dereekb/firebase-server": "13.
|
|
8
|
-
"@dereekb/date": "13.
|
|
9
|
-
"@dereekb/nestjs": "13.
|
|
10
|
-
"@dereekb/model": "13.
|
|
11
|
-
"@dereekb/rxjs": "13.
|
|
12
|
-
"@dereekb/util": "13.
|
|
5
|
+
"@dereekb/analytics": "13.30.0",
|
|
6
|
+
"@dereekb/firebase": "13.30.0",
|
|
7
|
+
"@dereekb/firebase-server": "13.30.0",
|
|
8
|
+
"@dereekb/date": "13.30.0",
|
|
9
|
+
"@dereekb/nestjs": "13.30.0",
|
|
10
|
+
"@dereekb/model": "13.30.0",
|
|
11
|
+
"@dereekb/rxjs": "13.30.0",
|
|
12
|
+
"@dereekb/util": "13.30.0"
|
|
13
13
|
},
|
|
14
14
|
"exports": {
|
|
15
15
|
"./package.json": "./package.json",
|
package/mcp/index.cjs.js
CHANGED
|
@@ -4,12 +4,12 @@ var common = require('@nestjs/common');
|
|
|
4
4
|
var node_fs = require('node:fs');
|
|
5
5
|
var mcp_js = require('@modelcontextprotocol/sdk/server/mcp.js');
|
|
6
6
|
var types_js = require('@modelcontextprotocol/sdk/types.js');
|
|
7
|
+
var firebase = require('@dereekb/firebase');
|
|
7
8
|
var oidc = require('@dereekb/firebase-server/oidc');
|
|
8
9
|
var util = require('@dereekb/util');
|
|
9
10
|
var firebaseServer = require('@dereekb/firebase-server');
|
|
10
11
|
var node_util = require('node:util');
|
|
11
12
|
var model = require('@dereekb/model');
|
|
12
|
-
var firebase = require('@dereekb/firebase');
|
|
13
13
|
var streamableHttp_js = require('@modelcontextprotocol/sdk/server/streamableHttp.js');
|
|
14
14
|
|
|
15
15
|
function _class_call_check$5(instance, Constructor) {
|
|
@@ -167,6 +167,22 @@ function _define_property$9(obj, key, value) {
|
|
|
167
167
|
* fills with a short justification for the call. The value is forwarded to analytics and stripped
|
|
168
168
|
* from the dispatched handler body. See {@link McpReasonParameterConfig}.
|
|
169
169
|
*/ _define_property$9(this, "reasonParameter", void 0);
|
|
170
|
+
/**
|
|
171
|
+
* Default OIDC scope group term required on EVERY `callModel` op, mirrored into MCP tool-list
|
|
172
|
+
* visibility, unless a finer term overrides it (a per-function `requiredScope`, then a
|
|
173
|
+
* {@link McpModuleConfig.modelRequiredScopes} entry). Threaded into the same
|
|
174
|
+
* `resolveEffectiveOidcScopeTerms` composition the server-side model-api scope gate
|
|
175
|
+
* (`assertModelApiOidcScope`) uses, so a tool is advertised only when the caller could actually invoke it.
|
|
176
|
+
*
|
|
177
|
+
* Set this to the SAME value passed to `ModelApiDispatchConfig.defaultRequiredScope` to
|
|
178
|
+
* keep tool visibility and enforcement in lockstep.
|
|
179
|
+
*/ _define_property$9(this, "defaultRequiredScope", void 0);
|
|
180
|
+
/**
|
|
181
|
+
* Per-model OIDC scope group-term overrides, keyed by {@link FirestoreModelType}, mirrored into MCP
|
|
182
|
+
* tool-list visibility. Set this to the SAME value passed to
|
|
183
|
+
* `ModelApiDispatchConfig.modelRequiredScopes` so per-model / verb-keyed restrictions that
|
|
184
|
+
* confine a subset client apply identically to the advertised tool list.
|
|
185
|
+
*/ _define_property$9(this, "modelRequiredScopes", void 0);
|
|
170
186
|
};
|
|
171
187
|
/**
|
|
172
188
|
* NestJS injection token for the optional {@link McpAuthRoleReader} provider.
|
|
@@ -1400,6 +1416,8 @@ function _unsupported_iterable_to_array$9(o, minLen) {
|
|
|
1400
1416
|
var seenNames = new Set();
|
|
1401
1417
|
var manifest = context === null || context === void 0 ? void 0 : context.manifest;
|
|
1402
1418
|
var naming = context === null || context === void 0 ? void 0 : context.naming;
|
|
1419
|
+
var defaultRequiredScope = context === null || context === void 0 ? void 0 : context.defaultRequiredScope;
|
|
1420
|
+
var modelRequiredScopes = context === null || context === void 0 ? void 0 : context.modelRequiredScopes;
|
|
1403
1421
|
var candidates = planMcpToolCandidates(apiDetails, naming);
|
|
1404
1422
|
var clashCounts = countVisibleAutoNameClashes(candidates);
|
|
1405
1423
|
var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
|
|
@@ -1411,6 +1429,8 @@ function _unsupported_iterable_to_array$9(o, minLen) {
|
|
|
1411
1429
|
clashCounts: clashCounts,
|
|
1412
1430
|
options: options,
|
|
1413
1431
|
manifest: manifest,
|
|
1432
|
+
defaultRequiredScope: defaultRequiredScope,
|
|
1433
|
+
modelRequiredScopes: modelRequiredScopes,
|
|
1414
1434
|
seenNames: seenNames,
|
|
1415
1435
|
outTools: tools,
|
|
1416
1436
|
outNeverVisibleTools: neverVisibleTools,
|
|
@@ -1582,7 +1602,7 @@ function _unsupported_iterable_to_array$9(o, minLen) {
|
|
|
1582
1602
|
*/ function buildToolFromCandidate(context) {
|
|
1583
1603
|
var _clashCounts_get, _ref;
|
|
1584
1604
|
var _handlerDetails_mcp, _handlerDetails_mcp1;
|
|
1585
|
-
var candidate = context.candidate, clashCounts = context.clashCounts, options = context.options, manifest = context.manifest, seenNames = context.seenNames, outTools = context.outTools, outNeverVisibleTools = context.outNeverVisibleTools, outSkipped = context.outSkipped, outWarnings = context.outWarnings;
|
|
1605
|
+
var candidate = context.candidate, clashCounts = context.clashCounts, options = context.options, manifest = context.manifest, defaultRequiredScope = context.defaultRequiredScope, modelRequiredScopes = context.modelRequiredScopes, seenNames = context.seenNames, outTools = context.outTools, outNeverVisibleTools = context.outNeverVisibleTools, outSkipped = context.outSkipped, outWarnings = context.outWarnings;
|
|
1586
1606
|
var modelType = candidate.modelType, callType = candidate.callType, handlerDetails = candidate.handlerDetails, specifier = candidate.specifier, dispatch = candidate.dispatch, modelSegment = candidate.modelSegment, overrideName = candidate.overrideName, baseName = candidate.baseName, classified = candidate.classified, isVisible = candidate.isVisible;
|
|
1587
1607
|
// A visible auto-named tool whose preferred name is produced by more than one visible tool is
|
|
1588
1608
|
// re-derived with the abbreviated call type so both survive on the wire. Overrides and hidden tools
|
|
@@ -1632,13 +1652,18 @@ function _unsupported_iterable_to_array$9(o, minLen) {
|
|
|
1632
1652
|
});
|
|
1633
1653
|
}
|
|
1634
1654
|
}
|
|
1635
|
-
//
|
|
1636
|
-
//
|
|
1637
|
-
//
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1655
|
+
// AND-of-ORs scope terms: the per-verb call-type scope (model.<call>) plus the effective group term,
|
|
1656
|
+
// resolved by the same precedence the server model-api scope gate uses (per-function requiredScope > per-model
|
|
1657
|
+
// requirement > module default) via the shared resolveEffectiveOidcScopeTerms — so tool visibility
|
|
1658
|
+
// tracks callability. Empty when no term applies (a non-CRUD call type with no per-function/model/
|
|
1659
|
+
// default term → no scope gate).
|
|
1660
|
+
var requiredScopeTerms = firebase.resolveEffectiveOidcScopeTerms({
|
|
1661
|
+
perVerbScope: resolveRequiredScope(callType),
|
|
1662
|
+
requiredScope: handlerDetails.requiredScope,
|
|
1663
|
+
modelRequirement: modelRequiredScopes === null || modelRequiredScopes === void 0 ? void 0 : modelRequiredScopes[modelType],
|
|
1664
|
+
call: callType,
|
|
1665
|
+
defaultRequiredScope: defaultRequiredScope
|
|
1666
|
+
});
|
|
1642
1667
|
var effectiveReadOnly = resolveEffectiveReadOnly((_handlerDetails_mcp = handlerDetails.mcp) === null || _handlerDetails_mcp === void 0 ? void 0 : _handlerDetails_mcp.readOnly, callType);
|
|
1643
1668
|
var annotations = resolveMcpToolAnnotations(effectiveReadOnly);
|
|
1644
1669
|
var description = applyWriteMarker(baseDescription, annotations);
|
|
@@ -1647,20 +1672,20 @@ function _unsupported_iterable_to_array$9(o, minLen) {
|
|
|
1647
1672
|
filterMetadata = {
|
|
1648
1673
|
visibilityKind: 'declarative',
|
|
1649
1674
|
rule: classified.rule,
|
|
1650
|
-
|
|
1675
|
+
requiredScopeTerms: requiredScopeTerms,
|
|
1651
1676
|
effectiveReadOnly: effectiveReadOnly
|
|
1652
1677
|
};
|
|
1653
1678
|
} else if (classified.visibilityKind === 'dynamic') {
|
|
1654
1679
|
filterMetadata = {
|
|
1655
1680
|
visibilityKind: 'dynamic',
|
|
1656
1681
|
visibilityFn: classified.visibilityFn,
|
|
1657
|
-
|
|
1682
|
+
requiredScopeTerms: requiredScopeTerms,
|
|
1658
1683
|
effectiveReadOnly: effectiveReadOnly
|
|
1659
1684
|
};
|
|
1660
1685
|
} else {
|
|
1661
1686
|
filterMetadata = {
|
|
1662
1687
|
visibilityKind: classified.visibilityKind,
|
|
1663
|
-
|
|
1688
|
+
requiredScopeTerms: requiredScopeTerms,
|
|
1664
1689
|
effectiveReadOnly: effectiveReadOnly
|
|
1665
1690
|
};
|
|
1666
1691
|
}
|
|
@@ -6163,7 +6188,9 @@ var McpServerFactoryService_1;
|
|
|
6163
6188
|
} else {
|
|
6164
6189
|
result = generateMcpToolDefinitions(apiDetails, undefined, {
|
|
6165
6190
|
manifest: manifest,
|
|
6166
|
-
naming: this._resolveToolNamingOptions()
|
|
6191
|
+
naming: this._resolveToolNamingOptions(),
|
|
6192
|
+
defaultRequiredScope: this.mcpConfig.defaultRequiredScope,
|
|
6193
|
+
modelRequiredScopes: this.mcpConfig.modelRequiredScopes
|
|
6167
6194
|
});
|
|
6168
6195
|
}
|
|
6169
6196
|
this._cachedTools = result;
|
|
@@ -6632,7 +6659,7 @@ var McpServerFactoryService_1;
|
|
|
6632
6659
|
* Synthesizes the same `{ auth: { token } }` shape that `getOidcScopesFromRequest`
|
|
6633
6660
|
* expects post-dispatch, so the upstream helper stays the single source of scope parsing.
|
|
6634
6661
|
* Returns `undefined` for non-OIDC callers (no `oidcValidatedToken.scope`) — the filter
|
|
6635
|
-
* loop treats that as "skip scope enforcement", matching `
|
|
6662
|
+
* loop treats that as "skip scope enforcement", matching the model-api scope gate (`assertModelApiOidcScope`).
|
|
6636
6663
|
*
|
|
6637
6664
|
* @param ctx - The per-request context carrying the validated auth payload.
|
|
6638
6665
|
* @returns The set of granted OIDC scopes, or `undefined` when scope enforcement should be skipped.
|
|
@@ -6676,9 +6703,7 @@ var McpServerFactoryService_1;
|
|
|
6676
6703
|
for(var _iterator = tools[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
|
|
6677
6704
|
var tool = _step.value;
|
|
6678
6705
|
var filterMetadata = tool.filterMetadata;
|
|
6679
|
-
if (scopes != null && filterMetadata.
|
|
6680
|
-
return scopes.has(scope);
|
|
6681
|
-
})) {
|
|
6706
|
+
if (scopes != null && filterMetadata.requiredScopeTerms != null && !firebase.oidcScopeTermsSatisfied(filterMetadata.requiredScopeTerms, scopes)) {
|
|
6682
6707
|
continue;
|
|
6683
6708
|
}
|
|
6684
6709
|
if (readOnlyMode && filterMetadata.effectiveReadOnly !== true) {
|