@dereekb/firebase-server 13.12.3 → 13.12.5

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.
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "@dereekb/firebase-server/model",
3
- "version": "13.12.3",
3
+ "version": "13.12.5",
4
4
  "peerDependencies": {
5
- "@dereekb/analytics": "13.12.3",
6
- "@dereekb/date": "13.12.3",
7
- "@dereekb/firebase": "13.12.3",
8
- "@dereekb/firebase-server": "13.12.3",
9
- "@dereekb/model": "13.12.3",
10
- "@dereekb/nestjs": "13.12.3",
11
- "@dereekb/rxjs": "13.12.3",
12
- "@dereekb/util": "13.12.3",
5
+ "@dereekb/analytics": "13.12.5",
6
+ "@dereekb/date": "13.12.5",
7
+ "@dereekb/firebase": "13.12.5",
8
+ "@dereekb/firebase-server": "13.12.5",
9
+ "@dereekb/model": "13.12.5",
10
+ "@dereekb/nestjs": "13.12.5",
11
+ "@dereekb/rxjs": "13.12.5",
12
+ "@dereekb/util": "13.12.5",
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
@@ -80,6 +80,31 @@ function _define_property$f(obj, key, value) {
80
80
  }
81
81
  return obj;
82
82
  }
83
+ /**
84
+ * Default {@link OidcRenderErrorFunction} that emits a JSON body with `error` and
85
+ * `error_description` fields, with `Content-Type: application/json`.
86
+ *
87
+ * Wired by {@link oidcModuleMetadata} when {@link OidcModuleConfig.renderError} is
88
+ * not provided. API-focused OIDC providers (the common case in dbx-components apps)
89
+ * want JSON errors rather than the HTML page oidc-provider renders by default.
90
+ */ var OIDC_JSON_RENDER_ERROR_FUNCTION = function OIDC_JSON_RENDER_ERROR_FUNCTION(ctx, out) {
91
+ ctx.type = 'application/json';
92
+ ctx.body = JSON.stringify({
93
+ error: out.error,
94
+ error_description: out.error_description
95
+ });
96
+ };
97
+ /**
98
+ * Returns the space-delimited list of every scope declared on `providerConfig.claims`.
99
+ *
100
+ * Suitable as the `scope` value on an {@link OidcResourceServerInfo} when the
101
+ * resource server should accept any scope the provider issues.
102
+ *
103
+ * @param providerConfig - The OIDC provider configuration whose scopes to enumerate.
104
+ * @returns Space-delimited string of all scope names from `providerConfig.claims`.
105
+ */ function allOidcScopesStringForProviderConfig(providerConfig) {
106
+ return Object.keys(providerConfig.claims).join(' ');
107
+ }
83
108
  /**
84
109
  * Default global ceiling for a client-requested login duration, in seconds. 90 days.
85
110
  */ var DEFAULT_MAX_REQUESTED_LOGIN_DURATION_SECONDS = 90 * util.SECONDS_IN_DAY;
