@dereekb/firebase-server 13.11.18 → 13.12.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/index.cjs.js +218 -31
- package/index.esm.js +217 -34
- package/mailgun/package.json +9 -9
- package/mcp/index.cjs.default.js +1 -0
- package/mcp/index.cjs.js +3646 -0
- package/mcp/index.cjs.mjs +2 -0
- package/mcp/index.d.ts +1 -0
- package/mcp/index.esm.js +3609 -0
- package/mcp/package.json +33 -0
- package/mcp/src/index.d.ts +1 -0
- package/mcp/src/lib/controller/index.d.ts +2 -0
- package/mcp/src/lib/controller/mcp.controller.d.ts +19 -0
- package/mcp/src/lib/controller/mcp.wellknown.controller.d.ts +25 -0
- package/mcp/src/lib/index.d.ts +5 -0
- package/mcp/src/lib/mcp.config.d.ts +100 -0
- package/mcp/src/lib/mcp.module.d.ts +49 -0
- package/mcp/src/lib/service/index.d.ts +8 -0
- package/mcp/src/lib/service/mcp.manifest.d.ts +150 -0
- package/mcp/src/lib/service/mcp.response-formatter.d.ts +36 -0
- package/mcp/src/lib/service/mcp.server.factory.d.ts +124 -0
- package/mcp/src/lib/service/mcp.tool-generator.d.ts +235 -0
- package/mcp/src/lib/service/mcp.visibility.d.ts +99 -0
- package/mcp/src/lib/service/tools/mcp.tool.model-decode.d.ts +87 -0
- package/mcp/src/lib/service/tools/mcp.tool.model-get.d.ts +81 -0
- package/mcp/src/lib/service/tools/mcp.tool.model-info.d.ts +84 -0
- package/mcp/src/lib/service/tools/mcp.tool.whoami.d.ts +72 -0
- package/mcp/src/lib/transport/index.d.ts +1 -0
- package/mcp/src/lib/transport/streamable-http.transport.d.ts +21 -0
- package/model/index.cjs.js +639 -266
- package/model/index.esm.js +640 -270
- package/model/package.json +9 -9
- package/model/src/lib/storagefile/extension/compress.pdf.d.ts +29 -1
- package/model/src/lib/storagefile/index.d.ts +1 -0
- package/model/src/lib/storagefile/storagefile.action.server.d.ts +29 -2
- package/model/src/lib/storagefile/storagefile.mcp.d.ts +32 -0
- package/model/src/lib/storagefile/storagefile.module.d.ts +20 -3
- package/oidc/index.cjs.js +322 -147
- package/oidc/index.esm.js +321 -148
- package/oidc/package.json +10 -10
- package/oidc/src/lib/controller/oidc.wellknown.controller.d.ts +6 -15
- package/oidc/src/lib/middleware/oauth-auth.middleware.d.ts +22 -2
- package/oidc/src/lib/middleware/oauth-auth.module.d.ts +57 -0
- package/oidc/src/lib/oidc.config.d.ts +73 -0
- package/oidc/src/lib/oidc.module.d.ts +44 -2
- package/package.json +22 -10
- package/src/lib/auth/auth.service.d.ts +16 -1
- package/src/lib/auth/auth.service.error.util.d.ts +24 -5
- package/src/lib/env/env.config.d.ts +16 -0
- package/src/lib/env/env.service.d.ts +9 -0
- package/src/lib/nest/controller/model/model.api.get.service.d.ts +35 -1
- package/src/lib/nest/env/env.service.d.ts +1 -0
- package/src/lib/nest/model/api.details.d.ts +116 -5
- package/src/lib/nest/model/crud.assert.function.d.ts +1 -1
- package/src/lib/nest/model/index.d.ts +1 -0
- package/src/lib/nest/model/invoke.model.function.d.ts +89 -0
- package/test/package.json +11 -11
- package/twilio/LICENSE +21 -0
- package/twilio/index.cjs.default.js +1 -0
- package/twilio/index.cjs.js +404 -0
- package/twilio/index.cjs.mjs +2 -0
- package/twilio/index.d.ts +1 -0
- package/twilio/index.esm.js +398 -0
- package/twilio/package.json +25 -0
- package/twilio/src/index.d.ts +1 -0
- package/twilio/src/lib/index.d.ts +1 -0
- package/twilio/src/lib/notification.send.service.twilio.d.ts +148 -0
- package/zoho/package.json +9 -9
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { JwksService } from '../service/oidc.jwks.service';
|
|
2
|
-
import { OidcModuleConfig } from '../oidc.config';
|
|
3
2
|
import { OidcProviderConfigService, type OidcDiscoveryMetadata } from '../service/oidc.config.service';
|
|
4
3
|
/**
|
|
5
|
-
* Controller for
|
|
4
|
+
* Controller for OIDC discovery and JWKS endpoints under `/.well-known`.
|
|
5
|
+
*
|
|
6
|
+
* The protected-resource discovery doc (`/.well-known/oauth-protected-resource`)
|
|
7
|
+
* is owned by the protected resource itself (the MCP module's
|
|
8
|
+
* `McpWellKnownController`), not the authorization server, per RFC 9728.
|
|
6
9
|
*/
|
|
7
10
|
export declare class OidcWellKnownController {
|
|
8
|
-
private readonly config;
|
|
9
11
|
private readonly providerConfigService;
|
|
10
12
|
private readonly jwksService;
|
|
11
|
-
constructor(
|
|
13
|
+
constructor(providerConfigService: OidcProviderConfigService, jwksService: JwksService);
|
|
12
14
|
/**
|
|
13
15
|
* OpenID Connect Discovery endpoint (RFC 8414 / OpenID Connect Discovery 1.0).
|
|
14
16
|
*
|
|
@@ -28,15 +30,4 @@ export declare class OidcWellKnownController {
|
|
|
28
30
|
getJwks(): Promise<{
|
|
29
31
|
keys: import("..").JsonWebKeyWithKid[];
|
|
30
32
|
}>;
|
|
31
|
-
/**
|
|
32
|
-
* OAuth Protected Resource discovery endpoint (RFC 8707).
|
|
33
|
-
*
|
|
34
|
-
* Returns the authorization server(s) that protect this resource,
|
|
35
|
-
* allowing clients to discover which authorization server to use.
|
|
36
|
-
*
|
|
37
|
-
* @returns The protected resource metadata with authorization server URLs.
|
|
38
|
-
*/
|
|
39
|
-
getProtectedResource(): {
|
|
40
|
-
authorization_servers: string[];
|
|
41
|
-
};
|
|
42
33
|
}
|
|
@@ -2,6 +2,7 @@ import { type NestMiddleware } from '@nestjs/common';
|
|
|
2
2
|
import { type Response, type NextFunction } from 'express';
|
|
3
3
|
import { OidcService } from '../service/oidc.service';
|
|
4
4
|
import { type OidcAuthenticatedRequest } from '../service/oidc.auth';
|
|
5
|
+
import { OidcAuthMiddlewareConfig } from './oauth-auth.module';
|
|
5
6
|
/**
|
|
6
7
|
* NestJS middleware that verifies OAuth bearer tokens issued by our OIDC provider.
|
|
7
8
|
*
|
|
@@ -9,13 +10,32 @@ import { type OidcAuthenticatedRequest } from '../service/oidc.auth';
|
|
|
9
10
|
* verifies it via the provider's AccessToken model, and attaches the
|
|
10
11
|
* auth context to the request as {@link OidcAuthData}.
|
|
11
12
|
*
|
|
13
|
+
* On 401, emits an RFC 9728 `WWW-Authenticate: Bearer ...` header — including
|
|
14
|
+
* the `resource_metadata` discovery URL when `OidcAuthMiddlewareConfig.resourceMetadataUrl`
|
|
15
|
+
* is configured — so OAuth-aware clients (Claude, mcp-inspector, etc.) can
|
|
16
|
+
* locate the discovery doc explicitly.
|
|
17
|
+
*
|
|
12
18
|
* Applied to routes via {@link ConfigureOidcAuthMiddlewareModule}.
|
|
13
19
|
*
|
|
20
|
+
* @example
|
|
21
|
+
* Wired automatically when `protectedPaths` is set on `oidcModuleMetadata`:
|
|
22
|
+
* ```ts
|
|
23
|
+
* oidcModuleMetadata({
|
|
24
|
+
* dependencyModule: MyOidcDependencyModule,
|
|
25
|
+
* config: {
|
|
26
|
+
* protectedPaths: ['/api/model', '/mcp'],
|
|
27
|
+
* resourceMetadataUrl: 'https://api.example.com/.well-known/oauth-protected-resource'
|
|
28
|
+
* }
|
|
29
|
+
* })
|
|
30
|
+
* ```
|
|
31
|
+
*
|
|
14
32
|
* @throws {UnauthorizedException} When the Authorization header is missing, malformed, or the token is invalid/expired.
|
|
15
33
|
*/
|
|
16
34
|
export declare class OidcAuthBearerTokenMiddleware implements NestMiddleware {
|
|
17
35
|
private readonly oidcService;
|
|
36
|
+
private readonly config;
|
|
18
37
|
private readonly logger;
|
|
19
|
-
constructor(oidcService: OidcService);
|
|
20
|
-
use(req: OidcAuthenticatedRequest,
|
|
38
|
+
constructor(oidcService: OidcService, config: OidcAuthMiddlewareConfig);
|
|
39
|
+
use(req: OidcAuthenticatedRequest, res: Response, next: NextFunction): Promise<void>;
|
|
40
|
+
private _setWwwAuthenticate;
|
|
21
41
|
}
|
|
@@ -6,6 +6,23 @@ import { type SlashPath } from '@dereekb/util';
|
|
|
6
6
|
* Works in reverse of `FirebaseAppCheckMiddlewareConfig`: instead of protecting
|
|
7
7
|
* all routes and ignoring some, this only protects explicitly specified paths.
|
|
8
8
|
* Routes under the global API prefix (protected by AppCheck) are excluded.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* Manual configuration without going through `oidcModuleMetadata`:
|
|
12
|
+
* ```ts
|
|
13
|
+
* @Module({
|
|
14
|
+
* providers: [
|
|
15
|
+
* {
|
|
16
|
+
* provide: OidcAuthMiddlewareConfig,
|
|
17
|
+
* useValue: {
|
|
18
|
+
* protectedPaths: ['/api/model', '/mcp'],
|
|
19
|
+
* resourceMetadataUrl: 'https://api.example.com/.well-known/oauth-protected-resource'
|
|
20
|
+
* } satisfies OidcAuthMiddlewareConfig
|
|
21
|
+
* }
|
|
22
|
+
* ]
|
|
23
|
+
* })
|
|
24
|
+
* export class MyApiModule {}
|
|
25
|
+
* ```
|
|
9
26
|
*/
|
|
10
27
|
export declare abstract class OidcAuthMiddlewareConfig {
|
|
11
28
|
/**
|
|
@@ -16,7 +33,30 @@ export declare abstract class OidcAuthMiddlewareConfig {
|
|
|
16
33
|
* since those are protected by AppCheck.
|
|
17
34
|
*/
|
|
18
35
|
readonly protectedPaths: SlashPath[];
|
|
36
|
+
/**
|
|
37
|
+
* Absolute URL of the OAuth 2.0 Protected Resource Metadata document
|
|
38
|
+
* (RFC 9728). When set, included as the `resource_metadata` parameter
|
|
39
|
+
* of the `WWW-Authenticate: Bearer` header on 401 responses so OAuth
|
|
40
|
+
* clients can locate the discovery doc explicitly rather than relying on
|
|
41
|
+
* origin-rooted path-walkback (which fails when the function isn't
|
|
42
|
+
* mounted at `/` — e.g. behind the Firebase Functions emulator URL prefix).
|
|
43
|
+
*/
|
|
44
|
+
readonly resourceMetadataUrl?: string;
|
|
19
45
|
}
|
|
46
|
+
/**
|
|
47
|
+
* Builds the `WWW-Authenticate: Bearer ...` challenge string emitted on 401
|
|
48
|
+
* responses to OAuth-protected routes.
|
|
49
|
+
*
|
|
50
|
+
* Per RFC 6750 §3 / RFC 7235, auth-params are comma-separated (optionally
|
|
51
|
+
* with surrounding whitespace). When `resourceMetadataUrl` is provided, it's
|
|
52
|
+
* included as the RFC 9728 `resource_metadata` hint so clients can locate the
|
|
53
|
+
* discovery doc directly instead of relying on origin-rooted path-walkback.
|
|
54
|
+
*
|
|
55
|
+
* @param error - The RFC 6750 `error` token (e.g. `invalid_token`, `invalid_request`).
|
|
56
|
+
* @param resourceMetadataUrl - Optional protected-resource metadata URL.
|
|
57
|
+
* @returns Header value, e.g. `Bearer resource_metadata="…", error="invalid_token"`.
|
|
58
|
+
*/
|
|
59
|
+
export declare function buildBearerChallenge(error: string, resourceMetadataUrl?: string): string;
|
|
20
60
|
/**
|
|
21
61
|
* Applies OAuth bearer token verification as global Express middleware on
|
|
22
62
|
* the given NestJS application.
|
|
@@ -39,5 +79,22 @@ export declare abstract class OidcAuthMiddlewareConfig {
|
|
|
39
79
|
* }
|
|
40
80
|
* };
|
|
41
81
|
* ```
|
|
82
|
+
*
|
|
83
|
+
* Pair with `oidcModuleMetadata` (passing `resourceMetadataUrl` on `config`) so
|
|
84
|
+
* the 401 emits an RFC 9728 `WWW-Authenticate: Bearer resource_metadata="..."`
|
|
85
|
+
* header — required when the resource server isn't mounted at the origin root
|
|
86
|
+
* (e.g. behind the Firebase Functions emulator URL prefix), since RFC 9728's
|
|
87
|
+
* default path-walkback lands at a 404 in that case.
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* ```ts
|
|
91
|
+
* oidcModuleMetadata({
|
|
92
|
+
* dependencyModule: MyOidcDependencyModule,
|
|
93
|
+
* config: {
|
|
94
|
+
* protectedPaths: ['/api/model', '/mcp'],
|
|
95
|
+
* resourceMetadataUrl: 'http://localhost:9902/dereekb-components/us-central1/api/.well-known/oauth-protected-resource'
|
|
96
|
+
* }
|
|
97
|
+
* })
|
|
98
|
+
* ```
|
|
42
99
|
*/
|
|
43
100
|
export declare function applyOidcAuthMiddleware(nestApp: INestApplication): void;
|
|
@@ -9,6 +9,37 @@ import { type JwksKeyConverterConfig } from './model';
|
|
|
9
9
|
* Matches the `renderError` option from the oidc-provider `Configuration` type.
|
|
10
10
|
*/
|
|
11
11
|
export type OidcRenderErrorFunction = Configuration['renderError'];
|
|
12
|
+
/**
|
|
13
|
+
* Per-resource configuration returned by `features.resourceIndicators.getResourceServerInfo`
|
|
14
|
+
* to oidc-provider when a client requests an access token bound to a specific resource
|
|
15
|
+
* (RFC 8707).
|
|
16
|
+
*
|
|
17
|
+
* Shape mirrors oidc-provider's `ResourceServer` documented at
|
|
18
|
+
* `features.resourceIndicators.getResourceServerInfo` in
|
|
19
|
+
* `node_modules/oidc-provider/lib/helpers/defaults.js`.
|
|
20
|
+
*/
|
|
21
|
+
export interface OidcResourceServerInfo {
|
|
22
|
+
/**
|
|
23
|
+
* Space-delimited scopes valid on this resource server. Issued access tokens
|
|
24
|
+
* are filtered to the intersection of the client-requested scopes and this
|
|
25
|
+
* allow-list.
|
|
26
|
+
*/
|
|
27
|
+
readonly scope: string;
|
|
28
|
+
/**
|
|
29
|
+
* `aud` claim placed on tokens bound to this resource. Defaults to the
|
|
30
|
+
* resource indicator URL when omitted.
|
|
31
|
+
*/
|
|
32
|
+
readonly audience?: string;
|
|
33
|
+
/**
|
|
34
|
+
* Access-token TTL override for this resource (seconds). Falls back to the
|
|
35
|
+
* provider's `ttl.AccessToken` when omitted.
|
|
36
|
+
*/
|
|
37
|
+
readonly accessTokenTTL?: number;
|
|
38
|
+
/**
|
|
39
|
+
* Access-token format. Defaults to `'opaque'`.
|
|
40
|
+
*/
|
|
41
|
+
readonly accessTokenFormat?: 'opaque' | 'jwt';
|
|
42
|
+
}
|
|
12
43
|
/**
|
|
13
44
|
* OIDC provider-level configuration for scopes, grant types, response types,
|
|
14
45
|
* and claim mappings. These values drive both the oidc-provider instance and the
|
|
@@ -193,6 +224,48 @@ export declare abstract class OidcModuleConfig {
|
|
|
193
224
|
* since those are typically protected by AppCheck.
|
|
194
225
|
*/
|
|
195
226
|
readonly protectedPaths?: SlashPath[];
|
|
227
|
+
/**
|
|
228
|
+
* Map of recognized OAuth resource indicator URLs (RFC 8707) to their
|
|
229
|
+
* {@link OidcResourceServerInfo}. When non-empty, oidc-provider's
|
|
230
|
+
* `features.resourceIndicators.getResourceServerInfo` is wired to look up
|
|
231
|
+
* each requested `resource` parameter against this map and reject unknown
|
|
232
|
+
* resources with `invalid_target`.
|
|
233
|
+
*
|
|
234
|
+
* Required for OAuth-aware MCP clients (Claude, mcp-inspector) — they pass
|
|
235
|
+
* the MCP URL from the protected-resource discovery doc as the `resource`
|
|
236
|
+
* parameter on `/authorize` and `/token`, and oidc-provider rejects every
|
|
237
|
+
* such request with `invalid_target` unless that URL is recognized here.
|
|
238
|
+
*
|
|
239
|
+
* @example
|
|
240
|
+
* ```ts
|
|
241
|
+
* resourceServers: {
|
|
242
|
+
* 'http://localhost:9902/dereekb-components/us-central1/api/mcp': {
|
|
243
|
+
* scope: 'openid profile email offline_access demo model.create model.read model.update model.delete model.query model.invoke',
|
|
244
|
+
* audience: 'http://localhost:9902/dereekb-components/us-central1/api/mcp'
|
|
245
|
+
* }
|
|
246
|
+
* }
|
|
247
|
+
* ```
|
|
248
|
+
*/
|
|
249
|
+
readonly resourceServers?: Readonly<Record<string, OidcResourceServerInfo>>;
|
|
250
|
+
/**
|
|
251
|
+
* Absolute URL of the OAuth 2.0 Protected Resource Metadata document
|
|
252
|
+
* (RFC 9728) for the resources guarded by {@link protectedPaths}.
|
|
253
|
+
*
|
|
254
|
+
* When set, the OIDC bearer middleware emits
|
|
255
|
+
* `WWW-Authenticate: Bearer resource_metadata="<url>", error="invalid_token"`
|
|
256
|
+
* on 401 responses, so OAuth-aware clients (Claude, mcp-inspector) can
|
|
257
|
+
* locate the discovery doc without relying on RFC 9728's origin-rooted
|
|
258
|
+
* path-walkback rule — which only works when the resource server is
|
|
259
|
+
* actually mounted at the origin root.
|
|
260
|
+
*
|
|
261
|
+
* Typically derived from `appMcpUrl` (or the equivalent protected-resource
|
|
262
|
+
* URL) — e.g. for `appMcpUrl = https://api.example.com/mcp`, the value is
|
|
263
|
+
* `https://api.example.com/.well-known/oauth-protected-resource`.
|
|
264
|
+
*
|
|
265
|
+
* @example
|
|
266
|
+
* resourceMetadataUrl: 'http://localhost:9902/dereekb-components/us-central1/api/.well-known/oauth-protected-resource'
|
|
267
|
+
*/
|
|
268
|
+
readonly resourceMetadataUrl?: string;
|
|
196
269
|
/**
|
|
197
270
|
* Supported token endpoint authentication methods.
|
|
198
271
|
*
|
|
@@ -74,6 +74,20 @@ export declare const FIREBASE_SERVER_OIDC_ROUTES_FOR_GLOBAL_ROUTE_EXCLUDE: strin
|
|
|
74
74
|
* @throws {Error} When `appUrl` is missing, the resolved issuer lacks an HTTP prefix, or the encryption secret is invalid.
|
|
75
75
|
*/
|
|
76
76
|
export declare function oidcModuleConfigFactory(configService: ConfigService, envService: FirebaseServerEnvService): OidcModuleConfig;
|
|
77
|
+
/**
|
|
78
|
+
* Derives the RFC 9728 protected-resource metadata URL from `envService.appMcpUrl`.
|
|
79
|
+
*
|
|
80
|
+
* For `appMcpUrl = http://localhost:9902/dereekb-components/us-central1/api/mcp`, this
|
|
81
|
+
* returns `http://localhost:9902/dereekb-components/us-central1/api/.well-known/oauth-protected-resource` —
|
|
82
|
+
* i.e. the discovery doc colocated with the MCP endpoint, regardless of any URL prefix
|
|
83
|
+
* imposed by the runtime (Firebase Functions emulator, Cloud Run path, etc.).
|
|
84
|
+
*
|
|
85
|
+
* Returns `undefined` when no `appMcpUrl` is configured.
|
|
86
|
+
*
|
|
87
|
+
* @param envService - The Firebase server environment service.
|
|
88
|
+
* @returns The discovery URL, or `undefined` if `appMcpUrl` is not set.
|
|
89
|
+
*/
|
|
90
|
+
export declare function deriveResourceMetadataUrlFromEnv(envService: FirebaseServerEnvService): string | undefined;
|
|
77
91
|
/**
|
|
78
92
|
* Factory that creates {@link OidcServerFirestoreCollections} using the provided Firestore context
|
|
79
93
|
* and JWKS encryption config from {@link OidcModuleConfig}.
|
|
@@ -83,6 +97,11 @@ export declare function oidcModuleConfigFactory(configService: ConfigService, en
|
|
|
83
97
|
* @returns The configured OidcServerFirestoreCollections.
|
|
84
98
|
*/
|
|
85
99
|
export declare function oidcFirestoreCollectionsFactory(firestoreContext: FirestoreContext, oidcModuleConfig: OidcModuleConfig): OidcServerFirestoreCollections;
|
|
100
|
+
/**
|
|
101
|
+
* Subset of {@link OidcModuleConfig} that consumers may override via
|
|
102
|
+
* `oidcModuleMetadata`'s `config` or `configFactory`.
|
|
103
|
+
*/
|
|
104
|
+
export type OidcModuleMetadataOverrides = Partial<Pick<OidcModuleConfig, 'issuer' | 'suppressBodyParserWarning' | 'renderError' | 'protectedPaths' | 'appOAuthInteractionPath' | 'appOAuthLoginUrlPart' | 'appOAuthConsentUrlPart' | 'tokenEndpointAuthMethods' | 'registrationEnabled' | 'trustProxy' | 'trustProxyInNonProduction' | 'tokenLifetimes' | 'maxRequestedLoginDuration' | 'minRequestedLoginDuration' | 'defaultRequestedLoginDuration' | 'resourceServers' | 'resourceMetadataUrl'>>;
|
|
86
105
|
export interface ProvideAppOidcModuleMetadataConfig extends Pick<ModuleMetadata, 'imports' | 'exports' | 'providers'> {
|
|
87
106
|
/**
|
|
88
107
|
* Module that exports the required dependencies for this module.
|
|
@@ -90,14 +109,37 @@ export interface ProvideAppOidcModuleMetadataConfig extends Pick<ModuleMetadata,
|
|
|
90
109
|
*/
|
|
91
110
|
readonly dependencyModule: Required<ModuleMetadata>['imports']['0'];
|
|
92
111
|
/**
|
|
93
|
-
* Optional overrides to merge into the {@link OidcModuleConfig} produced by the factory.
|
|
112
|
+
* Optional static overrides to merge into the {@link OidcModuleConfig} produced by the factory.
|
|
94
113
|
*
|
|
95
114
|
* The `issuer` override is honored verbatim and takes precedence over the factory-derived issuer
|
|
96
115
|
* (which is normally built from `envService.appApiUrl` origin ?? `envService.appUrl`). Pass an
|
|
97
116
|
* explicit `issuer` when the consumer wants a canonical issuer URL that does not match either
|
|
98
117
|
* environment URL — e.g., when serving OIDC behind a non-`/oidc` path or under a vanity host.
|
|
118
|
+
*
|
|
119
|
+
* For values that must be derived from runtime services (e.g. `resourceServers` keyed on
|
|
120
|
+
* `envService.appMcpUrl`), use {@link configFactory} instead. When both are supplied,
|
|
121
|
+
* `configFactory`'s result is merged on top of `config`.
|
|
122
|
+
*/
|
|
123
|
+
readonly config?: OidcModuleMetadataOverrides;
|
|
124
|
+
/**
|
|
125
|
+
* Optional dynamic overrides that depend on runtime services. Called inside the
|
|
126
|
+
* `OidcModuleConfig` factory with the resolved {@link FirebaseServerEnvService} and
|
|
127
|
+
* {@link ConfigService}. Merged on top of {@link config} (so `configFactory` values win).
|
|
128
|
+
*
|
|
129
|
+
* Use this when an override field (typically `resourceServers` or `resourceMetadataUrl`)
|
|
130
|
+
* must be derived from `envService.appMcpUrl` rather than from a value that's stable
|
|
131
|
+
* at module-import time.
|
|
132
|
+
*
|
|
133
|
+
* @example
|
|
134
|
+
* ```ts
|
|
135
|
+
* configFactory: (envService) => ({
|
|
136
|
+
* resourceServers: envService.appMcpUrl
|
|
137
|
+
* ? { [envService.appMcpUrl]: { scope: ALL_SCOPES, audience: envService.appMcpUrl } }
|
|
138
|
+
* : undefined
|
|
139
|
+
* })
|
|
140
|
+
* ```
|
|
99
141
|
*/
|
|
100
|
-
readonly
|
|
142
|
+
readonly configFactory?: (envService: FirebaseServerEnvService, configService: ConfigService) => OidcModuleMetadataOverrides;
|
|
101
143
|
}
|
|
102
144
|
/**
|
|
103
145
|
* Convenience function used to generate ModuleMetadata for an app's OidcModule.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dereekb/firebase-server",
|
|
3
|
-
"version": "13.
|
|
3
|
+
"version": "13.12.1",
|
|
4
4
|
"sideEffects": false,
|
|
5
5
|
"exports": {
|
|
6
6
|
"./test": {
|
|
@@ -15,6 +15,12 @@
|
|
|
15
15
|
"import": "./mailgun/index.cjs.mjs",
|
|
16
16
|
"default": "./mailgun/index.cjs.js"
|
|
17
17
|
},
|
|
18
|
+
"./twilio": {
|
|
19
|
+
"module": "./twilio/index.esm.js",
|
|
20
|
+
"types": "./twilio/index.d.ts",
|
|
21
|
+
"import": "./twilio/index.cjs.mjs",
|
|
22
|
+
"default": "./twilio/index.cjs.js"
|
|
23
|
+
},
|
|
18
24
|
"./zoho": {
|
|
19
25
|
"module": "./zoho/index.esm.js",
|
|
20
26
|
"types": "./zoho/index.d.ts",
|
|
@@ -33,6 +39,12 @@
|
|
|
33
39
|
"import": "./oidc/index.cjs.mjs",
|
|
34
40
|
"default": "./oidc/index.cjs.js"
|
|
35
41
|
},
|
|
42
|
+
"./mcp": {
|
|
43
|
+
"module": "./mcp/index.esm.js",
|
|
44
|
+
"types": "./mcp/index.d.ts",
|
|
45
|
+
"import": "./mcp/index.cjs.mjs",
|
|
46
|
+
"default": "./mcp/index.cjs.js"
|
|
47
|
+
},
|
|
36
48
|
"./package.json": "./package.json",
|
|
37
49
|
".": {
|
|
38
50
|
"module": "./index.esm.js",
|
|
@@ -44,15 +56,15 @@
|
|
|
44
56
|
"main": "./index.cjs.js",
|
|
45
57
|
"types": "./src/index.d.ts",
|
|
46
58
|
"peerDependencies": {
|
|
47
|
-
"@dereekb/analytics": "13.
|
|
48
|
-
"@dereekb/date": "13.
|
|
49
|
-
"@dereekb/dbx-core": "13.
|
|
50
|
-
"@dereekb/firebase": "13.
|
|
51
|
-
"@dereekb/model": "13.
|
|
52
|
-
"@dereekb/nestjs": "13.
|
|
53
|
-
"@dereekb/rxjs": "13.
|
|
54
|
-
"@dereekb/util": "13.
|
|
55
|
-
"@dereekb/zoho": "13.
|
|
59
|
+
"@dereekb/analytics": "13.12.1",
|
|
60
|
+
"@dereekb/date": "13.12.1",
|
|
61
|
+
"@dereekb/dbx-core": "13.12.1",
|
|
62
|
+
"@dereekb/firebase": "13.12.1",
|
|
63
|
+
"@dereekb/model": "13.12.1",
|
|
64
|
+
"@dereekb/nestjs": "13.12.1",
|
|
65
|
+
"@dereekb/rxjs": "13.12.1",
|
|
66
|
+
"@dereekb/util": "13.12.1",
|
|
67
|
+
"@dereekb/zoho": "13.12.1",
|
|
56
68
|
"@google-cloud/firestore": "^7.11.6",
|
|
57
69
|
"@google-cloud/storage": "^7.19.0",
|
|
58
70
|
"@nestjs/common": "^11.1.19",
|
|
@@ -180,6 +180,13 @@ export declare abstract class AbstractFirebaseServerAuthUserContext<S extends Fi
|
|
|
180
180
|
*/
|
|
181
181
|
protected _generateResetPasswordKey(): string;
|
|
182
182
|
beginResetPassword(): Promise<FirebaseServerAuthResetUserPasswordClaims>;
|
|
183
|
+
/**
|
|
184
|
+
* Returns the TTL (in milliseconds) for newly-generated reset codes. Subclasses may override
|
|
185
|
+
* to customize expiry.
|
|
186
|
+
*
|
|
187
|
+
* @returns The TTL in milliseconds for newly-generated reset codes.
|
|
188
|
+
*/
|
|
189
|
+
protected _resetCodeExpiresInMs(): Milliseconds;
|
|
183
190
|
loadResetPasswordClaims<T extends FirebaseServerAuthResetUserPasswordClaims = FirebaseServerAuthResetUserPasswordClaims>(): Promise<Maybe<T>>;
|
|
184
191
|
setPassword(password: PasswordString): Promise<admin.auth.UserRecord>;
|
|
185
192
|
updateUser(template: admin.auth.UpdateRequest): Promise<admin.auth.UserRecord>;
|
|
@@ -753,11 +760,19 @@ export interface FirebaseServerUserPasswordResetService<D = unknown, U extends F
|
|
|
753
760
|
completePasswordReset(uid: FirebaseAuthUserId, input: FirebaseServerAuthCompletePasswordResetInput): Promise<admin.auth.UserRecord>;
|
|
754
761
|
}
|
|
755
762
|
/**
|
|
756
|
-
* Default throttle duration (
|
|
763
|
+
* Default throttle duration (60 seconds) between reset content re-sends to prevent spam while
|
|
764
|
+
* remaining responsive for legitimate users who didn't receive the first email.
|
|
757
765
|
*
|
|
758
766
|
* Used by {@link AbstractFirebaseServerUserPasswordResetService.sendResetContent} to rate-limit delivery.
|
|
759
767
|
*/
|
|
760
768
|
export declare const DEFAULT_RESET_COM_THROTTLE_TIME: number;
|
|
769
|
+
/**
|
|
770
|
+
* Default lifetime (15 minutes) of a reset code before it expires.
|
|
771
|
+
*
|
|
772
|
+
* Used by {@link AbstractFirebaseServerAuthUserContext.beginResetPassword} to set
|
|
773
|
+
* `resetExpiresAt` in the user's claims.
|
|
774
|
+
*/
|
|
775
|
+
export declare const DEFAULT_RESET_CODE_EXPIRES_IN: number;
|
|
761
776
|
/**
|
|
762
777
|
* Base implementation of {@link FirebaseServerUserPasswordResetService} that handles reset initiation,
|
|
763
778
|
* claims management, throttled reset content delivery, and reset completion.
|
|
@@ -1,18 +1,32 @@
|
|
|
1
|
+
import { type Maybe } from '@dereekb/util';
|
|
1
2
|
/**
|
|
2
3
|
* Wraps an async function that uses {@link FirebaseServerUserPasswordResetService} methods,
|
|
3
4
|
* catching password-reset-specific errors and re-throwing them as appropriate {@link HttpsError} instances
|
|
4
5
|
* suitable for returning to clients.
|
|
5
6
|
*
|
|
6
|
-
*
|
|
7
|
+
* Default mapping (security-hardened — enumeration-safe, for end-user callers):
|
|
7
8
|
* - {@link FirebaseServerAuthPasswordResetInvalidCodeError} → permission-denied (403)
|
|
8
|
-
* - {@link FirebaseServerAuthPasswordResetNoResetConfigError} →
|
|
9
|
-
*
|
|
10
|
-
* - {@link FirebaseServerAuthPasswordResetSendOnceError} →
|
|
9
|
+
* - {@link FirebaseServerAuthPasswordResetNoResetConfigError} → permission-denied (403), same opaque
|
|
10
|
+
* response as InvalidCode so callers cannot distinguish "wrong code" from "no reset active".
|
|
11
|
+
* - {@link FirebaseServerAuthPasswordResetSendOnceError} → permission-denied (403), same opaque
|
|
12
|
+
* response so callers cannot probe whether a reset was already initiated.
|
|
13
|
+
* - {@link FirebaseServerAuthPasswordResetThrottleError} → unavailable (503) with a generic message
|
|
14
|
+
* (no `lastSentAt` is leaked).
|
|
15
|
+
*
|
|
16
|
+
* Admin mapping (`isAdmin: true`) — preserves distinct error codes and statuses so an admin
|
|
17
|
+
* resetting on behalf of another user can see the actual reason. Used when the call originates
|
|
18
|
+
* from an authenticated admin, where enumeration is not a concern:
|
|
19
|
+
* - {@link FirebaseServerAuthPasswordResetInvalidCodeError} → permission-denied (403)
|
|
20
|
+
* - {@link FirebaseServerAuthPasswordResetNoResetConfigError} → bad-request (400), distinct message
|
|
21
|
+
* - {@link FirebaseServerAuthPasswordResetSendOnceError} → bad-request (400), distinct message
|
|
22
|
+
* - {@link FirebaseServerAuthPasswordResetThrottleError} → unavailable (503), same as default
|
|
11
23
|
*
|
|
12
24
|
* @param fn - The async function to execute.
|
|
25
|
+
* @param isAdmin - When `true`, returns distinct error codes/messages instead of the
|
|
26
|
+
* enumeration-safe opaque response. Pass the caller's admin status here.
|
|
13
27
|
* @returns The result of the function.
|
|
14
28
|
*/
|
|
15
|
-
export declare function catchAndThrowPasswordResetServerErrors<T>(fn: () => Promise<T>): Promise<T>;
|
|
29
|
+
export declare function catchAndThrowPasswordResetServerErrors<T>(fn: () => Promise<T>, isAdmin?: Maybe<boolean>): Promise<T>;
|
|
16
30
|
/**
|
|
17
31
|
* Creates a permission-denied (403) server error for an invalid or expired password reset code.
|
|
18
32
|
*
|
|
@@ -22,6 +36,11 @@ export declare function authServicePasswordResetInvalidCodeError(): import("fire
|
|
|
22
36
|
/**
|
|
23
37
|
* Creates a bad-request (400) server error when no active password reset exists for the user.
|
|
24
38
|
*
|
|
39
|
+
* Distinct from {@link authServicePasswordResetInvalidCodeError}. Use only when the caller is
|
|
40
|
+
* trusted (e.g., an admin) and revealing "no reset active" is not an enumeration risk; for
|
|
41
|
+
* end-user callers, {@link catchAndThrowPasswordResetServerErrors} maps this case to the same
|
|
42
|
+
* opaque response as `InvalidCodeError`.
|
|
43
|
+
*
|
|
25
44
|
* @returns An {@link HttpsError} with code `invalid-argument`.
|
|
26
45
|
*/
|
|
27
46
|
export declare function authServicePasswordResetNoConfigError(): import("firebase-functions/https").HttpsError;
|
|
@@ -13,6 +13,22 @@ export interface FirebaseServerEnvironmentConfig extends ServerEnvironmentConfig
|
|
|
13
13
|
* it from `appUrl + globalApiRoutePrefix`.
|
|
14
14
|
*/
|
|
15
15
|
readonly appApiUrl?: Maybe<WebsiteUrlWithPrefix>;
|
|
16
|
+
/**
|
|
17
|
+
* The MCP endpoint URL (e.g., 'https://api.example.com/mcp').
|
|
18
|
+
*
|
|
19
|
+
* When set, the firebase-server MCP module advertises this URL as the protected
|
|
20
|
+
* resource in `/.well-known/oauth-protected-resource`, and the OIDC bearer
|
|
21
|
+
* middleware uses its origin to construct the `WWW-Authenticate`
|
|
22
|
+
* `resource_metadata` hint on 401 responses.
|
|
23
|
+
*
|
|
24
|
+
* Set explicitly when the MCP endpoint lives on a different origin than the
|
|
25
|
+
* frontend `appUrl` (e.g., dev: behind a different port than the SPA proxy;
|
|
26
|
+
* prod: a dedicated `api.*` subdomain that bypasses Firebase Hosting).
|
|
27
|
+
*
|
|
28
|
+
* When omitted, the MCP module falls back to deriving the URL from
|
|
29
|
+
* `appApiUrl` (or `appUrl`).
|
|
30
|
+
*/
|
|
31
|
+
readonly appMcpUrl?: Maybe<WebsiteUrlWithPrefix>;
|
|
16
32
|
/**
|
|
17
33
|
* The webhook URL. When not set explicitly, `buildNestServerRootModule()` computes
|
|
18
34
|
* it from `appUrl + /webhook`.
|
|
@@ -45,6 +45,15 @@ export declare abstract class FirebaseServerEnvService {
|
|
|
45
45
|
* The full API URL (e.g., 'https://app.example.com/api').
|
|
46
46
|
*/
|
|
47
47
|
abstract readonly appApiUrl: Maybe<WebsiteUrl>;
|
|
48
|
+
/**
|
|
49
|
+
* The full MCP endpoint URL (e.g., 'https://api.example.com/mcp').
|
|
50
|
+
*
|
|
51
|
+
* When set, advertised verbatim in `/.well-known/oauth-protected-resource` and
|
|
52
|
+
* used to derive the OIDC bearer middleware's `WWW-Authenticate`
|
|
53
|
+
* `resource_metadata` hint. When omitted, modules fall back to deriving the
|
|
54
|
+
* URL from `appApiUrl` / `appUrl`.
|
|
55
|
+
*/
|
|
56
|
+
abstract readonly appMcpUrl: Maybe<WebsiteUrl>;
|
|
48
57
|
/**
|
|
49
58
|
* The full webhook URL (e.g., 'https://app.example.com/webhook').
|
|
50
59
|
*/
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type Maybe } from '@dereekb/util';
|
|
2
|
-
import { type FirestoreModelKey, type FirestoreModelType } from '@dereekb/firebase';
|
|
2
|
+
import { type FirestoreModelIdentity, type FirestoreModelKey, type FirestoreModelType } from '@dereekb/firebase';
|
|
3
3
|
import { type INestApplicationContext } from '@nestjs/common';
|
|
4
4
|
import { ModelApiDispatchConfig } from './model.api.dispatch';
|
|
5
5
|
import { type FirebaseServerAuthData } from '../auth.context.server';
|
|
@@ -29,6 +29,24 @@ export interface ModelAccessReadError {
|
|
|
29
29
|
readonly message: string;
|
|
30
30
|
readonly code?: string;
|
|
31
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* Shape of a single failed-key entry from `useMultipleModels({ throwOnFirstError: false })`.
|
|
34
|
+
* Exposed so the mapper below stays testable without a live nest context.
|
|
35
|
+
*/
|
|
36
|
+
export interface ModelAccessUseMultipleModelsFailureEntry {
|
|
37
|
+
readonly key: FirestoreModelKey;
|
|
38
|
+
readonly error: unknown;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Maps a single `useMultipleModels` failure entry into the public {@link ModelAccessReadError}
|
|
42
|
+
* shape. Unwraps the typical Firebase error shapes via {@link firebaseServerErrorInfo} so
|
|
43
|
+
* permission-denied / not-found errors surface a real message + code instead of the generic
|
|
44
|
+
* `"Unknown error"` fallback the inline mapping used previously.
|
|
45
|
+
*
|
|
46
|
+
* @param entry - A failed-key entry from the underlying multi-read.
|
|
47
|
+
* @returns A `{ key, message, code? }` triple safe to return to API/MCP callers.
|
|
48
|
+
*/
|
|
49
|
+
export declare function modelAccessReadErrorFromUseMultipleModelsFailure(entry: ModelAccessUseMultipleModelsFailureEntry): ModelAccessReadError;
|
|
32
50
|
/**
|
|
33
51
|
* Service for direct document reads using the `useModel()` permission-checking pattern.
|
|
34
52
|
*
|
|
@@ -38,7 +56,23 @@ export interface ModelAccessReadError {
|
|
|
38
56
|
*/
|
|
39
57
|
export declare class ModelApiGetService {
|
|
40
58
|
private readonly _nestContext;
|
|
59
|
+
private _identityByModelType;
|
|
41
60
|
constructor(config: ModelApiDispatchConfig, nestApplication: INestApplicationContext);
|
|
61
|
+
/**
|
|
62
|
+
* Returns the registered {@link FirestoreModelIdentity} for the given `modelType` string, or
|
|
63
|
+
* `undefined` when no model of that type is registered.
|
|
64
|
+
*
|
|
65
|
+
* Identities are read from `firebaseModelsService` and cached on first successful call. The
|
|
66
|
+
* lookup uses the provided `auth` to build a real model context — `getFirestoreCollection(ctx)`
|
|
67
|
+
* is context-dependent (calls `ctx.collection(...)` or similar), so a synthetic empty context
|
|
68
|
+
* is not sufficient.
|
|
69
|
+
*
|
|
70
|
+
* @param modelType - The Firestore model type string (e.g., 'guestbook', 'profile').
|
|
71
|
+
* @param auth - The request's auth data; used to build a context for the (one-time) lookup.
|
|
72
|
+
* @returns The matching identity or `undefined`.
|
|
73
|
+
*/
|
|
74
|
+
getModelIdentity(modelType: FirestoreModelType, auth: Maybe<FirebaseServerAuthData>): Maybe<FirestoreModelIdentity>;
|
|
75
|
+
private _buildIdentityMap;
|
|
42
76
|
/**
|
|
43
77
|
* Reads a single document by model type and key with permission checking.
|
|
44
78
|
*
|
|
@@ -16,6 +16,7 @@ export declare class DefaultFirebaseServerEnvService extends ServerEnvironmentSe
|
|
|
16
16
|
*/
|
|
17
17
|
get developmentSchedulerEnabled(): boolean;
|
|
18
18
|
get appUrlDetails(): Maybe<WebsiteUrlDetails>;
|
|
19
|
+
get appMcpUrl(): Maybe<WebsiteUrl>;
|
|
19
20
|
get appWebhookUrl(): Maybe<WebsiteUrl>;
|
|
20
21
|
get isApiEnabled(): boolean;
|
|
21
22
|
get isWebhooksEnabled(): boolean;
|