@dereekb/firebase-server 13.10.9 → 13.11.1
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/mailgun/package.json +9 -9
- package/model/package.json +9 -9
- package/oidc/index.cjs.js +166 -2
- package/oidc/index.esm.js +161 -4
- package/oidc/package.json +10 -10
- package/oidc/src/lib/controller/oidc.provider.controller.d.ts +16 -1
- package/oidc/src/lib/index.d.ts +1 -0
- package/oidc/src/lib/scope.d.ts +59 -0
- package/oidc/src/lib/service/oidc.auth.d.ts +20 -0
- package/package.json +10 -10
- package/src/lib/nest/model/index.d.ts +1 -0
- package/test/index.cjs.js +137 -18
- package/test/index.esm.js +137 -18
- package/test/package.json +11 -11
- package/test/src/lib/firebase/firebase.admin.auth.d.ts +37 -10
- package/test/src/lib/firebase/firebase.admin.collection.d.ts +3 -0
- package/test/src/lib/firebase/firebase.admin.function.d.ts +2 -0
- package/test/src/lib/firebase/firebase.admin.nest.d.ts +3 -0
- package/test/src/lib/firebase/firebase.admin.nest.function.callable.context.d.ts +3 -0
- package/test/src/lib/firebase/firebase.admin.nest.function.cloud.context.d.ts +3 -0
- package/test/src/lib/firebase/firebase.admin.nest.function.d.ts +4 -0
- package/test/src/lib/firebase/firebase.d.ts +13 -0
- package/test/src/lib/firebase/firebase.function.d.ts +19 -0
- package/test/src/lib/firebase/firebase.test.d.ts +2 -2
- package/test/src/lib/firestore/firestore.d.ts +1 -0
- package/test/src/lib/oidc/oidc.test.fixture.d.ts +17 -0
- package/test/src/lib/oidc/oidc.test.flow.d.ts +25 -1
- package/test/src/lib/storage/storage.d.ts +1 -0
- package/zoho/package.json +9 -9
package/mailgun/package.json
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dereekb/firebase-server/mailgun",
|
|
3
|
-
"version": "13.
|
|
3
|
+
"version": "13.11.1",
|
|
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.11.1",
|
|
6
|
+
"@dereekb/firebase": "13.11.1",
|
|
7
|
+
"@dereekb/firebase-server": "13.11.1",
|
|
8
|
+
"@dereekb/date": "13.11.1",
|
|
9
|
+
"@dereekb/nestjs": "13.11.1",
|
|
10
|
+
"@dereekb/model": "13.11.1",
|
|
11
|
+
"@dereekb/rxjs": "13.11.1",
|
|
12
|
+
"@dereekb/util": "13.11.1"
|
|
13
13
|
},
|
|
14
14
|
"exports": {
|
|
15
15
|
"./package.json": "./package.json",
|
package/model/package.json
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dereekb/firebase-server/model",
|
|
3
|
-
"version": "13.
|
|
3
|
+
"version": "13.11.1",
|
|
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.
|
|
5
|
+
"@dereekb/analytics": "13.11.1",
|
|
6
|
+
"@dereekb/date": "13.11.1",
|
|
7
|
+
"@dereekb/firebase": "13.11.1",
|
|
8
|
+
"@dereekb/firebase-server": "13.11.1",
|
|
9
|
+
"@dereekb/model": "13.11.1",
|
|
10
|
+
"@dereekb/nestjs": "13.11.1",
|
|
11
|
+
"@dereekb/rxjs": "13.11.1",
|
|
12
|
+
"@dereekb/util": "13.11.1",
|
|
13
13
|
"@nestjs/common": "^11.1.19",
|
|
14
14
|
"@nestjs/config": "^4.0.4",
|
|
15
15
|
"archiver": "^7.0.1",
|
package/oidc/index.cjs.js
CHANGED
|
@@ -4076,6 +4076,33 @@ var _logger = new common.Logger('applyOidcAuthMiddleware');
|
|
|
4076
4076
|
_logger.debug("Applied OAuth bearer token middleware for paths: ".concat(protectedPaths.join(', ')));
|
|
4077
4077
|
}
|
|
4078
4078
|
|
|
4079
|
+
// MARK: Scopes
|
|
4080
|
+
/**
|
|
4081
|
+
* Reads the set of OIDC scopes carried by a callable request's bearer token.
|
|
4082
|
+
*
|
|
4083
|
+
* The `ModelApiCallModelDispatchService` builds a synthetic `CallableRequest` with
|
|
4084
|
+
* `auth.token = oidcValidatedToken ?? {}`, so the OIDC scope string lives at
|
|
4085
|
+
* `request.auth.token.scope` for OIDC callers and is `undefined` for non-OIDC
|
|
4086
|
+
* (regular Firebase ID-token) callers.
|
|
4087
|
+
*
|
|
4088
|
+
* Returning `undefined` for non-OIDC callers lets callers (e.g. `oidcCallModelScopePreAssert`)
|
|
4089
|
+
* skip scope enforcement instead of falsely treating the missing claim as "no scopes granted".
|
|
4090
|
+
*
|
|
4091
|
+
* The parameter is intentionally typed as `unknown` because `DecodedIdToken` (Firebase
|
|
4092
|
+
* Auth's typed token shape) does not declare a `scope` field, while OIDC bearer-token
|
|
4093
|
+
* callers attach one at runtime. Reading is done defensively.
|
|
4094
|
+
*
|
|
4095
|
+
* @param request - The callable request as seen inside an `onCallModel` handler.
|
|
4096
|
+
* @returns A `Set<string>` of granted scopes, or `undefined` when the request carries no OIDC `scope` claim.
|
|
4097
|
+
*/ function getOidcScopesFromRequest(request) {
|
|
4098
|
+
var _request_auth_token, _request_auth;
|
|
4099
|
+
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;
|
|
4100
|
+
var result = typeof scope === 'string' ? new Set(scope.split(' ').filter(function(value) {
|
|
4101
|
+
return value.length > 0;
|
|
4102
|
+
})) : undefined;
|
|
4103
|
+
return result;
|
|
4104
|
+
}
|
|
4105
|
+
|
|
4079
4106
|
function asyncGeneratorStep$3(gen, resolve, reject, _next, _throw, key, arg) {
|
|
4080
4107
|
try {
|
|
4081
4108
|
var info = gen[key](arg);
|
|
@@ -5163,16 +5190,38 @@ function _ts_generator$1(thisArg, body) {
|
|
|
5163
5190
|
* The provider's callback strips the controller prefix from the URL so that
|
|
5164
5191
|
* the provider sees paths relative to its issuer (e.g., `/auth` instead of `/oidc/auth`).
|
|
5165
5192
|
*/ exports.OidcProviderController = /*#__PURE__*/ function() {
|
|
5166
|
-
function OidcProviderController(oidcService) {
|
|
5193
|
+
function OidcProviderController(oidcService, oidcProviderConfigService) {
|
|
5167
5194
|
_class_call_check$1(this, OidcProviderController);
|
|
5168
5195
|
_define_property$2(this, "oidcService", void 0);
|
|
5196
|
+
_define_property$2(this, "oidcProviderConfigService", void 0);
|
|
5169
5197
|
_define_property$2(this, "_callback", void 0);
|
|
5170
5198
|
this.oidcService = oidcService;
|
|
5199
|
+
this.oidcProviderConfigService = oidcProviderConfigService;
|
|
5171
5200
|
this._callback = this.oidcService.getProvider().then(function(p) {
|
|
5172
5201
|
return p.callback();
|
|
5173
5202
|
});
|
|
5174
5203
|
}
|
|
5175
5204
|
_create_class$1(OidcProviderController, [
|
|
5205
|
+
{
|
|
5206
|
+
/**
|
|
5207
|
+
* GET /oidc/login/client
|
|
5208
|
+
*
|
|
5209
|
+
* Convenience redirect from the API issuer path back to the frontend app's
|
|
5210
|
+
* OAuth login page. Lets a user who lands on the API host get bounced to the
|
|
5211
|
+
* client-side login UI. Any incoming query string is forwarded so flow params
|
|
5212
|
+
* (e.g., `uid`, `state`) survive the redirect, merged with any params already
|
|
5213
|
+
* baked into `appLoginUrl`.
|
|
5214
|
+
*
|
|
5215
|
+
* @param req - Inbound Express request; only `originalUrl` is used so any incoming query string can be forwarded to the redirect target.
|
|
5216
|
+
* @param res - Express response used to issue the 302 redirect to the configured `appLoginUrl`.
|
|
5217
|
+
*/ key: "redirectToClientLogin",
|
|
5218
|
+
value: function redirectToClientLogin(req, res) {
|
|
5219
|
+
res.redirect(mergeQueryParamsFromOriginalUrl({
|
|
5220
|
+
baseUrl: this.oidcProviderConfigService.appLoginUrl,
|
|
5221
|
+
originalUrl: req.originalUrl
|
|
5222
|
+
}));
|
|
5223
|
+
}
|
|
5224
|
+
},
|
|
5176
5225
|
{
|
|
5177
5226
|
key: "handleOidcRequest",
|
|
5178
5227
|
value: function handleOidcRequest(req, res) {
|
|
@@ -5201,6 +5250,11 @@ function _ts_generator$1(thisArg, body) {
|
|
|
5201
5250
|
]);
|
|
5202
5251
|
return OidcProviderController;
|
|
5203
5252
|
}();
|
|
5253
|
+
__decorate([
|
|
5254
|
+
common.Get('login/client'),
|
|
5255
|
+
__param(0, common.Req()),
|
|
5256
|
+
__param(1, common.Res())
|
|
5257
|
+
], exports.OidcProviderController.prototype, "redirectToClientLogin", null);
|
|
5204
5258
|
__decorate([
|
|
5205
5259
|
common.All('{*path}'),
|
|
5206
5260
|
__param(0, common.Req()),
|
|
@@ -5208,8 +5262,31 @@ __decorate([
|
|
|
5208
5262
|
], exports.OidcProviderController.prototype, "handleOidcRequest", null);
|
|
5209
5263
|
exports.OidcProviderController = __decorate([
|
|
5210
5264
|
common.Controller('oidc'),
|
|
5211
|
-
__param(0, common.Inject(exports.OidcService))
|
|
5265
|
+
__param(0, common.Inject(exports.OidcService)),
|
|
5266
|
+
__param(1, common.Inject(exports.OidcProviderConfigService))
|
|
5212
5267
|
], exports.OidcProviderController);
|
|
5268
|
+
/**
|
|
5269
|
+
* Merges any query string present on `originalUrl` into `baseUrl`, preserving any params already
|
|
5270
|
+
* baked into `baseUrl`. Bare-string concatenation would produce malformed URLs (`?foo=1?bar=2`)
|
|
5271
|
+
* when `baseUrl` already contains a `?`.
|
|
5272
|
+
*
|
|
5273
|
+
* @param input - The base/original URL pair to merge.
|
|
5274
|
+
* @param input.baseUrl - The destination URL whose query string should be augmented with any incoming params.
|
|
5275
|
+
* @param input.originalUrl - The inbound request URL whose query string is appended onto `baseUrl`.
|
|
5276
|
+
* @returns The base URL with the original URL's query string appended using the appropriate `?`/`&` separator.
|
|
5277
|
+
*/ function mergeQueryParamsFromOriginalUrl(input) {
|
|
5278
|
+
var queryIndex = input.originalUrl.indexOf('?');
|
|
5279
|
+
if (queryIndex < 0) {
|
|
5280
|
+
return input.baseUrl;
|
|
5281
|
+
}
|
|
5282
|
+
var incomingSearch = input.originalUrl.slice(queryIndex + 1);
|
|
5283
|
+
if (incomingSearch.length === 0) {
|
|
5284
|
+
return input.baseUrl;
|
|
5285
|
+
}
|
|
5286
|
+
var baseQueryIndex = input.baseUrl.indexOf('?');
|
|
5287
|
+
var separator = baseQueryIndex < 0 ? '?' : '&';
|
|
5288
|
+
return "".concat(input.baseUrl).concat(separator).concat(incomingSearch);
|
|
5289
|
+
}
|
|
5213
5290
|
|
|
5214
5291
|
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
|
|
5215
5292
|
try {
|
|
@@ -5735,6 +5812,90 @@ function _unsupported_iterable_to_array(o, minLen) {
|
|
|
5735
5812
|
};
|
|
5736
5813
|
}
|
|
5737
5814
|
|
|
5815
|
+
/**
|
|
5816
|
+
* Prefix shared by every callModel OIDC scope (e.g., `model.create`).
|
|
5817
|
+
*
|
|
5818
|
+
* Kept stable so OAuth consent screens render consistent labels and so
|
|
5819
|
+
* future per-resource scopes (e.g., `model.create:profile`) compose cleanly.
|
|
5820
|
+
*/ var CALL_MODEL_OIDC_SCOPE_PREFIX = 'model.';
|
|
5821
|
+
/**
|
|
5822
|
+
* Canonical CRUD scopes enforced on the `callModel` API.
|
|
5823
|
+
*
|
|
5824
|
+
* Each scope corresponds 1:1 to a {@link KnownOnCallFunctionType}; see
|
|
5825
|
+
* {@link CALL_MODEL_OIDC_SCOPE_FOR_CALL_TYPE}.
|
|
5826
|
+
*/ var CALL_MODEL_OIDC_SCOPES = [
|
|
5827
|
+
'model.create',
|
|
5828
|
+
'model.read',
|
|
5829
|
+
'model.update',
|
|
5830
|
+
'model.delete',
|
|
5831
|
+
'model.query'
|
|
5832
|
+
];
|
|
5833
|
+
/**
|
|
5834
|
+
* Maps each known CRUD call type to the scope an OIDC token must carry to invoke it.
|
|
5835
|
+
*/ var CALL_MODEL_OIDC_SCOPE_FOR_CALL_TYPE = {
|
|
5836
|
+
create: 'model.create',
|
|
5837
|
+
read: 'model.read',
|
|
5838
|
+
update: 'model.update',
|
|
5839
|
+
delete: 'model.delete',
|
|
5840
|
+
query: 'model.query'
|
|
5841
|
+
};
|
|
5842
|
+
/**
|
|
5843
|
+
* Resolves the OIDC scope that an OIDC-authenticated caller must hold to invoke
|
|
5844
|
+
* the given callModel `call` type.
|
|
5845
|
+
*
|
|
5846
|
+
* Returns `undefined` for non-CRUD (custom) call types so that scope enforcement
|
|
5847
|
+
* is opt-in for app-specific verbs — apps can still gate them via their own
|
|
5848
|
+
* `preAssert` if needed.
|
|
5849
|
+
*
|
|
5850
|
+
* @param call - The CRUD call type from {@link OnCallTypedModelParams.call}.
|
|
5851
|
+
* @returns The required scope, or `undefined` if `call` is not one of the known CRUD verbs.
|
|
5852
|
+
*/ function callModelOidcScopeForCallType(call) {
|
|
5853
|
+
var result = call == null ? undefined : CALL_MODEL_OIDC_SCOPE_FOR_CALL_TYPE[call];
|
|
5854
|
+
return result;
|
|
5855
|
+
}
|
|
5856
|
+
/**
|
|
5857
|
+
* Error code used when an OIDC-authenticated caller is missing the required
|
|
5858
|
+
* `model.*` scope for a callModel CRUD operation.
|
|
5859
|
+
*/ var CALL_MODEL_MISSING_OIDC_SCOPE_ERROR_CODE = 'CALL_MODEL_MISSING_OIDC_SCOPE';
|
|
5860
|
+
/**
|
|
5861
|
+
* Builds a {@link AssertModelCrudRequestFunction} that rejects callModel requests
|
|
5862
|
+
* lacking the OIDC scope mapped from the call type.
|
|
5863
|
+
*
|
|
5864
|
+
* Bypasses the check (no-op) when the request is not OIDC-authenticated — i.e.
|
|
5865
|
+
* when {@link getOidcScopesFromRequest} returns `undefined` because there is no
|
|
5866
|
+
* `auth.token.scope` claim on the request. Regular Firebase ID-token callers are
|
|
5867
|
+
* unaffected and continue to be gated by auth roles.
|
|
5868
|
+
*
|
|
5869
|
+
* Bypasses the check for custom (non-CRUD) call types so app-specific verbs
|
|
5870
|
+
* remain unrestricted unless an app wires its own assertion.
|
|
5871
|
+
*
|
|
5872
|
+
* Wire as the `preAssert` of {@link onCallModel}.
|
|
5873
|
+
*
|
|
5874
|
+
* @returns The pre-assertion function ready to plug into `OnCallModelConfig.preAssert`.
|
|
5875
|
+
*/ function oidcCallModelScopePreAssert() {
|
|
5876
|
+
var fn = function fn(context) {
|
|
5877
|
+
var requiredScope = callModelOidcScopeForCallType(context.call);
|
|
5878
|
+
var scopes = requiredScope == null ? undefined : getOidcScopesFromRequest(context.request);
|
|
5879
|
+
var isMissingScope = requiredScope != null && scopes != null && !scopes.has(requiredScope);
|
|
5880
|
+
if (isMissingScope) {
|
|
5881
|
+
throw firebaseServer.forbiddenError({
|
|
5882
|
+
status: 403,
|
|
5883
|
+
code: CALL_MODEL_MISSING_OIDC_SCOPE_ERROR_CODE,
|
|
5884
|
+
message: "Missing required OIDC scope for callModel: ".concat(requiredScope),
|
|
5885
|
+
data: {
|
|
5886
|
+
requiredScope: requiredScope,
|
|
5887
|
+
call: context.call
|
|
5888
|
+
}
|
|
5889
|
+
});
|
|
5890
|
+
}
|
|
5891
|
+
};
|
|
5892
|
+
return fn;
|
|
5893
|
+
}
|
|
5894
|
+
|
|
5895
|
+
exports.CALL_MODEL_MISSING_OIDC_SCOPE_ERROR_CODE = CALL_MODEL_MISSING_OIDC_SCOPE_ERROR_CODE;
|
|
5896
|
+
exports.CALL_MODEL_OIDC_SCOPES = CALL_MODEL_OIDC_SCOPES;
|
|
5897
|
+
exports.CALL_MODEL_OIDC_SCOPE_FOR_CALL_TYPE = CALL_MODEL_OIDC_SCOPE_FOR_CALL_TYPE;
|
|
5898
|
+
exports.CALL_MODEL_OIDC_SCOPE_PREFIX = CALL_MODEL_OIDC_SCOPE_PREFIX;
|
|
5738
5899
|
exports.DEFAULT_APP_OAUTH_CONSENT_PATH_PART = DEFAULT_APP_OAUTH_CONSENT_PATH_PART;
|
|
5739
5900
|
exports.DEFAULT_APP_OAUTH_INTERACTION_PATH = DEFAULT_APP_OAUTH_INTERACTION_PATH;
|
|
5740
5901
|
exports.DEFAULT_APP_OAUTH_LOGIN_PATH_PART = DEFAULT_APP_OAUTH_LOGIN_PATH_PART;
|
|
@@ -5765,15 +5926,18 @@ exports.OidcServerFirestoreCollections = OidcServerFirestoreCollections;
|
|
|
5765
5926
|
exports.activeJwksKeysQuery = activeJwksKeysQuery;
|
|
5766
5927
|
exports.appOidcModelModuleMetadata = appOidcModelModuleMetadata;
|
|
5767
5928
|
exports.applyOidcAuthMiddleware = applyOidcAuthMiddleware;
|
|
5929
|
+
exports.callModelOidcScopeForCallType = callModelOidcScopeForCallType;
|
|
5768
5930
|
exports.createAdapterFactory = createAdapterFactory;
|
|
5769
5931
|
exports.createOidcClientFactory = createOidcClientFactory;
|
|
5770
5932
|
exports.deleteOidcClientFactory = deleteOidcClientFactory;
|
|
5933
|
+
exports.getOidcScopesFromRequest = getOidcScopesFromRequest;
|
|
5771
5934
|
exports.jwksKeyCollectionReference = jwksKeyCollectionReference;
|
|
5772
5935
|
exports.jwksKeyConverter = jwksKeyConverter;
|
|
5773
5936
|
exports.jwksKeyFirestoreCollection = jwksKeyFirestoreCollection;
|
|
5774
5937
|
exports.jwksKeyIdentity = jwksKeyIdentity;
|
|
5775
5938
|
exports.jwksKeysWithStatusQuery = jwksKeysWithStatusQuery;
|
|
5776
5939
|
exports.nonRetiredJwksKeysQuery = nonRetiredJwksKeysQuery;
|
|
5940
|
+
exports.oidcCallModelScopePreAssert = oidcCallModelScopePreAssert;
|
|
5777
5941
|
exports.oidcFirestoreCollectionsFactory = oidcFirestoreCollectionsFactory;
|
|
5778
5942
|
exports.oidcModelServerActions = oidcModelServerActions;
|
|
5779
5943
|
exports.oidcModelServerActionsFactory = oidcModelServerActionsFactory;
|
package/oidc/index.esm.js
CHANGED
|
@@ -3,7 +3,7 @@ import { generateKeyPairSync, randomBytes } from 'node:crypto';
|
|
|
3
3
|
import { resolveEncryptionKey, encryptValue, decryptValue, createAesStringEncryptionProvider, isValidAES256GCMEncryptionSecret } from '@dereekb/nestjs';
|
|
4
4
|
import { where, iterateFirestoreDocumentSnapshotPairs, firestoreModelIdentity, snapshotConverterFunctions, optionalFirestoreDate, firestoreDate, firestoreEnum, firestorePassThroughField, AbstractFirestoreDocument, createOidcClientParamsType, deleteOidcClientParamsType, rotateOidcClientSecretParamsType, updateOidcClientParamsType, firestoreModelKey, oidcEntryIdentity, OIDC_ENTRY_CLIENT_TYPE, oidcEntriesByUserCodeQuery, oidcEntriesByUidQuery, oidcEntriesByGrantIdQuery, oidcEntryFirestoreCollection } from '@dereekb/firebase';
|
|
5
5
|
import { cachedGetter, unixDateTimeSecondsNumberToDate, unixDateTimeSecondsNumberForNow, selectiveFieldEncryptor, filterUndefinedValues, websiteUrlFromPaths, firstValue, hasHttpPrefix } from '@dereekb/util';
|
|
6
|
-
import { firestoreEncryptedField, firebaseServerActionsContext, FirebaseServerEnvService, FIREBASE_FIRESTORE_CONTEXT_TOKEN, FirebaseServerFirestoreContextModule } from '@dereekb/firebase-server';
|
|
6
|
+
import { firestoreEncryptedField, firebaseServerActionsContext, FirebaseServerEnvService, FIREBASE_FIRESTORE_CONTEXT_TOKEN, FirebaseServerFirestoreContextModule, forbiddenError } from '@dereekb/firebase-server';
|
|
7
7
|
import { nanoid } from 'nanoid';
|
|
8
8
|
import { makeUrlSearchParamsString } from '@dereekb/util/fetch';
|
|
9
9
|
import { ConfigService, ConfigModule } from '@nestjs/config';
|
|
@@ -4074,6 +4074,33 @@ var _logger = new Logger('applyOidcAuthMiddleware');
|
|
|
4074
4074
|
_logger.debug("Applied OAuth bearer token middleware for paths: ".concat(protectedPaths.join(', ')));
|
|
4075
4075
|
}
|
|
4076
4076
|
|
|
4077
|
+
// MARK: Scopes
|
|
4078
|
+
/**
|
|
4079
|
+
* Reads the set of OIDC scopes carried by a callable request's bearer token.
|
|
4080
|
+
*
|
|
4081
|
+
* The `ModelApiCallModelDispatchService` builds a synthetic `CallableRequest` with
|
|
4082
|
+
* `auth.token = oidcValidatedToken ?? {}`, so the OIDC scope string lives at
|
|
4083
|
+
* `request.auth.token.scope` for OIDC callers and is `undefined` for non-OIDC
|
|
4084
|
+
* (regular Firebase ID-token) callers.
|
|
4085
|
+
*
|
|
4086
|
+
* Returning `undefined` for non-OIDC callers lets callers (e.g. `oidcCallModelScopePreAssert`)
|
|
4087
|
+
* skip scope enforcement instead of falsely treating the missing claim as "no scopes granted".
|
|
4088
|
+
*
|
|
4089
|
+
* The parameter is intentionally typed as `unknown` because `DecodedIdToken` (Firebase
|
|
4090
|
+
* Auth's typed token shape) does not declare a `scope` field, while OIDC bearer-token
|
|
4091
|
+
* callers attach one at runtime. Reading is done defensively.
|
|
4092
|
+
*
|
|
4093
|
+
* @param request - The callable request as seen inside an `onCallModel` handler.
|
|
4094
|
+
* @returns A `Set<string>` of granted scopes, or `undefined` when the request carries no OIDC `scope` claim.
|
|
4095
|
+
*/ function getOidcScopesFromRequest(request) {
|
|
4096
|
+
var _request_auth_token, _request_auth;
|
|
4097
|
+
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;
|
|
4098
|
+
var result = typeof scope === 'string' ? new Set(scope.split(' ').filter(function(value) {
|
|
4099
|
+
return value.length > 0;
|
|
4100
|
+
})) : undefined;
|
|
4101
|
+
return result;
|
|
4102
|
+
}
|
|
4103
|
+
|
|
4077
4104
|
function asyncGeneratorStep$3(gen, resolve, reject, _next, _throw, key, arg) {
|
|
4078
4105
|
try {
|
|
4079
4106
|
var info = gen[key](arg);
|
|
@@ -5161,16 +5188,38 @@ function _ts_generator$1(thisArg, body) {
|
|
|
5161
5188
|
* The provider's callback strips the controller prefix from the URL so that
|
|
5162
5189
|
* the provider sees paths relative to its issuer (e.g., `/auth` instead of `/oidc/auth`).
|
|
5163
5190
|
*/ var OidcProviderController = /*#__PURE__*/ function() {
|
|
5164
|
-
function OidcProviderController(oidcService) {
|
|
5191
|
+
function OidcProviderController(oidcService, oidcProviderConfigService) {
|
|
5165
5192
|
_class_call_check$1(this, OidcProviderController);
|
|
5166
5193
|
_define_property$2(this, "oidcService", void 0);
|
|
5194
|
+
_define_property$2(this, "oidcProviderConfigService", void 0);
|
|
5167
5195
|
_define_property$2(this, "_callback", void 0);
|
|
5168
5196
|
this.oidcService = oidcService;
|
|
5197
|
+
this.oidcProviderConfigService = oidcProviderConfigService;
|
|
5169
5198
|
this._callback = this.oidcService.getProvider().then(function(p) {
|
|
5170
5199
|
return p.callback();
|
|
5171
5200
|
});
|
|
5172
5201
|
}
|
|
5173
5202
|
_create_class$1(OidcProviderController, [
|
|
5203
|
+
{
|
|
5204
|
+
/**
|
|
5205
|
+
* GET /oidc/login/client
|
|
5206
|
+
*
|
|
5207
|
+
* Convenience redirect from the API issuer path back to the frontend app's
|
|
5208
|
+
* OAuth login page. Lets a user who lands on the API host get bounced to the
|
|
5209
|
+
* client-side login UI. Any incoming query string is forwarded so flow params
|
|
5210
|
+
* (e.g., `uid`, `state`) survive the redirect, merged with any params already
|
|
5211
|
+
* baked into `appLoginUrl`.
|
|
5212
|
+
*
|
|
5213
|
+
* @param req - Inbound Express request; only `originalUrl` is used so any incoming query string can be forwarded to the redirect target.
|
|
5214
|
+
* @param res - Express response used to issue the 302 redirect to the configured `appLoginUrl`.
|
|
5215
|
+
*/ key: "redirectToClientLogin",
|
|
5216
|
+
value: function redirectToClientLogin(req, res) {
|
|
5217
|
+
res.redirect(mergeQueryParamsFromOriginalUrl({
|
|
5218
|
+
baseUrl: this.oidcProviderConfigService.appLoginUrl,
|
|
5219
|
+
originalUrl: req.originalUrl
|
|
5220
|
+
}));
|
|
5221
|
+
}
|
|
5222
|
+
},
|
|
5174
5223
|
{
|
|
5175
5224
|
key: "handleOidcRequest",
|
|
5176
5225
|
value: function handleOidcRequest(req, res) {
|
|
@@ -5199,6 +5248,11 @@ function _ts_generator$1(thisArg, body) {
|
|
|
5199
5248
|
]);
|
|
5200
5249
|
return OidcProviderController;
|
|
5201
5250
|
}();
|
|
5251
|
+
__decorate([
|
|
5252
|
+
Get('login/client'),
|
|
5253
|
+
__param(0, Req()),
|
|
5254
|
+
__param(1, Res())
|
|
5255
|
+
], OidcProviderController.prototype, "redirectToClientLogin", null);
|
|
5202
5256
|
__decorate([
|
|
5203
5257
|
All('{*path}'),
|
|
5204
5258
|
__param(0, Req()),
|
|
@@ -5206,8 +5260,31 @@ __decorate([
|
|
|
5206
5260
|
], OidcProviderController.prototype, "handleOidcRequest", null);
|
|
5207
5261
|
OidcProviderController = __decorate([
|
|
5208
5262
|
Controller('oidc'),
|
|
5209
|
-
__param(0, Inject(OidcService))
|
|
5263
|
+
__param(0, Inject(OidcService)),
|
|
5264
|
+
__param(1, Inject(OidcProviderConfigService))
|
|
5210
5265
|
], OidcProviderController);
|
|
5266
|
+
/**
|
|
5267
|
+
* Merges any query string present on `originalUrl` into `baseUrl`, preserving any params already
|
|
5268
|
+
* baked into `baseUrl`. Bare-string concatenation would produce malformed URLs (`?foo=1?bar=2`)
|
|
5269
|
+
* when `baseUrl` already contains a `?`.
|
|
5270
|
+
*
|
|
5271
|
+
* @param input - The base/original URL pair to merge.
|
|
5272
|
+
* @param input.baseUrl - The destination URL whose query string should be augmented with any incoming params.
|
|
5273
|
+
* @param input.originalUrl - The inbound request URL whose query string is appended onto `baseUrl`.
|
|
5274
|
+
* @returns The base URL with the original URL's query string appended using the appropriate `?`/`&` separator.
|
|
5275
|
+
*/ function mergeQueryParamsFromOriginalUrl(input) {
|
|
5276
|
+
var queryIndex = input.originalUrl.indexOf('?');
|
|
5277
|
+
if (queryIndex < 0) {
|
|
5278
|
+
return input.baseUrl;
|
|
5279
|
+
}
|
|
5280
|
+
var incomingSearch = input.originalUrl.slice(queryIndex + 1);
|
|
5281
|
+
if (incomingSearch.length === 0) {
|
|
5282
|
+
return input.baseUrl;
|
|
5283
|
+
}
|
|
5284
|
+
var baseQueryIndex = input.baseUrl.indexOf('?');
|
|
5285
|
+
var separator = baseQueryIndex < 0 ? '?' : '&';
|
|
5286
|
+
return "".concat(input.baseUrl).concat(separator).concat(incomingSearch);
|
|
5287
|
+
}
|
|
5211
5288
|
|
|
5212
5289
|
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
|
|
5213
5290
|
try {
|
|
@@ -5733,4 +5810,84 @@ function _unsupported_iterable_to_array(o, minLen) {
|
|
|
5733
5810
|
};
|
|
5734
5811
|
}
|
|
5735
5812
|
|
|
5736
|
-
|
|
5813
|
+
/**
|
|
5814
|
+
* Prefix shared by every callModel OIDC scope (e.g., `model.create`).
|
|
5815
|
+
*
|
|
5816
|
+
* Kept stable so OAuth consent screens render consistent labels and so
|
|
5817
|
+
* future per-resource scopes (e.g., `model.create:profile`) compose cleanly.
|
|
5818
|
+
*/ var CALL_MODEL_OIDC_SCOPE_PREFIX = 'model.';
|
|
5819
|
+
/**
|
|
5820
|
+
* Canonical CRUD scopes enforced on the `callModel` API.
|
|
5821
|
+
*
|
|
5822
|
+
* Each scope corresponds 1:1 to a {@link KnownOnCallFunctionType}; see
|
|
5823
|
+
* {@link CALL_MODEL_OIDC_SCOPE_FOR_CALL_TYPE}.
|
|
5824
|
+
*/ var CALL_MODEL_OIDC_SCOPES = [
|
|
5825
|
+
'model.create',
|
|
5826
|
+
'model.read',
|
|
5827
|
+
'model.update',
|
|
5828
|
+
'model.delete',
|
|
5829
|
+
'model.query'
|
|
5830
|
+
];
|
|
5831
|
+
/**
|
|
5832
|
+
* Maps each known CRUD call type to the scope an OIDC token must carry to invoke it.
|
|
5833
|
+
*/ var CALL_MODEL_OIDC_SCOPE_FOR_CALL_TYPE = {
|
|
5834
|
+
create: 'model.create',
|
|
5835
|
+
read: 'model.read',
|
|
5836
|
+
update: 'model.update',
|
|
5837
|
+
delete: 'model.delete',
|
|
5838
|
+
query: 'model.query'
|
|
5839
|
+
};
|
|
5840
|
+
/**
|
|
5841
|
+
* Resolves the OIDC scope that an OIDC-authenticated caller must hold to invoke
|
|
5842
|
+
* the given callModel `call` type.
|
|
5843
|
+
*
|
|
5844
|
+
* Returns `undefined` for non-CRUD (custom) call types so that scope enforcement
|
|
5845
|
+
* is opt-in for app-specific verbs — apps can still gate them via their own
|
|
5846
|
+
* `preAssert` if needed.
|
|
5847
|
+
*
|
|
5848
|
+
* @param call - The CRUD call type from {@link OnCallTypedModelParams.call}.
|
|
5849
|
+
* @returns The required scope, or `undefined` if `call` is not one of the known CRUD verbs.
|
|
5850
|
+
*/ function callModelOidcScopeForCallType(call) {
|
|
5851
|
+
var result = call == null ? undefined : CALL_MODEL_OIDC_SCOPE_FOR_CALL_TYPE[call];
|
|
5852
|
+
return result;
|
|
5853
|
+
}
|
|
5854
|
+
/**
|
|
5855
|
+
* Error code used when an OIDC-authenticated caller is missing the required
|
|
5856
|
+
* `model.*` scope for a callModel CRUD operation.
|
|
5857
|
+
*/ var CALL_MODEL_MISSING_OIDC_SCOPE_ERROR_CODE = 'CALL_MODEL_MISSING_OIDC_SCOPE';
|
|
5858
|
+
/**
|
|
5859
|
+
* Builds a {@link AssertModelCrudRequestFunction} that rejects callModel requests
|
|
5860
|
+
* lacking the OIDC scope mapped from the call type.
|
|
5861
|
+
*
|
|
5862
|
+
* Bypasses the check (no-op) when the request is not OIDC-authenticated — i.e.
|
|
5863
|
+
* when {@link getOidcScopesFromRequest} returns `undefined` because there is no
|
|
5864
|
+
* `auth.token.scope` claim on the request. Regular Firebase ID-token callers are
|
|
5865
|
+
* unaffected and continue to be gated by auth roles.
|
|
5866
|
+
*
|
|
5867
|
+
* Bypasses the check for custom (non-CRUD) call types so app-specific verbs
|
|
5868
|
+
* remain unrestricted unless an app wires its own assertion.
|
|
5869
|
+
*
|
|
5870
|
+
* Wire as the `preAssert` of {@link onCallModel}.
|
|
5871
|
+
*
|
|
5872
|
+
* @returns The pre-assertion function ready to plug into `OnCallModelConfig.preAssert`.
|
|
5873
|
+
*/ function oidcCallModelScopePreAssert() {
|
|
5874
|
+
var fn = function fn(context) {
|
|
5875
|
+
var requiredScope = callModelOidcScopeForCallType(context.call);
|
|
5876
|
+
var scopes = requiredScope == null ? undefined : getOidcScopesFromRequest(context.request);
|
|
5877
|
+
var isMissingScope = requiredScope != null && scopes != null && !scopes.has(requiredScope);
|
|
5878
|
+
if (isMissingScope) {
|
|
5879
|
+
throw forbiddenError({
|
|
5880
|
+
status: 403,
|
|
5881
|
+
code: CALL_MODEL_MISSING_OIDC_SCOPE_ERROR_CODE,
|
|
5882
|
+
message: "Missing required OIDC scope for callModel: ".concat(requiredScope),
|
|
5883
|
+
data: {
|
|
5884
|
+
requiredScope: requiredScope,
|
|
5885
|
+
call: context.call
|
|
5886
|
+
}
|
|
5887
|
+
});
|
|
5888
|
+
}
|
|
5889
|
+
};
|
|
5890
|
+
return fn;
|
|
5891
|
+
}
|
|
5892
|
+
|
|
5893
|
+
export { CALL_MODEL_MISSING_OIDC_SCOPE_ERROR_CODE, CALL_MODEL_OIDC_SCOPES, CALL_MODEL_OIDC_SCOPE_FOR_CALL_TYPE, CALL_MODEL_OIDC_SCOPE_PREFIX, DEFAULT_APP_OAUTH_CONSENT_PATH_PART, DEFAULT_APP_OAUTH_INTERACTION_PATH, DEFAULT_APP_OAUTH_LOGIN_PATH_PART, 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_ROUTES_FOR_GLOBAL_ROUTE_EXCLUDE, JwksFirestoreCollections, JwksKeyDocument, JwksService, JwksServiceConfig, JwksServiceStorageConfig, OIDC_ENCRYPTED_PAYLOAD_FIELDS, OIDC_JWKS_ENCRYPTION_SECRET_ENV_KEY, OidcAccountService, OidcAccountServiceDelegate, OidcAccountServiceUserContext, OidcAuth, OidcAuthBearerTokenMiddleware, OidcAuthMiddlewareConfig, OidcClientService, OidcEncryptionService, OidcInteractionController, OidcInteractionService, OidcModelServerActions, OidcModuleConfig, OidcProviderConfigService, OidcProviderController, OidcServerFirestoreCollections, OidcService, OidcWellKnownController, activeJwksKeysQuery, appOidcModelModuleMetadata, applyOidcAuthMiddleware, callModelOidcScopeForCallType, createAdapterFactory, createOidcClientFactory, deleteOidcClientFactory, getOidcScopesFromRequest, jwksKeyCollectionReference, jwksKeyConverter, jwksKeyFirestoreCollection, jwksKeyIdentity, jwksKeysWithStatusQuery, nonRetiredJwksKeysQuery, oidcCallModelScopePreAssert, oidcFirestoreCollectionsFactory, oidcModelServerActions, oidcModelServerActionsFactory, oidcModuleConfigFactory, oidcModuleMetadata, 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.11.1",
|
|
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.11.1",
|
|
6
|
+
"@dereekb/date": "13.11.1",
|
|
7
|
+
"@dereekb/firebase": "13.11.1",
|
|
8
|
+
"@dereekb/firebase-server": "13.11.1",
|
|
9
|
+
"@dereekb/model": "13.11.1",
|
|
10
|
+
"@dereekb/nestjs": "13.11.1",
|
|
11
|
+
"@dereekb/rxjs": "13.11.1",
|
|
12
|
+
"@dereekb/util": "13.11.1",
|
|
13
|
+
"@dereekb/zoho": "13.11.1",
|
|
14
14
|
"@nestjs/common": "^11.1.19",
|
|
15
15
|
"@nestjs/config": "^4.0.4",
|
|
16
16
|
"express": "^5.2.1",
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type Request, type Response } from 'express';
|
|
2
2
|
import { OidcService } from '../service/oidc.service';
|
|
3
|
+
import { OidcProviderConfigService } from '../service/oidc.config.service';
|
|
3
4
|
/**
|
|
4
5
|
* Catch-all controller that proxies requests to the oidc-provider callback.
|
|
5
6
|
*
|
|
@@ -12,7 +13,21 @@ import { OidcService } from '../service/oidc.service';
|
|
|
12
13
|
*/
|
|
13
14
|
export declare class OidcProviderController {
|
|
14
15
|
private readonly oidcService;
|
|
16
|
+
private readonly oidcProviderConfigService;
|
|
15
17
|
private _callback;
|
|
16
|
-
constructor(oidcService: OidcService);
|
|
18
|
+
constructor(oidcService: OidcService, oidcProviderConfigService: OidcProviderConfigService);
|
|
19
|
+
/**
|
|
20
|
+
* GET /oidc/login/client
|
|
21
|
+
*
|
|
22
|
+
* Convenience redirect from the API issuer path back to the frontend app's
|
|
23
|
+
* OAuth login page. Lets a user who lands on the API host get bounced to the
|
|
24
|
+
* client-side login UI. Any incoming query string is forwarded so flow params
|
|
25
|
+
* (e.g., `uid`, `state`) survive the redirect, merged with any params already
|
|
26
|
+
* baked into `appLoginUrl`.
|
|
27
|
+
*
|
|
28
|
+
* @param req - Inbound Express request; only `originalUrl` is used so any incoming query string can be forwarded to the redirect target.
|
|
29
|
+
* @param res - Express response used to issue the 302 redirect to the configured `appLoginUrl`.
|
|
30
|
+
*/
|
|
31
|
+
redirectToClientLogin(req: Request, res: Response): void;
|
|
17
32
|
handleOidcRequest(req: Request, res: Response): Promise<void>;
|
|
18
33
|
}
|
package/oidc/src/lib/index.d.ts
CHANGED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { type Maybe } from '@dereekb/util';
|
|
2
|
+
import { type KnownOnCallFunctionType, type OnCallFunctionType, type OnCallTypedModelParams } from '@dereekb/firebase';
|
|
3
|
+
import { type AssertModelCrudRequestFunction } from '@dereekb/firebase-server';
|
|
4
|
+
/**
|
|
5
|
+
* Prefix shared by every callModel OIDC scope (e.g., `model.create`).
|
|
6
|
+
*
|
|
7
|
+
* Kept stable so OAuth consent screens render consistent labels and so
|
|
8
|
+
* future per-resource scopes (e.g., `model.create:profile`) compose cleanly.
|
|
9
|
+
*/
|
|
10
|
+
export declare const CALL_MODEL_OIDC_SCOPE_PREFIX = "model.";
|
|
11
|
+
/**
|
|
12
|
+
* Canonical CRUD scopes enforced on the `callModel` API.
|
|
13
|
+
*
|
|
14
|
+
* Each scope corresponds 1:1 to a {@link KnownOnCallFunctionType}; see
|
|
15
|
+
* {@link CALL_MODEL_OIDC_SCOPE_FOR_CALL_TYPE}.
|
|
16
|
+
*/
|
|
17
|
+
export declare const CALL_MODEL_OIDC_SCOPES: readonly ["model.create", "model.read", "model.update", "model.delete", "model.query"];
|
|
18
|
+
/**
|
|
19
|
+
* Union of the five canonical callModel CRUD scope strings.
|
|
20
|
+
*/
|
|
21
|
+
export type CallModelOidcScope = (typeof CALL_MODEL_OIDC_SCOPES)[number];
|
|
22
|
+
/**
|
|
23
|
+
* Maps each known CRUD call type to the scope an OIDC token must carry to invoke it.
|
|
24
|
+
*/
|
|
25
|
+
export declare const CALL_MODEL_OIDC_SCOPE_FOR_CALL_TYPE: Readonly<Record<KnownOnCallFunctionType, CallModelOidcScope>>;
|
|
26
|
+
/**
|
|
27
|
+
* Resolves the OIDC scope that an OIDC-authenticated caller must hold to invoke
|
|
28
|
+
* the given callModel `call` type.
|
|
29
|
+
*
|
|
30
|
+
* Returns `undefined` for non-CRUD (custom) call types so that scope enforcement
|
|
31
|
+
* is opt-in for app-specific verbs — apps can still gate them via their own
|
|
32
|
+
* `preAssert` if needed.
|
|
33
|
+
*
|
|
34
|
+
* @param call - The CRUD call type from {@link OnCallTypedModelParams.call}.
|
|
35
|
+
* @returns The required scope, or `undefined` if `call` is not one of the known CRUD verbs.
|
|
36
|
+
*/
|
|
37
|
+
export declare function callModelOidcScopeForCallType(call: Maybe<OnCallFunctionType>): Maybe<CallModelOidcScope>;
|
|
38
|
+
/**
|
|
39
|
+
* Error code used when an OIDC-authenticated caller is missing the required
|
|
40
|
+
* `model.*` scope for a callModel CRUD operation.
|
|
41
|
+
*/
|
|
42
|
+
export declare const CALL_MODEL_MISSING_OIDC_SCOPE_ERROR_CODE = "CALL_MODEL_MISSING_OIDC_SCOPE";
|
|
43
|
+
/**
|
|
44
|
+
* Builds a {@link AssertModelCrudRequestFunction} that rejects callModel requests
|
|
45
|
+
* lacking the OIDC scope mapped from the call type.
|
|
46
|
+
*
|
|
47
|
+
* Bypasses the check (no-op) when the request is not OIDC-authenticated — i.e.
|
|
48
|
+
* when {@link getOidcScopesFromRequest} returns `undefined` because there is no
|
|
49
|
+
* `auth.token.scope` claim on the request. Regular Firebase ID-token callers are
|
|
50
|
+
* unaffected and continue to be gated by auth roles.
|
|
51
|
+
*
|
|
52
|
+
* Bypasses the check for custom (non-CRUD) call types so app-specific verbs
|
|
53
|
+
* remain unrestricted unless an app wires its own assertion.
|
|
54
|
+
*
|
|
55
|
+
* Wire as the `preAssert` of {@link onCallModel}.
|
|
56
|
+
*
|
|
57
|
+
* @returns The pre-assertion function ready to plug into `OnCallModelConfig.preAssert`.
|
|
58
|
+
*/
|
|
59
|
+
export declare function oidcCallModelScopePreAssert(): AssertModelCrudRequestFunction<unknown, OnCallTypedModelParams>;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type Maybe } from '@dereekb/util';
|
|
1
2
|
import { type OidcEntryClientId } from '@dereekb/firebase';
|
|
2
3
|
import { type FirebaseServerAuthData, type FirebaseServerAuthenticatedRequest } from '@dereekb/firebase-server';
|
|
3
4
|
/**
|
|
@@ -24,3 +25,22 @@ export interface OidcAuthData extends FirebaseServerAuthData {
|
|
|
24
25
|
* successful bearer token verification.
|
|
25
26
|
*/
|
|
26
27
|
export type OidcAuthenticatedRequest = FirebaseServerAuthenticatedRequest<OidcAuthData>;
|
|
28
|
+
/**
|
|
29
|
+
* Reads the set of OIDC scopes carried by a callable request's bearer token.
|
|
30
|
+
*
|
|
31
|
+
* The `ModelApiCallModelDispatchService` builds a synthetic `CallableRequest` with
|
|
32
|
+
* `auth.token = oidcValidatedToken ?? {}`, so the OIDC scope string lives at
|
|
33
|
+
* `request.auth.token.scope` for OIDC callers and is `undefined` for non-OIDC
|
|
34
|
+
* (regular Firebase ID-token) callers.
|
|
35
|
+
*
|
|
36
|
+
* Returning `undefined` for non-OIDC callers lets callers (e.g. `oidcCallModelScopePreAssert`)
|
|
37
|
+
* skip scope enforcement instead of falsely treating the missing claim as "no scopes granted".
|
|
38
|
+
*
|
|
39
|
+
* The parameter is intentionally typed as `unknown` because `DecodedIdToken` (Firebase
|
|
40
|
+
* Auth's typed token shape) does not declare a `scope` field, while OIDC bearer-token
|
|
41
|
+
* callers attach one at runtime. Reading is done defensively.
|
|
42
|
+
*
|
|
43
|
+
* @param request - The callable request as seen inside an `onCallModel` handler.
|
|
44
|
+
* @returns A `Set<string>` of granted scopes, or `undefined` when the request carries no OIDC `scope` claim.
|
|
45
|
+
*/
|
|
46
|
+
export declare function getOidcScopesFromRequest(request: unknown): Maybe<Set<string>>;
|