@@ -158,8 +183,10 @@ function _define_property$f(obj, key, value) {
158
183
  /**
159
184
  * Custom error rendering function for the oidc-provider.
160
185
  *
161
- * When not provided, defaults to a JSON error response with `error` and `error_description` fields.
162
- * Set this to customize how OIDC errors are presented (e.g. redirect to an error page).
186
+ * When not provided, {@link OIDC_JSON_RENDER_ERROR_FUNCTION} is wired by
187
+ * {@link oidcModuleMetadata} so OIDC errors are returned as a JSON body with
188
+ * `error` and `error_description` fields. Set this to customize how OIDC
189
+ * errors are presented (e.g. redirect to an error page).
163
190
  *
164
191
  * The function signature matches oidc-provider's `renderError` configuration option.
165
192
  */ _define_property$f(this, "renderError", void 0);
@@ -205,6 +232,22 @@ function _define_property$f(obj, key, value) {
205
232
  * ```
206
233
  */ _define_property$f(this, "resourceServers", void 0);
207
234
  /**
235
+ * When `true`, {@link oidcModuleMetadata} automatically derives a resource-server
236
+ * entry from `envService.appMcpUrl` and the registered {@link OidcAccountService}'s
237
+ * provider config, then merges it into {@link resourceServers}. Any explicit entry
238
+ * supplied via {@link resourceServers} wins on key collisions.
239
+ *
240
+ * Required for OAuth-aware MCP clients (Claude, mcp-inspector) — they pass the
241
+ * MCP URL as the `resource` parameter on `/authorize` and `/token`, and oidc-provider
242
+ * rejects every such request with `invalid_target` unless that URL is registered.
243
+ *
244
+ * No effect when `envService.appMcpUrl` is unset.
245
+ *
246
+ * Defaults to `false`.
247
+ *
248
+ * @see buildFirebaseServerMcpResourceServer
249
+ */ _define_property$f(this, "configureMcpResourceServer", void 0);
250
+ /**
208
251
  * Absolute URL of the OAuth 2.0 Protected Resource Metadata document
209
252
  * (RFC 9728) for the resources guarded by {@link protectedPaths}.
210
253
  *
@@ -2203,14 +2246,19 @@ function _ts_generator$7(thisArg, body) {
2203
2246
  value: /**
2204
2247
  * Creates a new OIDC client through the oidc-provider.
2205
2248
  *
2206
- * Generates `client_id` and `client_secret` using the same defaults as oidc-provider's
2207
- * registration flow, validates via `Client.validate`, and persists through the adapter.
2249
+ * Generates `client_id` (and, when the auth method requires one, a `client_secret`) using the
2250
+ * same defaults as oidc-provider's registration flow, validates via `Client.validate`, and
2251
+ * persists through the adapter.
2252
+ *
2253
+ * A secret is only generated when `ProviderClient.needsSecret()` returns `true`. The secret-less
2254
+ * methods `private_key_jwt` and `'none'` (public PKCE client) therefore persist no secret and the
2255
+ * returned `client_secret` is `undefined`.
2208
2256
  *
2209
2257
  * @param params - Client registration parameters.
2210
2258
  * @param validatedMetadata - Optional pre-validated metadata to merge into the client properties.
2211
2259
  * Use this for server-side fields (e.g., inline `jwks`) that have already been validated
2212
2260
  * and should not be exposed through the API params.
2213
- * @returns The generated client ID and secret (plaintext, returned only once).
2261
+ * @returns The generated client ID and, for secret-based methods, the secret (plaintext, returned only once).
2214
2262
  */ function createClient(params, validatedMetadata) {
2215
2263
  return _async_to_generator$7(function() {
2216
2264
  var provider, ProviderClient, clientId, firestoreOwnerKey, properties, clientSecret, client, payload;
@@ -2372,9 +2420,12 @@ function _ts_generator$7(thisArg, body) {
2372
2420
  * Generates a new `client_secret`, re-validates via `Client.validate()`, and persists.
2373
2421
  * The new secret is returned in plaintext — this is the only time it is available.
2374
2422
  *
2423
+ * Rejects public PKCE clients (`token_endpoint_auth_method === 'none'`): they have no secret,
2424
+ * so there is nothing to rotate.
2425
+ *
2375
2426
  * @param clientId - The client's document/adapter entry ID.
2376
2427
  * @returns The client ID and new secret (plaintext, returned only once).
2377
- * @throws {Error} When the client is not found.
2428
+ * @throws {Error} When the client is not found, or is a public PKCE (`none`) client.
2378
2429
  */ function rotateClientSecret(clientId) {
2379
2430
  return _async_to_generator$7(function() {
2380
2431
  var provider, ProviderClient, existing, newSecret, updatedMetadata, client;
@@ -2397,6 +2448,9 @@ function _ts_generator$7(thisArg, body) {
2397
2448
  if (!existing) {
2398
2449
  throw new Error('Client not found.');
2399
2450
  }
2451
+ if (existing.token_endpoint_auth_method === firebase.PUBLIC_PKCE_TOKEN_ENDPOINT_AUTH_METHOD) {
2452
+ throw new Error('Cannot rotate a client secret for a public PKCE client (token_endpoint_auth_method "none"). Public clients have no secret.');
2453
+ }
2400
2454
  newSecret = node_crypto.randomBytes(64).toString('base64url');
2401
2455
  updatedMetadata = _object_spread_props$2(_object_spread$5({}, existing), {
2402
2456
  client_secret: newSecret,
@@ -6575,6 +6629,40 @@ function _unsupported_iterable_to_array(o, minLen) {
6575
6629
  var trimmedPath = parsed.pathname.replace(/\/[^/]*\/?$/, '');
6576
6630
  return "".concat(parsed.origin).concat(trimmedPath, "/.well-known/oauth-protected-resource");
6577
6631
  }
6632
+ /**
6633
+ * Builds a single-entry {@link OidcResourceServerInfo} map for the MCP endpoint
6634
+ * declared on `envService.appMcpUrl`, with `scope` set to every scope declared
6635
+ * on `providerConfig.claims` (so any scope the provider issues is valid on the
6636
+ * resource server) and `audience` set to the MCP URL.
6637
+ *
6638
+ * Returns `undefined` when no `appMcpUrl` is configured.
6639
+ *
6640
+ * Used internally by {@link oidcModuleMetadata} when {@link OidcModuleConfig.configureMcpResourceServer}
6641
+ * is enabled, and exported for apps that need to combine the MCP resource server
6642
+ * with additional app-specific entries:
6643
+ *
6644
+ * @example
6645
+ * ```ts
6646
+ * resourceServers: {
6647
+ * ...(buildFirebaseServerMcpResourceServer({ envService, providerConfig: APP_PROVIDER_CONFIG }) ?? {}),
6648
+ * 'https://api.example.com/extras': {
6649
+ * scope: 'openid profile',
6650
+ * audience: 'https://api.example.com/extras'
6651
+ * }
6652
+ * }
6653
+ * ```
6654
+ */ function buildFirebaseServerMcpResourceServer(input) {
6655
+ var envService = input.envService, providerConfig = input.providerConfig;
6656
+ var mcpUrl = envService.appMcpUrl;
6657
+ var result;
6658
+ if (mcpUrl) {
6659
+ result = _define_property({}, mcpUrl, {
6660
+ scope: allOidcScopesStringForProviderConfig(providerConfig),
6661
+ audience: mcpUrl
6662
+ });
6663
+ }
6664
+ return result;
6665
+ }
6578
6666
  /**
6579
6667
  * Factory that creates {@link OidcServerFirestoreCollections} using the provided Firestore context
6580
6668
  * and JWKS encryption config from {@link OidcModuleConfig}.
@@ -6631,9 +6719,10 @@ function _unsupported_iterable_to_array(o, minLen) {
6631
6719
  provide: OidcModuleConfig,
6632
6720
  inject: [
6633
6721
  config.ConfigService,
6634
- firebaseServer.FirebaseServerEnvService
6722
+ firebaseServer.FirebaseServerEnvService,
6723
+ OidcAccountService
6635
6724
  ],
6636
- useFactory: function useFactory(configService, envService) {
6725
+ useFactory: function useFactory(configService, envService, oidcAccountService) {
6637
6726
  var moduleConfig = oidcModuleConfigFactory(configService, envService);
6638
6727
  var dynamicOverrides = configFactory ? configFactory(envService, configService) : undefined;
6639
6728
  var merged = config$1 || dynamicOverrides ? _object_spread({}, config$1, dynamicOverrides) : undefined;
@@ -6644,6 +6733,22 @@ function _unsupported_iterable_to_array(o, minLen) {
6644
6733
  result.trustProxy = true;
6645
6734
  }
6646
6735
  }
6736
+ // Apply the default JSON error renderer when no custom renderError is configured.
6737
+ if (!result.renderError) {
6738
+ result.renderError = OIDC_JSON_RENDER_ERROR_FUNCTION;
6739
+ }
6740
+ // Auto-derive the MCP resource-server entry from envService.appMcpUrl and the
6741
+ // registered OidcAccountService's providerConfig. Any explicit resourceServers
6742
+ // entry from the consumer wins on key collisions.
6743
+ if (result.configureMcpResourceServer) {
6744
+ var mcpResourceServer = buildFirebaseServerMcpResourceServer({
6745
+ envService: envService,
6746
+ providerConfig: oidcAccountService.providerConfig
6747
+ });
6748
+ if (mcpResourceServer) {
6749
+ result.resourceServers = _object_spread({}, mcpResourceServer, result.resourceServers);
6750
+ }
6751
+ }
6647
6752
  return result;
6648
6753
  }
6649
6754
  },
@@ -6750,6 +6855,7 @@ exports.JwksKeyDocument = JwksKeyDocument;
6750
6855
  exports.JwksServiceConfig = JwksServiceConfig;
6751
6856
  exports.JwksServiceStorageConfig = JwksServiceStorageConfig;
6752
6857
  exports.OIDC_ENCRYPTED_PAYLOAD_FIELDS = OIDC_ENCRYPTED_PAYLOAD_FIELDS;
6858
+ exports.OIDC_JSON_RENDER_ERROR_FUNCTION = OIDC_JSON_RENDER_ERROR_FUNCTION;
6753
6859
  exports.OIDC_JWKS_ENCRYPTION_SECRET_ENV_KEY = OIDC_JWKS_ENCRYPTION_SECRET_ENV_KEY;
6754
6860
  exports.OidcAccountService = OidcAccountService;
6755
6861
  exports.OidcAccountServiceDelegate = OidcAccountServiceDelegate;
@@ -6761,10 +6867,12 @@ exports.OidcModelServerActions = OidcModelServerActions;
6761
6867
  exports.OidcModuleConfig = OidcModuleConfig;
6762
6868
  exports.OidcServerFirestoreCollections = OidcServerFirestoreCollections;
6763
6869
  exports.activeJwksKeysQuery = activeJwksKeysQuery;
6870
+ exports.allOidcScopesStringForProviderConfig = allOidcScopesStringForProviderConfig;
6764
6871
  exports.appOidcModelModuleMetadata = appOidcModelModuleMetadata;
6765
6872
  exports.applyOidcAuthMiddleware = applyOidcAuthMiddleware;
6766
6873
  exports.applyOidcCorsMiddleware = applyOidcCorsMiddleware;
6767
6874
  exports.buildBearerChallenge = buildBearerChallenge;
6875
+ exports.buildFirebaseServerMcpResourceServer = buildFirebaseServerMcpResourceServer;
6768
6876
  exports.createAdapterFactory = createAdapterFactory;
6769
6877
  exports.createOidcClientFactory = createOidcClientFactory;
6770
6878
  exports.deleteOidcClientFactory = deleteOidcClientFactory;
package/oidc/index.esm.js CHANGED
@@ -3,7 +3,7 @@ import { errors } from 'oidc-provider';
3
3
  import { SECONDS_IN_MINUTE, SECONDS_IN_DAY, cachedGetter, unixDateTimeSecondsNumberToDate, unixDateTimeSecondsNumberForNow, selectiveFieldEncryptor, filterUndefinedValues, websiteUrlFromPaths, firstValue, hasHttpPrefix } from '@dereekb/util';
4
4
  import { generateKeyPairSync, randomBytes } from 'node:crypto';
5
5
  import { resolveEncryptionKey, encryptValue, decryptValue, createAesStringEncryptionProvider, isValidAES256GCMEncryptionSecret } from '@dereekb/nestjs';
6
- import { where, iterateFirestoreDocumentSnapshotPairs, firestoreModelIdentity, snapshotConverterFunctions, optionalFirestoreDate, firestoreDate, firestoreEnum, firestorePassThroughField, AbstractFirestoreDocument, createOidcClientParamsType, deleteOidcClientParamsType, deleteOidcTokenParamsType, rotateOidcClientSecretParamsType, updateOidcClientParamsType, firestoreModelKey, oidcEntryIdentity, OIDC_ENTRY_CLIENT_TYPE, oidcEntriesByUserCodeQuery, oidcEntriesByUidQuery, oidcEntriesByGrantIdQuery, oidcEntryFirestoreCollection, callModelOidcScopeForCallType, CALL_MODEL_MISSING_OIDC_SCOPE_ERROR_CODE } from '@dereekb/firebase';
6
+ import { where, iterateFirestoreDocumentSnapshotPairs, firestoreModelIdentity, snapshotConverterFunctions, optionalFirestoreDate, firestoreDate, firestoreEnum, firestorePassThroughField, AbstractFirestoreDocument, createOidcClientParamsType, deleteOidcClientParamsType, deleteOidcTokenParamsType, rotateOidcClientSecretParamsType, updateOidcClientParamsType, firestoreModelKey, oidcEntryIdentity, PUBLIC_PKCE_TOKEN_ENDPOINT_AUTH_METHOD, OIDC_ENTRY_CLIENT_TYPE, oidcEntriesByUserCodeQuery, oidcEntriesByUidQuery, oidcEntriesByGrantIdQuery, oidcEntryFirestoreCollection, callModelOidcScopeForCallType, CALL_MODEL_MISSING_OIDC_SCOPE_ERROR_CODE } from '@dereekb/firebase';
7
7
  import { firestoreEncryptedField, firebaseServerActionsContext, FirebaseServerEnvService, FIREBASE_FIRESTORE_CONTEXT_TOKEN, FirebaseServerFirestoreContextModule, forbiddenError } from '@dereekb/firebase-server';
8
8
  import { nanoid } from 'nanoid';
9
9
  import { safeToJsDate } from '@dereekb/date';
@@ -78,6 +78,31 @@ function _define_property$f(obj, key, value) {
78
78
  }
79
79
  return obj;
80
80
  }
81
+ /**
82
+ * Default {@link OidcRenderErrorFunction} that emits a JSON body with `error` and
83
+ * `error_description` fields, with `Content-Type: application/json`.
84
+ *
85
+ * Wired by {@link oidcModuleMetadata} when {@link OidcModuleConfig.renderError} is
86
+ * not provided. API-focused OIDC providers (the common case in dbx-components apps)
87
+ * want JSON errors rather than the HTML page oidc-provider renders by default.
88
+ */ var OIDC_JSON_RENDER_ERROR_FUNCTION = function OIDC_JSON_RENDER_ERROR_FUNCTION(ctx, out) {
89
+ ctx.type = 'application/json';
90
+ ctx.body = JSON.stringify({
91
+ error: out.error,
92
+ error_description: out.error_description
93
+ });
94
+ };
95
+ /**
96
+ * Returns the space-delimited list of every scope declared on `providerConfig.claims`.
97
+ *
98
+ * Suitable as the `scope` value on an {@link OidcResourceServerInfo} when the
99
+ * resource server should accept any scope the provider issues.
100
+ *
101
+ * @param providerConfig - The OIDC provider configuration whose scopes to enumerate.
102
+ * @returns Space-delimited string of all scope names from `providerConfig.claims`.
103
+ */ function allOidcScopesStringForProviderConfig(providerConfig) {
104
+ return Object.keys(providerConfig.claims).join(' ');
105
+ }
81
106
  /**
82
107
  * Default global ceiling for a client-requested login duration, in seconds. 90 days.
83
108
  */ var DEFAULT_MAX_REQUESTED_LOGIN_DURATION_SECONDS = 90 * SECONDS_IN_DAY;
@@ -156,8 +181,10 @@ function _define_property$f(obj, key, value) {
156
181
  /**
157
182
  * Custom error rendering function for the oidc-provider.
158
183
  *
159
- * When not provided, defaults to a JSON error response with `error` and `error_description` fields.
160
- * Set this to customize how OIDC errors are presented (e.g. redirect to an error page).
184
+ * When not provided, {@link OIDC_JSON_RENDER_ERROR_FUNCTION} is wired by
185
+ * {@link oidcModuleMetadata} so OIDC errors are returned as a JSON body with
186
+ * `error` and `error_description` fields. Set this to customize how OIDC
187
+ * errors are presented (e.g. redirect to an error page).
161
188
  *
162
189
  * The function signature matches oidc-provider's `renderError` configuration option.
163
190
  */ _define_property$f(this, "renderError", void 0);
@@ -203,6 +230,22 @@ function _define_property$f(obj, key, value) {
203
230
  * ```
204
231
  */ _define_property$f(this, "resourceServers", void 0);
205
232
  /**
233
+ * When `true`, {@link oidcModuleMetadata} automatically derives a resource-server
234
+ * entry from `envService.appMcpUrl` and the registered {@link OidcAccountService}'s
235
+ * provider config, then merges it into {@link resourceServers}. Any explicit entry
236
+ * supplied via {@link resourceServers} wins on key collisions.
237
+ *
238
+ * Required for OAuth-aware MCP clients (Claude, mcp-inspector) — they pass the
239
+ * MCP URL as the `resource` parameter on `/authorize` and `/token`, and oidc-provider
240
+ * rejects every such request with `invalid_target` unless that URL is registered.
241
+ *
242
+ * No effect when `envService.appMcpUrl` is unset.
243
+ *
244
+ * Defaults to `false`.
245
+ *
246
+ * @see buildFirebaseServerMcpResourceServer
247
+ */ _define_property$f(this, "configureMcpResourceServer", void 0);
248
+ /**
206
249
  * Absolute URL of the OAuth 2.0 Protected Resource Metadata document
207
250
  * (RFC 9728) for the resources guarded by {@link protectedPaths}.
208
251
  *
@@ -2201,14 +2244,19 @@ function _ts_generator$7(thisArg, body) {
2201
2244
  value: /**
2202
2245
  * Creates a new OIDC client through the oidc-provider.
2203
2246
  *
2204
- * Generates `client_id` and `client_secret` using the same defaults as oidc-provider's
2205
- * registration flow, validates via `Client.validate`, and persists through the adapter.
2247
+ * Generates `client_id` (and, when the auth method requires one, a `client_secret`) using the
2248
+ * same defaults as oidc-provider's registration flow, validates via `Client.validate`, and
2249
+ * persists through the adapter.
2250
+ *
2251
+ * A secret is only generated when `ProviderClient.needsSecret()` returns `true`. The secret-less
2252
+ * methods `private_key_jwt` and `'none'` (public PKCE client) therefore persist no secret and the
2253
+ * returned `client_secret` is `undefined`.
2206
2254
  *
2207
2255
  * @param params - Client registration parameters.
2208
2256
  * @param validatedMetadata - Optional pre-validated metadata to merge into the client properties.
2209
2257
  * Use this for server-side fields (e.g., inline `jwks`) that have already been validated
2210
2258
  * and should not be exposed through the API params.
2211
- * @returns The generated client ID and secret (plaintext, returned only once).
2259
+ * @returns The generated client ID and, for secret-based methods, the secret (plaintext, returned only once).
2212
2260
  */ function createClient(params, validatedMetadata) {
2213
2261
  return _async_to_generator$7(function() {
2214
2262
  var provider, ProviderClient, clientId, firestoreOwnerKey, properties, clientSecret, client, payload;
@@ -2370,9 +2418,12 @@ function _ts_generator$7(thisArg, body) {
2370
2418
  * Generates a new `client_secret`, re-validates via `Client.validate()`, and persists.
2371
2419
  * The new secret is returned in plaintext — this is the only time it is available.
2372
2420
  *
2421
+ * Rejects public PKCE clients (`token_endpoint_auth_method === 'none'`): they have no secret,
2422
+ * so there is nothing to rotate.
2423
+ *
2373
2424
  * @param clientId - The client's document/adapter entry ID.
2374
2425
  * @returns The client ID and new secret (plaintext, returned only once).
2375
- * @throws {Error} When the client is not found.
2426
+ * @throws {Error} When the client is not found, or is a public PKCE (`none`) client.
2376
2427
  */ function rotateClientSecret(clientId) {
2377
2428
  return _async_to_generator$7(function() {
2378
2429
  var provider, ProviderClient, existing, newSecret, updatedMetadata, client;
@@ -2395,6 +2446,9 @@ function _ts_generator$7(thisArg, body) {
2395
2446
  if (!existing) {
2396
2447
  throw new Error('Client not found.');
2397
2448
  }
2449
+ if (existing.token_endpoint_auth_method === PUBLIC_PKCE_TOKEN_ENDPOINT_AUTH_METHOD) {
2450
+ throw new Error('Cannot rotate a client secret for a public PKCE client (token_endpoint_auth_method "none"). Public clients have no secret.');
2451
+ }
2398
2452
  newSecret = randomBytes(64).toString('base64url');
2399
2453
  updatedMetadata = _object_spread_props$2(_object_spread$5({}, existing), {
2400
2454
  client_secret: newSecret,
@@ -6573,6 +6627,40 @@ function _unsupported_iterable_to_array(o, minLen) {
6573
6627
  var trimmedPath = parsed.pathname.replace(/\/[^/]*\/?$/, '');
6574
6628
  return "".concat(parsed.origin).concat(trimmedPath, "/.well-known/oauth-protected-resource");
6575
6629
  }
6630
+ /**
6631
+ * Builds a single-entry {@link OidcResourceServerInfo} map for the MCP endpoint
6632
+ * declared on `envService.appMcpUrl`, with `scope` set to every scope declared
6633
+ * on `providerConfig.claims` (so any scope the provider issues is valid on the
6634
+ * resource server) and `audience` set to the MCP URL.
6635
+ *
6636
+ * Returns `undefined` when no `appMcpUrl` is configured.
6637
+ *
6638
+ * Used internally by {@link oidcModuleMetadata} when {@link OidcModuleConfig.configureMcpResourceServer}
6639
+ * is enabled, and exported for apps that need to combine the MCP resource server
6640
+ * with additional app-specific entries:
6641
+ *
6642
+ * @example
6643
+ * ```ts
6644
+ * resourceServers: {
6645
+ * ...(buildFirebaseServerMcpResourceServer({ envService, providerConfig: APP_PROVIDER_CONFIG }) ?? {}),
6646
+ * 'https://api.example.com/extras': {
6647
+ * scope: 'openid profile',
6648
+ * audience: 'https://api.example.com/extras'
6649
+ * }
6650
+ * }
6651
+ * ```
6652
+ */ function buildFirebaseServerMcpResourceServer(input) {
6653
+ var envService = input.envService, providerConfig = input.providerConfig;
6654
+ var mcpUrl = envService.appMcpUrl;
6655
+ var result;
6656
+ if (mcpUrl) {
6657
+ result = _define_property({}, mcpUrl, {
6658
+ scope: allOidcScopesStringForProviderConfig(providerConfig),
6659
+ audience: mcpUrl
6660
+ });
6661
+ }
6662
+ return result;
6663
+ }
6576
6664
  /**
6577
6665
  * Factory that creates {@link OidcServerFirestoreCollections} using the provided Firestore context
6578
6666
  * and JWKS encryption config from {@link OidcModuleConfig}.
@@ -6629,9 +6717,10 @@ function _unsupported_iterable_to_array(o, minLen) {
6629
6717
  provide: OidcModuleConfig,
6630
6718
  inject: [
6631
6719
  ConfigService,
6632
- FirebaseServerEnvService
6720
+ FirebaseServerEnvService,
6721
+ OidcAccountService
6633
6722
  ],
6634
- useFactory: function useFactory(configService, envService) {
6723
+ useFactory: function useFactory(configService, envService, oidcAccountService) {
6635
6724
  var moduleConfig = oidcModuleConfigFactory(configService, envService);
6636
6725
  var dynamicOverrides = configFactory ? configFactory(envService, configService) : undefined;
6637
6726
  var merged = config || dynamicOverrides ? _object_spread({}, config, dynamicOverrides) : undefined;
@@ -6642,6 +6731,22 @@ function _unsupported_iterable_to_array(o, minLen) {
6642
6731
  result.trustProxy = true;
6643
6732
  }
6644
6733
  }
6734
+ // Apply the default JSON error renderer when no custom renderError is configured.
6735
+ if (!result.renderError) {
6736
+ result.renderError = OIDC_JSON_RENDER_ERROR_FUNCTION;
6737
+ }
6738
+ // Auto-derive the MCP resource-server entry from envService.appMcpUrl and the
6739
+ // registered OidcAccountService's providerConfig. Any explicit resourceServers
6740
+ // entry from the consumer wins on key collisions.
6741
+ if (result.configureMcpResourceServer) {
6742
+ var mcpResourceServer = buildFirebaseServerMcpResourceServer({
6743
+ envService: envService,
6744
+ providerConfig: oidcAccountService.providerConfig
6745
+ });
6746
+ if (mcpResourceServer) {
6747
+ result.resourceServers = _object_spread({}, mcpResourceServer, result.resourceServers);
6748
+ }
6749
+ }
6645
6750
  return result;
6646
6751
  }
6647
6752
  },
@@ -6728,4 +6833,4 @@ function _unsupported_iterable_to_array(o, minLen) {
6728
6833
  return fn;
6729
6834
  }
6730
6835
 
6731
- export { DEFAULT_APP_OAUTH_CONSENT_PATH_PART, DEFAULT_APP_OAUTH_INTERACTION_PATH, DEFAULT_APP_OAUTH_LOGIN_PATH_PART, DEFAULT_MAX_REQUESTED_LOGIN_DURATION_SECONDS, DEFAULT_MIN_REQUESTED_LOGIN_DURATION_SECONDS, 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, GRANTABLE_MODEL_NAMES, 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, applyOidcCorsMiddleware, buildBearerChallenge, createAdapterFactory, createOidcClientFactory, deleteOidcClientFactory, deleteOidcTokenFactory, deriveResourceMetadataUrlFromEnv, getOidcScopesFromRequest, jwksKeyCollectionReference, jwksKeyConverter, jwksKeyFirestoreCollection, jwksKeyIdentity, jwksKeysWithStatusQuery, nonRetiredJwksKeysQuery, oidcCallModelScopePreAssert, oidcFirestoreCollectionsFactory, oidcModelServerActions, oidcModelServerActionsFactory, oidcModuleConfigFactory, oidcModuleMetadata, resolveEffectiveSubset, rotateOidcClientSecretFactory, rotatedJwksKeysQuery, updateOidcClientFactory };
6836
+ export { DEFAULT_APP_OAUTH_CONSENT_PATH_PART, DEFAULT_APP_OAUTH_INTERACTION_PATH, DEFAULT_APP_OAUTH_LOGIN_PATH_PART, DEFAULT_MAX_REQUESTED_LOGIN_DURATION_SECONDS, DEFAULT_MIN_REQUESTED_LOGIN_DURATION_SECONDS, 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, GRANTABLE_MODEL_NAMES, JwksFirestoreCollections, JwksKeyDocument, JwksService, JwksServiceConfig, JwksServiceStorageConfig, OIDC_ENCRYPTED_PAYLOAD_FIELDS, OIDC_JSON_RENDER_ERROR_FUNCTION, OIDC_JWKS_ENCRYPTION_SECRET_ENV_KEY, OidcAccountService, OidcAccountServiceDelegate, OidcAccountServiceUserContext, OidcAuth, OidcAuthBearerTokenMiddleware, OidcAuthMiddlewareConfig, OidcClientService, OidcEncryptionService, OidcInteractionController, OidcInteractionService, OidcModelServerActions, OidcModuleConfig, OidcProviderConfigService, OidcProviderController, OidcServerFirestoreCollections, OidcService, OidcWellKnownController, activeJwksKeysQuery, allOidcScopesStringForProviderConfig, appOidcModelModuleMetadata, applyOidcAuthMiddleware, applyOidcCorsMiddleware, buildBearerChallenge, buildFirebaseServerMcpResourceServer, createAdapterFactory, createOidcClientFactory, deleteOidcClientFactory, deleteOidcTokenFactory, deriveResourceMetadataUrlFromEnv, getOidcScopesFromRequest, jwksKeyCollectionReference, jwksKeyConverter, jwksKeyFirestoreCollection, jwksKeyIdentity, jwksKeysWithStatusQuery, nonRetiredJwksKeysQuery, oidcCallModelScopePreAssert, oidcFirestoreCollectionsFactory, oidcModelServerActions, oidcModelServerActionsFactory, oidcModuleConfigFactory, oidcModuleMetadata, resolveEffectiveSubset, rotateOidcClientSecretFactory, rotatedJwksKeysQuery, updateOidcClientFactory };
package/oidc/package.json CHANGED
@@ -1,16 +1,16 @@
1
1
  {
2
2
  "name": "@dereekb/firebase-server/oidc",
3
- "version": "13.12.3",
3
+ "version": "13.12.5",
4
4
  "peerDependencies": {
5
- "@dereekb/analytics": "13.12.3",
6
- "@dereekb/date": "13.12.3",
7
- "@dereekb/firebase": "13.12.3",
8
- "@dereekb/firebase-server": "13.12.3",
9
- "@dereekb/model": "13.12.3",
10
- "@dereekb/nestjs": "13.12.3",
11
- "@dereekb/rxjs": "13.12.3",
12
- "@dereekb/util": "13.12.3",
13
- "@dereekb/zoho": "13.12.3",
5
+ "@dereekb/analytics": "13.12.5",
6
+ "@dereekb/date": "13.12.5",
7
+ "@dereekb/firebase": "13.12.5",
8
+ "@dereekb/firebase-server": "13.12.5",
9
+ "@dereekb/model": "13.12.5",
10
+ "@dereekb/nestjs": "13.12.5",
11
+ "@dereekb/rxjs": "13.12.5",
12
+ "@dereekb/util": "13.12.5",
13
+ "@dereekb/zoho": "13.12.5",
14
14
  "@nestjs/common": "^11.1.19",
15
15
  "@nestjs/config": "^4.0.4",
16
16
  "express": "^5.2.1",
@@ -9,6 +9,15 @@ 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
+ * Default {@link OidcRenderErrorFunction} that emits a JSON body with `error` and
14
+ * `error_description` fields, with `Content-Type: application/json`.
15
+ *
16
+ * Wired by {@link oidcModuleMetadata} when {@link OidcModuleConfig.renderError} is
17
+ * not provided. API-focused OIDC providers (the common case in dbx-components apps)
18
+ * want JSON errors rather than the HTML page oidc-provider renders by default.
19
+ */
20
+ export declare const OIDC_JSON_RENDER_ERROR_FUNCTION: OidcRenderErrorFunction;
12
21
  /**
13
22
  * Per-resource configuration returned by `features.resourceIndicators.getResourceServerInfo`
14
23
  * to oidc-provider when a client requests an access token bound to a specific resource
@@ -78,6 +87,16 @@ export interface OidcProviderConfig<S extends OidcScope = OidcScope> {
78
87
  */
79
88
  readonly grantTypes: string[];
80
89
  }
90
+ /**
91
+ * Returns the space-delimited list of every scope declared on `providerConfig.claims`.
92
+ *
93
+ * Suitable as the `scope` value on an {@link OidcResourceServerInfo} when the
94
+ * resource server should accept any scope the provider issues.
95
+ *
96
+ * @param providerConfig - The OIDC provider configuration whose scopes to enumerate.
97
+ * @returns Space-delimited string of all scope names from `providerConfig.claims`.
98
+ */
99
+ export declare function allOidcScopesStringForProviderConfig<S extends OidcScope = OidcScope>(providerConfig: OidcProviderConfig<S>): string;
81
100
  /**
82
101
  * Configures the lifetime (in seconds) for each token type issued by the OIDC provider.
83
102
  *
@@ -197,8 +216,10 @@ export declare abstract class OidcModuleConfig {
197
216
  /**
198
217
  * Custom error rendering function for the oidc-provider.
199
218
  *
200
- * When not provided, defaults to a JSON error response with `error` and `error_description` fields.
201
- * Set this to customize how OIDC errors are presented (e.g. redirect to an error page).
219
+ * When not provided, {@link OIDC_JSON_RENDER_ERROR_FUNCTION} is wired by
220
+ * {@link oidcModuleMetadata} so OIDC errors are returned as a JSON body with
221
+ * `error` and `error_description` fields. Set this to customize how OIDC
222
+ * errors are presented (e.g. redirect to an error page).
202
223
  *
203
224
  * The function signature matches oidc-provider's `renderError` configuration option.
204
225
  */
@@ -247,6 +268,23 @@ export declare abstract class OidcModuleConfig {
247
268
  * ```
248
269
  */
249
270
  readonly resourceServers?: Readonly<Record<string, OidcResourceServerInfo>>;
271
+ /**
272
+ * When `true`, {@link oidcModuleMetadata} automatically derives a resource-server
273
+ * entry from `envService.appMcpUrl` and the registered {@link OidcAccountService}'s
274
+ * provider config, then merges it into {@link resourceServers}. Any explicit entry
275
+ * supplied via {@link resourceServers} wins on key collisions.
276
+ *
277
+ * Required for OAuth-aware MCP clients (Claude, mcp-inspector) — they pass the
278
+ * MCP URL as the `resource` parameter on `/authorize` and `/token`, and oidc-provider
279
+ * rejects every such request with `invalid_target` unless that URL is registered.
280
+ *
281
+ * No effect when `envService.appMcpUrl` is unset.
282
+ *
283
+ * Defaults to `false`.
284
+ *
285
+ * @see buildFirebaseServerMcpResourceServer
286
+ */
287
+ readonly configureMcpResourceServer?: boolean;
250
288
  /**
251
289
  * Absolute URL of the OAuth 2.0 Protected Resource Metadata document
252
290
  * (RFC 9728) for the resources guarded by {@link protectedPaths}.
@@ -1,6 +1,6 @@
1
1
  import { type ModuleMetadata } from '@nestjs/common';
2
2
  import { ConfigService } from '@nestjs/config';
3
- import { OidcModuleConfig } from './oidc.config';
3
+ import { OidcModuleConfig, type OidcProviderConfig, type OidcResourceServerInfo } from './oidc.config';
4
4
  import { type FirestoreContext } from '@dereekb/firebase';
5
5
  import { FirebaseServerEnvService } from '@dereekb/firebase-server';
6
6
  import { OidcServerFirestoreCollections } from './model/model';
@@ -88,6 +88,43 @@ export declare function oidcModuleConfigFactory(configService: ConfigService, en
88
88
  * @returns The discovery URL, or `undefined` if `appMcpUrl` is not set.
89
89
  */
90
90
  export declare function deriveResourceMetadataUrlFromEnv(envService: FirebaseServerEnvService): string | undefined;
91
+ /**
92
+ * Input for {@link buildFirebaseServerMcpResourceServer}.
93
+ */
94
+ export interface BuildFirebaseServerMcpResourceServerInput {
95
+ /**
96
+ * The Firebase server environment service. The MCP URL is read from `envService.appMcpUrl`.
97
+ */
98
+ readonly envService: FirebaseServerEnvService;
99
+ /**
100
+ * The OIDC provider config whose scopes back the resource server's `scope` value.
101
+ */
102
+ readonly providerConfig: OidcProviderConfig;
103
+ }
104
+ /**
105
+ * Builds a single-entry {@link OidcResourceServerInfo} map for the MCP endpoint
106
+ * declared on `envService.appMcpUrl`, with `scope` set to every scope declared
107
+ * on `providerConfig.claims` (so any scope the provider issues is valid on the
108
+ * resource server) and `audience` set to the MCP URL.
109
+ *
110
+ * Returns `undefined` when no `appMcpUrl` is configured.
111
+ *
112
+ * Used internally by {@link oidcModuleMetadata} when {@link OidcModuleConfig.configureMcpResourceServer}
113
+ * is enabled, and exported for apps that need to combine the MCP resource server
114
+ * with additional app-specific entries:
115
+ *
116
+ * @example
117
+ * ```ts
118
+ * resourceServers: {
119
+ * ...(buildFirebaseServerMcpResourceServer({ envService, providerConfig: APP_PROVIDER_CONFIG }) ?? {}),
120
+ * 'https://api.example.com/extras': {
121
+ * scope: 'openid profile',
122
+ * audience: 'https://api.example.com/extras'
123
+ * }
124
+ * }
125
+ * ```
126
+ */
127
+ export declare function buildFirebaseServerMcpResourceServer(input: BuildFirebaseServerMcpResourceServerInput): Record<string, OidcResourceServerInfo> | undefined;
91
128
  /**
92
129
  * Factory that creates {@link OidcServerFirestoreCollections} using the provided Firestore context
93
130
  * and JWKS encryption config from {@link OidcModuleConfig}.
@@ -101,7 +138,7 @@ export declare function oidcFirestoreCollectionsFactory(firestoreContext: Firest
101
138
  * Subset of {@link OidcModuleConfig} that consumers may override via
102
139
  * `oidcModuleMetadata`'s `config` or `configFactory`.
103
140
  */
104
- export type OidcModuleMetadataOverrides = Partial<Pick<OidcModuleConfig, 'issuer' | 'suppressBodyParserWarning' | 'renderError' | 'protectedPaths' | 'appOAuthInteractionPath' | 'appOAuthLoginUrlPart' | 'appOAuthConsentUrlPart' | 'tokenEndpointAuthMethods' | 'registrationEnabled' | 'trustProxy' | 'trustProxyInNonProduction' | 'tokenLifetimes' | 'maxRequestedLoginDuration' | 'minRequestedLoginDuration' | 'defaultRequestedLoginDuration' | 'resourceServers' | 'resourceMetadataUrl'>>;
141
+ export type OidcModuleMetadataOverrides = Partial<Pick<OidcModuleConfig, 'issuer' | 'suppressBodyParserWarning' | 'renderError' | 'protectedPaths' | 'appOAuthInteractionPath' | 'appOAuthLoginUrlPart' | 'appOAuthConsentUrlPart' | 'tokenEndpointAuthMethods' | 'registrationEnabled' | 'trustProxy' | 'trustProxyInNonProduction' | 'tokenLifetimes' | 'maxRequestedLoginDuration' | 'minRequestedLoginDuration' | 'defaultRequestedLoginDuration' | 'resourceServers' | 'resourceMetadataUrl' | 'configureMcpResourceServer'>>;
105
142
  export interface ProvideAppOidcModuleMetadataConfig extends Pick<ModuleMetadata, 'imports' | 'exports' | 'providers'> {
106
143
  /**
107
144
  * Module that exports the required dependencies for this module.
@@ -13,14 +13,19 @@ export declare class OidcClientService {
13
13
  /**
14
14
  * Creates a new OIDC client through the oidc-provider.
15
15
  *
16
- * Generates `client_id` and `client_secret` using the same defaults as oidc-provider's
17
- * registration flow, validates via `Client.validate`, and persists through the adapter.
16
+ * Generates `client_id` (and, when the auth method requires one, a `client_secret`) using the
17
+ * same defaults as oidc-provider's registration flow, validates via `Client.validate`, and
18
+ * persists through the adapter.
19
+ *
20
+ * A secret is only generated when `ProviderClient.needsSecret()` returns `true`. The secret-less
21
+ * methods `private_key_jwt` and `'none'` (public PKCE client) therefore persist no secret and the
22
+ * returned `client_secret` is `undefined`.
18
23
  *
19
24
  * @param params - Client registration parameters.
20
25
  * @param validatedMetadata - Optional pre-validated metadata to merge into the client properties.
21
26
  * Use this for server-side fields (e.g., inline `jwks`) that have already been validated
22
27
  * and should not be exposed through the API params.
23
- * @returns The generated client ID and secret (plaintext, returned only once).
28
+ * @returns The generated client ID and, for secret-based methods, the secret (plaintext, returned only once).
24
29
  */
25
30
  createClient(params: CreateOidcClientParams, validatedMetadata?: Partial<Pick<ClientMetadata, 'jwks'>>): Promise<CreateOidcClientResult>;
26
31
  /**
@@ -42,9 +47,12 @@ export declare class OidcClientService {
42
47
  * Generates a new `client_secret`, re-validates via `Client.validate()`, and persists.
43
48
  * The new secret is returned in plaintext — this is the only time it is available.
44
49
  *
50
+ * Rejects public PKCE clients (`token_endpoint_auth_method === 'none'`): they have no secret,
51
+ * so there is nothing to rotate.
52
+ *
45
53
  * @param clientId - The client's document/adapter entry ID.
46
54
  * @returns The client ID and new secret (plaintext, returned only once).
47
- * @throws {Error} When the client is not found.
55
+ * @throws {Error} When the client is not found, or is a public PKCE (`none`) client.
48
56
  */
49
57
  rotateClientSecret(clientId: OidcEntryClientId): Promise<RotateOidcClientSecretResult>;
50
58
  /**