@dereekb/firebase-server 13.11.3 → 13.11.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.
package/oidc/index.cjs.js CHANGED
@@ -1,12 +1,14 @@
1
1
  'use strict';
2
2
 
3
3
  var common = require('@nestjs/common');
4
+ var oidcProvider = require('oidc-provider');
5
+ var util = require('@dereekb/util');
4
6
  var node_crypto = require('node:crypto');
5
7
  var nestjs = require('@dereekb/nestjs');
6
8
  var firebase = require('@dereekb/firebase');
7
- var util = require('@dereekb/util');
8
9
  var firebaseServer = require('@dereekb/firebase-server');
9
10
  var nanoid = require('nanoid');
11
+ var date = require('@dereekb/date');
10
12
  var fetch = require('@dereekb/util/fetch');
11
13
  var config = require('@nestjs/config');
12
14
 
@@ -79,12 +81,20 @@ function _define_property$f(obj, key, value) {
79
81
  return obj;
80
82
  }
81
83
  /**
82
- * Default token lifetimes: 15 min access tokens, 30-day refresh tokens, 60 s auth codes.
84
+ * Default global ceiling for a client-requested login duration, in seconds. 90 days.
85
+ */ var DEFAULT_MAX_REQUESTED_LOGIN_DURATION_SECONDS = 90 * util.SECONDS_IN_DAY;
86
+ /**
87
+ * Default global floor for a client-requested login duration, in seconds. 1 hour.
88
+ */ var DEFAULT_MIN_REQUESTED_LOGIN_DURATION_SECONDS = 60 * util.SECONDS_IN_MINUTE;
89
+ /**
90
+ * Default token lifetimes: 15 min access tokens, 30-day refresh tokens, 30-day sessions/grants, 60 s auth codes.
83
91
  */ var DEFAULT_OIDC_TOKEN_LIFETIMES = {
84
- accessToken: 900,
85
- idToken: 3600,
86
- refreshToken: 30 * 24 * 60 * 60,
87
- authorizationCode: 60
92
+ accessToken: 15 * util.SECONDS_IN_MINUTE,
93
+ idToken: 60 * util.SECONDS_IN_MINUTE,
94
+ refreshToken: 30 * util.SECONDS_IN_DAY,
95
+ authorizationCode: util.SECONDS_IN_MINUTE,
96
+ session: 30 * util.SECONDS_IN_DAY,
97
+ grant: 30 * util.SECONDS_IN_DAY
88
98
  };
89
99
  // MARK: Config
90
100
  /**
@@ -120,6 +130,26 @@ function _define_property$f(obj, key, value) {
120
130
  * Token lifetime configuration.
121
131
  */ _define_property$f(this, "tokenLifetimes", void 0);
122
132
  /**
133
+ * Maximum login duration (seconds) a client may request via the `dbx_session_ttl` auth-URL param.
134
+ *
135
+ * Acts as the global server cap for any per-request duration. A registered client can declare its own
136
+ * lower ceiling via the `dbx_max_session_ttl` client metadata field; the effective ceiling is
137
+ * `min(client.dbx_max_session_ttl, this)`.
138
+ *
139
+ * Defaults to {@link DEFAULT_MAX_REQUESTED_LOGIN_DURATION_SECONDS} (90 days).
140
+ */ _define_property$f(this, "maxRequestedLoginDuration", void 0);
141
+ /**
142
+ * Minimum login duration (seconds) a client may request via the `dbx_session_ttl` auth-URL param.
143
+ *
144
+ * Requested values below this floor are clamped up. Prevents pathological 30-second sessions.
145
+ *
146
+ * Defaults to {@link DEFAULT_MIN_REQUESTED_LOGIN_DURATION_SECONDS} (1 hour).
147
+ */ _define_property$f(this, "minRequestedLoginDuration", void 0);
148
+ /**
149
+ * Default login duration (seconds) used when a client does NOT pass the `dbx_session_ttl`
150
+ * auth-URL param. When undefined, falls back to {@link OidcTokenLifetimes.refreshToken}.
151
+ */ _define_property$f(this, "defaultRequestedLoginDuration", void 0);
152
+ /**
123
153
  * JWKS service configuration (encryption secret, rotated key max age).
124
154
  */ _define_property$f(this, "jwksServiceConfig", void 0);
125
155
  /**
@@ -168,6 +198,33 @@ function _define_property$f(obj, key, value) {
168
198
  *
169
199
  * Defaults to `false`.
170
200
  */ _define_property$f(this, "registrationEnabled", void 0);
201
+ /**
202
+ * Whether the oidc-provider should trust upstream proxy headers
203
+ * (`X-Forwarded-Host`, `X-Forwarded-Proto`) when computing the request URL
204
+ * used to build resume/return URLs and discovery metadata absolute paths.
205
+ *
206
+ * Required when running behind a reverse proxy such as Firebase Hosting →
207
+ * Cloud Run / Cloud Functions, where the request `Host` header is the
208
+ * underlying Cloud Run host (e.g. `api-xxxxx-uc.a.run.app`) and not the
209
+ * canonical issuer host. Without this, the interaction `returnTo` URL is
210
+ * built off the Cloud Run host and the browser is redirected away from the
211
+ * issuer's domain after login — causing the interaction cookies (scoped to
212
+ * the canonical host) to be missing on the resume request and producing
213
+ * "authorization request has expired".
214
+ *
215
+ * Maps to `provider.proxy = <value>` on the underlying oidc-provider
216
+ * (which is the Koa `app.proxy` setting).
217
+ *
218
+ * Defaults to `'prod_only'` if the environment is `production`, otherwise `false`.
219
+ */ _define_property$f(this, "trustProxy", void 0);
220
+ /**
221
+ * Whether to trust proxy headers in a non-production environment, such as the local environment.
222
+ *
223
+ * The dev environment typically does not require proxy headers, and setting
224
+ * `true` will result in errors.
225
+ *
226
+ * Defaults to `false`.
227
+ */ _define_property$f(this, "trustProxyInNonProduction", void 0);
171
228
  }
172
229
  _create_class$d(OidcModuleConfig, null, [
173
230
  {
@@ -205,6 +262,95 @@ function _define_property$f(obj, key, value) {
205
262
  }
206
263
  ();
207
264
 
265
+ /**
266
+ * Custom OIDC auth-URL parameter clients use to request a non-default login duration.
267
+ *
268
+ * Value is integer seconds. Subject to per-client and global server caps via {@link resolveLoginDurationSeconds}.
269
+ */ var DBX_FIREBASE_SERVER_OIDC_SESSION_TTL_PARAM = 'dbx_session_ttl';
270
+ /**
271
+ * Custom oidc-provider client metadata field for a client's maximum requestable login duration (seconds).
272
+ */ var DBX_FIREBASE_SERVER_OIDC_MAX_SESSION_TTL_CLIENT_METADATA = 'dbx_max_session_ttl';
273
+ /**
274
+ * Clamps a requested login duration into the allowed range.
275
+ *
276
+ * Resolution order:
277
+ * 1. Pick the requested value, falling back to the configured default when omitted.
278
+ * 2. Compute the effective ceiling as `min(serverMax, clientMax-or-Infinity)`.
279
+ * 3. Clamp the value to `[serverMin, ceiling]`.
280
+ *
281
+ * @param input - The clamp inputs (requested value, client cap, server bounds, default).
282
+ * @returns The clamped duration in seconds.
283
+ *
284
+ * @example
285
+ * ```typescript
286
+ * resolveLoginDurationSeconds({
287
+ * requestedSeconds: 60 * 24 * 60 * 60, // 60 days
288
+ * clientMaxSeconds: 30 * 24 * 60 * 60, // 30 days client cap
289
+ * serverMaxSeconds: 90 * 24 * 60 * 60, // 90 days global cap
290
+ * serverMinSeconds: 60 * 60, // 1 hour floor
291
+ * defaultSeconds: 30 * 24 * 60 * 60
292
+ * }); // → 30 * 24 * 60 * 60 (clamped to client max)
293
+ * ```
294
+ */ function resolveLoginDurationSeconds(input) {
295
+ var _input_requestedSeconds, _input_clientMaxSeconds;
296
+ var raw = (_input_requestedSeconds = input.requestedSeconds) !== null && _input_requestedSeconds !== void 0 ? _input_requestedSeconds : input.defaultSeconds;
297
+ var ceiling = Math.min(input.serverMaxSeconds, (_input_clientMaxSeconds = input.clientMaxSeconds) !== null && _input_clientMaxSeconds !== void 0 ? _input_clientMaxSeconds : Infinity);
298
+ return Math.max(input.serverMinSeconds, Math.min(raw, ceiling));
299
+ }
300
+ // MARK: Param parsing
301
+ /**
302
+ * Parses a raw `dbx_session_ttl` value (string or number, from URL query / form / persisted interaction params)
303
+ * into a positive integer number of seconds. Returns `undefined` when missing or invalid so the caller falls
304
+ * back to a default.
305
+ *
306
+ * @param raw - The raw value to parse.
307
+ * @returns The parsed positive integer seconds, or `undefined` when absent/invalid.
308
+ */ function parseRequestedSessionTtlSeconds(raw) {
309
+ var result;
310
+ if (typeof raw === 'string' && raw.length > 0) {
311
+ var parsed = Number(raw);
312
+ if (Number.isFinite(parsed) && Number.isInteger(parsed) && parsed > 0) {
313
+ result = parsed;
314
+ }
315
+ } else if (typeof raw === 'number' && Number.isFinite(raw) && Number.isInteger(raw) && raw > 0) {
316
+ result = raw;
317
+ }
318
+ return result;
319
+ }
320
+ /**
321
+ * Reads {@link DBX_FIREBASE_SERVER_OIDC_SESSION_TTL_PARAM} from a KoaContextWithOIDC and parses it to a positive integer
322
+ * number of seconds. Returns `undefined` when missing or invalid (so the caller falls back to a default).
323
+ *
324
+ * @param ctx - The OIDC Koa context, or undefined.
325
+ * @returns The parsed positive integer seconds, or `undefined` when absent/invalid.
326
+ */ function readRequestedSessionTtlSeconds(ctx) {
327
+ var _ctx_oidc;
328
+ var params = ctx === null || ctx === void 0 ? void 0 : (_ctx_oidc = ctx.oidc) === null || _ctx_oidc === void 0 ? void 0 : _ctx_oidc.params;
329
+ var raw = params != null ? params[DBX_FIREBASE_SERVER_OIDC_SESSION_TTL_PARAM] : undefined;
330
+ return parseRequestedSessionTtlSeconds(raw);
331
+ }
332
+ // MARK: Grant remaining time
333
+ /**
334
+ * Inspects oidc-provider entities on the context to find the resolved Grant expiry.
335
+ *
336
+ * Returns the remaining seconds until the Grant expires, or `undefined` when no Grant is bound to the context.
337
+ *
338
+ * @param ctx - The OIDC Koa context, or undefined.
339
+ * @returns The remaining seconds until grant expiry, or `undefined` when no grant is bound or it has expired.
340
+ */ function readRemainingGrantSeconds(ctx) {
341
+ var _ctx_oidc_entities, _ctx_oidc;
342
+ var grant = ctx === null || ctx === void 0 ? void 0 : (_ctx_oidc = ctx.oidc) === null || _ctx_oidc === void 0 ? void 0 : (_ctx_oidc_entities = _ctx_oidc.entities) === null || _ctx_oidc_entities === void 0 ? void 0 : _ctx_oidc_entities.Grant;
343
+ var result;
344
+ if ((grant === null || grant === void 0 ? void 0 : grant.exp) != null && Number.isFinite(grant.exp)) {
345
+ var now = Math.floor(Date.now() / 1000);
346
+ var remaining = grant.exp - now;
347
+ if (remaining > 0) {
348
+ result = remaining;
349
+ }
350
+ }
351
+ return result;
352
+ }
353
+
208
354
  /**
209
355
  * Query for JwksKey documents with a specific status.
210
356
  *
@@ -1609,7 +1755,8 @@ function _ts_generator$8(thisArg, body) {
1609
1755
  createOidcClient: createOidcClientFactory(context),
1610
1756
  updateOidcClient: updateOidcClientFactory(context),
1611
1757
  rotateOidcClientSecret: rotateOidcClientSecretFactory(context),
1612
- deleteOidcClient: deleteOidcClientFactory(context)
1758
+ deleteOidcClient: deleteOidcClientFactory(context),
1759
+ deleteOidcToken: deleteOidcTokenFactory(context)
1613
1760
  };
1614
1761
  }
1615
1762
  // MARK: Actions
@@ -1748,6 +1895,59 @@ function _ts_generator$8(thisArg, body) {
1748
1895
  })();
1749
1896
  });
1750
1897
  }
1898
+ /**
1899
+ * Factory for the `deleteOidcToken` action.
1900
+ *
1901
+ * Asserts the target {@link OidcEntryDocument} is of type `Grant`, then delegates to
1902
+ * {@link OidcService.revokeGrant} so all grantable token entries (`AccessToken`,
1903
+ * `RefreshToken`, `AuthorizationCode`, `DeviceCode`, `BackchannelAuthenticationRequest`)
1904
+ * sharing the grant id are deleted along with the Grant entry itself.
1905
+ *
1906
+ * Per-user authorization (this user owns this Grant) is enforced upstream via the
1907
+ * model permission service before this action runs.
1908
+ *
1909
+ * @param context - the OIDC model server actions context
1910
+ * @returns a transform function factory for revoking OIDC grants
1911
+ */ function deleteOidcTokenFactory(context) {
1912
+ var oidcService = context.oidcService, firebaseServerActionTransformFunctionFactory = context.firebaseServerActionTransformFunctionFactory;
1913
+ return firebaseServerActionTransformFunctionFactory(firebase.deleteOidcTokenParamsType, function(_params) {
1914
+ return _async_to_generator$8(function() {
1915
+ return _ts_generator$8(this, function(_state) {
1916
+ return [
1917
+ 2,
1918
+ function(document) {
1919
+ return _async_to_generator$8(function() {
1920
+ var data;
1921
+ return _ts_generator$8(this, function(_state) {
1922
+ switch(_state.label){
1923
+ case 0:
1924
+ return [
1925
+ 4,
1926
+ document.snapshotData()
1927
+ ];
1928
+ case 1:
1929
+ data = _state.sent();
1930
+ if ((data === null || data === void 0 ? void 0 : data.type) !== 'Grant') {
1931
+ throw new Error('Only Grant entries can be revoked through this endpoint.');
1932
+ }
1933
+ return [
1934
+ 4,
1935
+ oidcService.revokeGrant(document.id)
1936
+ ];
1937
+ case 2:
1938
+ _state.sent();
1939
+ return [
1940
+ 2
1941
+ ];
1942
+ }
1943
+ });
1944
+ })();
1945
+ }
1946
+ ];
1947
+ });
1948
+ })();
1949
+ });
1950
+ }
1751
1951
 
1752
1952
  function asyncGeneratorStep$7(gen, resolve, reject, _next, _throw, key, arg) {
1753
1953
  try {
@@ -2007,6 +2207,9 @@ function _ts_generator$7(thisArg, body) {
2007
2207
  if (params.jwks_uri) {
2008
2208
  properties.jwks_uri = params.jwks_uri;
2009
2209
  }
2210
+ if (params.dbx_max_session_ttl != null) {
2211
+ properties.dbx_max_session_ttl = params.dbx_max_session_ttl;
2212
+ }
2010
2213
  // Merge any pre-validated metadata (e.g., inline jwks for private_key_jwt in tests)
2011
2214
  if (validatedMetadata === null || validatedMetadata === void 0 ? void 0 : validatedMetadata.jwks) {
2012
2215
  properties.jwks = validatedMetadata.jwks;
@@ -2064,7 +2267,7 @@ function _ts_generator$7(thisArg, body) {
2064
2267
  * @throws When the client is not found.
2065
2268
  */ function updateClient(clientId, params) {
2066
2269
  return _async_to_generator$7(function() {
2067
- var provider, ProviderClient, existing, updatedMetadata, _params_logo_uri, _params_client_uri, client;
2270
+ var provider, ProviderClient, existing, updatedMetadata, _params_logo_uri, _params_client_uri, _params_dbx_max_session_ttl, client;
2068
2271
  return _ts_generator$7(this, function(_state) {
2069
2272
  switch(_state.label){
2070
2273
  case 0:
@@ -2093,6 +2296,9 @@ function _ts_generator$7(thisArg, body) {
2093
2296
  if (params.client_uri !== undefined) {
2094
2297
  updatedMetadata.client_uri = (_params_client_uri = params.client_uri) !== null && _params_client_uri !== void 0 ? _params_client_uri : undefined;
2095
2298
  }
2299
+ if (params.dbx_max_session_ttl !== undefined) {
2300
+ updatedMetadata.dbx_max_session_ttl = (_params_dbx_max_session_ttl = params.dbx_max_session_ttl) !== null && _params_dbx_max_session_ttl !== void 0 ? _params_dbx_max_session_ttl : undefined;
2301
+ }
2096
2302
  // Mirrors oidc-provider's lib/helpers/add_client.js: re-validates and persists.
2097
2303
  return [
2098
2304
  4,
@@ -2275,13 +2481,15 @@ function _object_spread_props$1(target, source) {
2275
2481
  }
2276
2482
  // MARK: Provider Factories
2277
2483
  /**
2278
- * Factory that creates an {@link OidcModelServerActions} instance from the injected {@link OidcClientService}.
2484
+ * Factory that creates an {@link OidcModelServerActions} instance from the injected services.
2279
2485
  *
2280
- * @param oidcClientService - the OIDC client service to wire into the server actions
2486
+ * @param oidcClientService - the OIDC client service for client CRUD actions
2487
+ * @param oidcService - the core OIDC service for grant revocation
2281
2488
  * @returns the configured OidcModelServerActions instance
2282
- */ function oidcModelServerActionsFactory(oidcClientService) {
2489
+ */ function oidcModelServerActionsFactory(oidcClientService, oidcService) {
2283
2490
  var context = _object_spread_props$1(_object_spread$4({}, firebaseServer.firebaseServerActionsContext()), {
2284
- oidcClientService: oidcClientService
2491
+ oidcClientService: oidcClientService,
2492
+ oidcService: oidcService
2285
2493
  });
2286
2494
  return oidcModelServerActions(context);
2287
2495
  }
@@ -2307,7 +2515,8 @@ function _object_spread_props$1(target, source) {
2307
2515
  provide: OidcModelServerActions,
2308
2516
  useFactory: oidcModelServerActionsFactory,
2309
2517
  inject: [
2310
- OidcClientService
2518
+ OidcClientService,
2519
+ exports.OidcService
2311
2520
  ]
2312
2521
  }
2313
2522
  ]
@@ -2374,7 +2583,7 @@ function _define_property$a(obj, key, value) {
2374
2583
  }
2375
2584
  return obj;
2376
2585
  }
2377
- function _instanceof$1(left, right) {
2586
+ function _instanceof$2(left, right) {
2378
2587
  "@swc/helpers - instanceof";
2379
2588
  if (right != null && typeof Symbol !== "undefined" && right[Symbol.hasInstance]) {
2380
2589
  return !!right[Symbol.hasInstance](left);
@@ -2499,13 +2708,14 @@ function _ts_generator$6(thisArg, body) {
2499
2708
  // MARK: Adapter
2500
2709
  /**
2501
2710
  * Model types that support grantId-based revocation.
2502
- */ var GRANTABLE_MODELS = new Set([
2711
+ */ var GRANTABLE_MODEL_NAMES = [
2503
2712
  'AccessToken',
2504
2713
  'AuthorizationCode',
2505
2714
  'RefreshToken',
2506
2715
  'DeviceCode',
2507
2716
  'BackchannelAuthenticationRequest'
2508
- ]);
2717
+ ];
2718
+ var GRANTABLE_MODELS = new Set(GRANTABLE_MODEL_NAMES);
2509
2719
  /**
2510
2720
  * Creates an oidc-provider adapter constructor backed by Firestore via {@link OidcServerFirestoreCollections}.
2511
2721
  *
@@ -2536,22 +2746,44 @@ function _ts_generator$6(thisArg, body) {
2536
2746
  key: "upsert",
2537
2747
  value: function upsert(id, payload, expiresIn) {
2538
2748
  return _async_to_generator$6(function() {
2539
- var o, data, doc;
2749
+ var _payload_uid, _payload_clientId, o, uid, clientId, iat, createdAtIso, createdAt, data, doc;
2540
2750
  return _ts_generator$6(this, function(_state) {
2541
2751
  switch(_state.label){
2542
2752
  case 0:
2543
2753
  // Set ownership key for Client entries so firestore rules can restrict reads by owner.
2544
2754
  // The firestoreOwnerKey is passed through the payload metadata by OidcClientService.
2545
2755
  o = this.name === firebase.OIDC_ENTRY_CLIENT_TYPE ? payload.firestoreOwnerKey : undefined;
2756
+ // oidc-provider uses `accountId` as the user identifier on Grant, AccessToken,
2757
+ // RefreshToken, etc. — `payload.uid` is only populated on Session/Interaction.
2758
+ // Fall back to `accountId` so the indexed `uid` column works for all entry types.
2759
+ uid = (_payload_uid = payload.uid) !== null && _payload_uid !== void 0 ? _payload_uid : payload.accountId;
2760
+ // oidc-provider stores the OAuth client id as `clientId` on every grantable
2761
+ // model (Grant, AccessToken, RefreshToken, AuthorizationCode, DeviceCode, etc.).
2762
+ // Client entries themselves use `client_id` in the payload, so we read both.
2763
+ clientId = (_payload_clientId = payload.clientId) !== null && _payload_clientId !== void 0 ? _payload_clientId : payload.client_id;
2764
+ // Derive a stable `createdAt` from the payload so it survives upserts
2765
+ // (e.g. `consume`). Grant/AccessToken/RefreshToken/AuthorizationCode all
2766
+ // inherit `iat` (epoch seconds) from BaseToken; Client entries use
2767
+ // `created_at` (ISO string).
2768
+ iat = payload.iat;
2769
+ createdAtIso = payload.created_at;
2770
+ if (typeof iat === 'number') {
2771
+ createdAt = util.unixDateTimeSecondsNumberToDate(iat);
2772
+ } else if (createdAtIso) {
2773
+ createdAt = date.safeToJsDate(createdAtIso);
2774
+ }
2546
2775
  data = _object_spread$3({
2547
2776
  type: this.name,
2548
2777
  payload: encryptionService.encryptAdapterPayload(payload),
2549
2778
  o: o,
2550
- uid: payload.uid,
2779
+ uid: uid,
2551
2780
  grantId: payload.grantId,
2781
+ clientId: clientId,
2552
2782
  userCode: payload.userCode,
2553
2783
  consumed: payload.consumed
2554
- }, expiresIn ? {
2784
+ }, createdAt ? {
2785
+ createdAt: createdAt
2786
+ } : undefined, expiresIn ? {
2555
2787
  expiresAt: util.unixDateTimeSecondsNumberToDate(util.unixDateTimeSecondsNumberForNow() + expiresIn)
2556
2788
  } : undefined);
2557
2789
  doc = this.collection.documentAccessor().loadDocumentForId(id);
@@ -2760,7 +2992,7 @@ function _ts_generator$6(thisArg, body) {
2760
2992
  * @returns the decrypted adapter payload, or undefined if the entry has expired
2761
2993
  */ key: "_toPayload",
2762
2994
  value: function _toPayload(data) {
2763
- var expiresDate = data.expiresAt ? _instanceof$1(data.expiresAt, Date) ? data.expiresAt : data.expiresAt.toDate() : undefined;
2995
+ var expiresDate = data.expiresAt ? _instanceof$2(data.expiresAt, Date) ? data.expiresAt : data.expiresAt.toDate() : undefined;
2764
2996
  var isExpired = expiresDate != null && expiresDate < new Date();
2765
2997
  return isExpired ? undefined : encryptionService.decryptAdapterPayload(data.payload);
2766
2998
  }
@@ -3365,6 +3597,36 @@ function _ts_generator$5(thisArg, body) {
3365
3597
  return this._getProvider();
3366
3598
  }
3367
3599
  },
3600
+ {
3601
+ // MARK: Login Duration
3602
+ /**
3603
+ * Resolves the login-duration TTL (seconds) for a Grant being created from a fresh consent submission.
3604
+ *
3605
+ * The Grant TTL configured on the oidc-provider only fires when oidc-provider's koa middleware drives
3606
+ * `grant.save()` (so `AsyncLocalStorage` carries the request context). Our consent flow saves grants
3607
+ * from a NestJS controller, so we resolve the TTL up-front and pre-set `grant.expiresIn` before saving.
3608
+ *
3609
+ * Mirrors the resolution used by the `Grant`/`Session` TTL functions in {@link buildProviderConfiguration}.
3610
+ *
3611
+ * @param requestedRawTtl - The raw `dbx_session_ttl` value from `interaction.params`, if any.
3612
+ * @param clientPayload - The persisted client metadata, used to read the per-client `dbx_max_session_ttl` cap.
3613
+ * @returns The resolved Grant TTL in seconds.
3614
+ */ key: "resolveLoginDurationForGrant",
3615
+ value: function resolveLoginDurationForGrant(requestedRawTtl, clientPayload) {
3616
+ var _config_maxRequestedLoginDuration, _config_minRequestedLoginDuration, _config_defaultRequestedLoginDuration;
3617
+ var config = this.config;
3618
+ var serverMaxSeconds = (_config_maxRequestedLoginDuration = config.maxRequestedLoginDuration) !== null && _config_maxRequestedLoginDuration !== void 0 ? _config_maxRequestedLoginDuration : DEFAULT_MAX_REQUESTED_LOGIN_DURATION_SECONDS;
3619
+ var serverMinSeconds = (_config_minRequestedLoginDuration = config.minRequestedLoginDuration) !== null && _config_minRequestedLoginDuration !== void 0 ? _config_minRequestedLoginDuration : DEFAULT_MIN_REQUESTED_LOGIN_DURATION_SECONDS;
3620
+ var defaultSeconds = (_config_defaultRequestedLoginDuration = config.defaultRequestedLoginDuration) !== null && _config_defaultRequestedLoginDuration !== void 0 ? _config_defaultRequestedLoginDuration : config.tokenLifetimes.grant;
3621
+ return resolveLoginDurationSeconds({
3622
+ requestedSeconds: parseRequestedSessionTtlSeconds(requestedRawTtl),
3623
+ clientMaxSeconds: clientPayload === null || clientPayload === void 0 ? void 0 : clientPayload.dbx_max_session_ttl,
3624
+ serverMaxSeconds: serverMaxSeconds,
3625
+ serverMinSeconds: serverMinSeconds,
3626
+ defaultSeconds: defaultSeconds
3627
+ });
3628
+ }
3629
+ },
3368
3630
  {
3369
3631
  key: "verifyAccessToken",
3370
3632
  value: // MARK: Token Verification
@@ -3442,6 +3704,67 @@ function _ts_generator$5(thisArg, body) {
3442
3704
  }).call(this);
3443
3705
  }
3444
3706
  },
3707
+ {
3708
+ key: "revokeGrant",
3709
+ value: // MARK: Grant Revocation
3710
+ /**
3711
+ * Revokes a Grant entry and every grantable token entry that references it.
3712
+ *
3713
+ * Iterates through every grantable model (`AccessToken`, `AuthorizationCode`,
3714
+ * `RefreshToken`, `DeviceCode`, `BackchannelAuthenticationRequest`) and calls
3715
+ * the adapter's `revokeByGrantId` to delete all matching entries, then
3716
+ * deletes the Grant adapter entry itself. After this resolves, any token
3717
+ * referencing the grant is gone — `verifyAccessToken` returns `undefined`
3718
+ * and a `grant_type=refresh_token` exchange fails with `invalid_grant`.
3719
+ *
3720
+ * @param grantId - the grant id (and Grant adapter entry id) to revoke
3721
+ * @throws when the Grant entry does not exist
3722
+ */ function revokeGrant(grantId) {
3723
+ return _async_to_generator$5(function() {
3724
+ var provider, ProviderGrant, existing;
3725
+ return _ts_generator$5(this, function(_state) {
3726
+ switch(_state.label){
3727
+ case 0:
3728
+ return [
3729
+ 4,
3730
+ this.getProvider()
3731
+ ];
3732
+ case 1:
3733
+ provider = _state.sent();
3734
+ ProviderGrant = provider.Grant;
3735
+ return [
3736
+ 4,
3737
+ ProviderGrant.adapter.find(grantId)
3738
+ ];
3739
+ case 2:
3740
+ existing = _state.sent();
3741
+ if (!existing) {
3742
+ throw new Error('Grant not found.');
3743
+ }
3744
+ return [
3745
+ 4,
3746
+ Promise.all(GRANTABLE_MODEL_NAMES.map(function(modelName) {
3747
+ var _Model_adapter_revokeByGrantId, _Model_adapter;
3748
+ var Model = provider[modelName];
3749
+ return Model === null || Model === void 0 ? void 0 : (_Model_adapter = Model.adapter) === null || _Model_adapter === void 0 ? void 0 : (_Model_adapter_revokeByGrantId = _Model_adapter.revokeByGrantId) === null || _Model_adapter_revokeByGrantId === void 0 ? void 0 : _Model_adapter_revokeByGrantId.call(_Model_adapter, grantId);
3750
+ }))
3751
+ ];
3752
+ case 3:
3753
+ _state.sent();
3754
+ return [
3755
+ 4,
3756
+ ProviderGrant.adapter.destroy(grantId)
3757
+ ];
3758
+ case 4:
3759
+ _state.sent();
3760
+ return [
3761
+ 2
3762
+ ];
3763
+ }
3764
+ });
3765
+ }).call(this);
3766
+ }
3767
+ },
3445
3768
  {
3446
3769
  key: "findClientPayload",
3447
3770
  value: /**
@@ -3478,7 +3801,8 @@ function _ts_generator$5(thisArg, body) {
3478
3801
  token_endpoint_auth_method: existing.token_endpoint_auth_method,
3479
3802
  logo_uri: existing.logo_uri,
3480
3803
  client_uri: existing.client_uri,
3481
- created_at: existing.created_at
3804
+ created_at: existing.created_at,
3805
+ dbx_max_session_ttl: existing.dbx_max_session_ttl
3482
3806
  };
3483
3807
  }
3484
3808
  return [
@@ -3503,8 +3827,21 @@ function _ts_generator$5(thisArg, body) {
3503
3827
  */ key: "buildProviderConfiguration",
3504
3828
  value: function buildProviderConfiguration(cookieKeys) {
3505
3829
  var _this = this;
3830
+ var _config_maxRequestedLoginDuration, _config_minRequestedLoginDuration;
3506
3831
  var config = this.config;
3507
3832
  var providerConfig = this.providerConfigService.providerConfig;
3833
+ var serverMaxSeconds = (_config_maxRequestedLoginDuration = config.maxRequestedLoginDuration) !== null && _config_maxRequestedLoginDuration !== void 0 ? _config_maxRequestedLoginDuration : DEFAULT_MAX_REQUESTED_LOGIN_DURATION_SECONDS;
3834
+ var serverMinSeconds = (_config_minRequestedLoginDuration = config.minRequestedLoginDuration) !== null && _config_minRequestedLoginDuration !== void 0 ? _config_minRequestedLoginDuration : DEFAULT_MIN_REQUESTED_LOGIN_DURATION_SECONDS;
3835
+ var overrideDefaultSeconds = config.defaultRequestedLoginDuration;
3836
+ var resolveDurationFromCtx = function resolveDurationFromCtx(ctx, client, perModelDefaultSeconds) {
3837
+ return resolveLoginDurationSeconds({
3838
+ requestedSeconds: readRequestedSessionTtlSeconds(ctx),
3839
+ clientMaxSeconds: client === null || client === void 0 ? void 0 : client.dbx_max_session_ttl,
3840
+ serverMaxSeconds: serverMaxSeconds,
3841
+ serverMinSeconds: serverMinSeconds,
3842
+ defaultSeconds: overrideDefaultSeconds !== null && overrideDefaultSeconds !== void 0 ? overrideDefaultSeconds : perModelDefaultSeconds
3843
+ });
3844
+ };
3508
3845
  return _object_spread_props(_object_spread$2({
3509
3846
  routes: _object_spread$2({}, this.providerConfigService.routes),
3510
3847
  claims: _object_spread$2({}, providerConfig.claims),
@@ -3525,13 +3862,46 @@ function _ts_generator$5(thisArg, body) {
3525
3862
  enabled: this.providerConfigService.oidcRegistrationRouteEnabled
3526
3863
  }
3527
3864
  },
3865
+ extraParams: [
3866
+ DBX_FIREBASE_SERVER_OIDC_SESSION_TTL_PARAM
3867
+ ],
3868
+ extraClientMetadata: {
3869
+ properties: [
3870
+ DBX_FIREBASE_SERVER_OIDC_MAX_SESSION_TTL_CLIENT_METADATA
3871
+ ],
3872
+ validator: function validator(_ctx, key, value) {
3873
+ if (key !== DBX_FIREBASE_SERVER_OIDC_MAX_SESSION_TTL_CLIENT_METADATA) {
3874
+ return;
3875
+ }
3876
+ if (value === undefined || value === null) {
3877
+ return;
3878
+ }
3879
+ if (typeof value !== 'number' || !Number.isInteger(value) || value <= 0) {
3880
+ throw new oidcProvider.errors.InvalidClientMetadata("".concat(DBX_FIREBASE_SERVER_OIDC_MAX_SESSION_TTL_CLIENT_METADATA, " must be a positive integer (seconds)."));
3881
+ }
3882
+ if (value > serverMaxSeconds) {
3883
+ throw new oidcProvider.errors.InvalidClientMetadata("".concat(DBX_FIREBASE_SERVER_OIDC_MAX_SESSION_TTL_CLIENT_METADATA, " cannot exceed the server max of ").concat(serverMaxSeconds, " seconds."));
3884
+ }
3885
+ }
3886
+ },
3528
3887
  ttl: {
3529
3888
  AccessToken: config.tokenLifetimes.accessToken,
3530
3889
  IdToken: config.tokenLifetimes.idToken,
3531
3890
  AuthorizationCode: config.tokenLifetimes.authorizationCode,
3532
- RefreshToken: config.tokenLifetimes.refreshToken,
3533
- Session: 14 * 24 * 60 * 60,
3534
- Grant: 14 * 24 * 60 * 60,
3891
+ RefreshToken: function RefreshToken(ctx, _refreshToken, _client) {
3892
+ // The token endpoint's ctx.oidc.params holds the token-request body (code, code_verifier, …),
3893
+ // not the original auth params — `dbx_session_ttl` is no longer reachable here. The Grant
3894
+ // entity is bound on both initial issuance (authorization_code → token) and rotation
3895
+ // (refresh_token → token), and its `exp` already encodes the resolved login duration.
3896
+ var remaining = readRemainingGrantSeconds(ctx);
3897
+ return Math.min(remaining !== null && remaining !== void 0 ? remaining : config.tokenLifetimes.refreshToken, config.tokenLifetimes.refreshToken);
3898
+ },
3899
+ Session: function Session(ctx, _session) {
3900
+ return resolveDurationFromCtx(ctx, undefined, config.tokenLifetimes.session);
3901
+ },
3902
+ Grant: function Grant(ctx, _grant, client) {
3903
+ return resolveDurationFromCtx(ctx, client, config.tokenLifetimes.grant);
3904
+ },
3535
3905
  Interaction: 60 * 60,
3536
3906
  DeviceCode: 10 * 60
3537
3907
  },
@@ -3650,7 +4020,7 @@ function _ts_generator$5(thisArg, body) {
3650
4020
  value: // MARK: Internal
3651
4021
  function _buildProvider() {
3652
4022
  return _async_to_generator$5(function() {
3653
- var _this, config, findAccount, signingKey, result, getEncryptionKey, cookieKey, adapterFactory, providerConfiguration, _ref, ProviderClass, provider;
4023
+ var _this, _config_trustProxy, config, findAccount, signingKey, result, getEncryptionKey, cookieKey, adapterFactory, providerConfiguration, _ref, ProviderClass, provider;
3654
4024
  return _ts_generator$5(this, function(_state) {
3655
4025
  switch(_state.label){
3656
4026
  case 0:
@@ -3701,6 +4071,12 @@ function _ts_generator$5(thisArg, body) {
3701
4071
  ]
3702
4072
  }
3703
4073
  }));
4074
+ // Trust upstream X-Forwarded-* headers when running behind a reverse proxy
4075
+ // (e.g. Firebase Hosting → Cloud Run). Without this, oidc-provider builds
4076
+ // the interaction `returnTo` URL off the Cloud Run host instead of the
4077
+ // issuer's canonical host, so cookies set on the issuer host are not sent
4078
+ // on resume → "authorization request has expired".
4079
+ provider.proxy = (_config_trustProxy = config.trustProxy) !== null && _config_trustProxy !== void 0 ? _config_trustProxy : true;
3704
4080
  if (config.suppressBodyParserWarning) ;
3705
4081
  return [
3706
4082
  2,
@@ -3784,7 +4160,7 @@ function _define_property$6(obj, key, value) {
3784
4160
  }
3785
4161
  return obj;
3786
4162
  }
3787
- function _instanceof(left, right) {
4163
+ function _instanceof$1(left, right) {
3788
4164
  "@swc/helpers - instanceof";
3789
4165
  if (right != null && typeof Symbol !== "undefined" && right[Symbol.hasInstance]) {
3790
4166
  return !!right[Symbol.hasInstance](left);
@@ -3948,7 +4324,7 @@ function _ts_generator$4(thisArg, body) {
3948
4324
  ];
3949
4325
  case 3:
3950
4326
  err = _state.sent();
3951
- if (_instanceof(err, common.UnauthorizedException)) {
4327
+ if (_instanceof$1(err, common.UnauthorizedException)) {
3952
4328
  throw err;
3953
4329
  }
3954
4330
  this.logger.error('Bearer token verification failed', err);
@@ -4413,13 +4789,20 @@ function _ts_generator$3(thisArg, body) {
4413
4789
  value: /**
4414
4790
  * Finds an existing grant by ID, or creates a new one.
4415
4791
  *
4792
+ * For new grants, optionally pre-set `expiresIn` so {@link Grant#save} writes the requested TTL
4793
+ * instead of falling through to the configured Grant TTL function (which cannot read the original
4794
+ * `dbx_session_ttl` request param when save is driven from a NestJS controller — see
4795
+ * {@link OidcService#resolveLoginDurationForGrant}).
4796
+ *
4416
4797
  * @param grantId - the existing grant ID to look up, or undefined to create a new grant
4417
4798
  * @param accountId - the account ID for creating a new grant
4418
4799
  * @param clientId - the client ID for creating a new grant
4800
+ * @param expiresInSeconds - optional TTL (seconds) to assign to a newly-created grant
4419
4801
  * @returns the found or newly created grant
4420
- */ function findOrCreateGrant(grantId, accountId, clientId) {
4802
+ */ // eslint-disable-next-line @typescript-eslint/max-params -- positional args mirror the existing call sites
4803
+ function findOrCreateGrant(grantId, accountId, clientId, expiresInSeconds) {
4421
4804
  return _async_to_generator$3(function() {
4422
- var provider, grant, found;
4805
+ var provider, grant, found, grantInit;
4423
4806
  return _ts_generator$3(this, function(_state) {
4424
4807
  switch(_state.label){
4425
4808
  case 0:
@@ -4448,10 +4831,14 @@ function _ts_generator$3(thisArg, body) {
4448
4831
  4
4449
4832
  ];
4450
4833
  case 3:
4451
- grant = new provider.Grant({
4834
+ grantInit = {
4452
4835
  accountId: accountId,
4453
4836
  clientId: clientId
4454
- });
4837
+ };
4838
+ if (expiresInSeconds != null) {
4839
+ grantInit.expiresIn = expiresInSeconds;
4840
+ }
4841
+ grant = new provider.Grant(grantInit);
4455
4842
  _state.label = 4;
4456
4843
  case 4:
4457
4844
  return [
@@ -4540,6 +4927,14 @@ function _define_property$3(obj, key, value) {
4540
4927
  }
4541
4928
  return obj;
4542
4929
  }
4930
+ function _instanceof(left, right) {
4931
+ "@swc/helpers - instanceof";
4932
+ if (right != null && typeof Symbol !== "undefined" && right[Symbol.hasInstance]) {
4933
+ return !!right[Symbol.hasInstance](left);
4934
+ } else {
4935
+ return left instanceof right;
4936
+ }
4937
+ }
4543
4938
  function _iterable_to_array_limit(arr, i) {
4544
4939
  var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
4545
4940
  if (_i == null) return;
@@ -4677,6 +5072,14 @@ function _ts_generator$2(thisArg, body) {
4677
5072
  };
4678
5073
  }
4679
5074
  }
5075
+ /**
5076
+ * OIDC scopes that the server always grants on consent when they were
5077
+ * requested, regardless of whether the frontend included them in
5078
+ * `grantedOIDCScopes`. `openid` is required for any OIDC flow, so the
5079
+ * UI shows it as non-deselectable and the server enforces it here.
5080
+ */ var ALWAYS_GRANTED_OIDC_SCOPES = [
5081
+ 'openid'
5082
+ ];
4680
5083
  // MARK: Interaction Controller
4681
5084
  /**
4682
5085
  * Controller for OIDC interaction endpoints (login/consent).
@@ -4688,14 +5091,16 @@ function _ts_generator$2(thisArg, body) {
4688
5091
  * Firebase Auth ID token and bypass the oidc-provider interaction cookie
4689
5092
  * (which is scoped to the frontend path set by `interactions.url`).
4690
5093
  */ exports.OidcInteractionController = /*#__PURE__*/ function() {
4691
- function OidcInteractionController(oidcInteractionService, oidcProviderConfigService, accountService) {
5094
+ function OidcInteractionController(oidcInteractionService, oidcProviderConfigService, accountService, oidcService) {
4692
5095
  _class_call_check$2(this, OidcInteractionController);
4693
5096
  _define_property$3(this, "oidcInteractionService", void 0);
4694
5097
  _define_property$3(this, "oidcProviderConfigService", void 0);
4695
5098
  _define_property$3(this, "accountService", void 0);
5099
+ _define_property$3(this, "oidcService", void 0);
4696
5100
  this.oidcInteractionService = oidcInteractionService;
4697
5101
  this.oidcProviderConfigService = oidcProviderConfigService;
4698
5102
  this.accountService = accountService;
5103
+ this.oidcService = oidcService;
4699
5104
  }
4700
5105
  _create_class$2(OidcInteractionController, [
4701
5106
  {
@@ -4829,7 +5234,7 @@ function _ts_generator$2(thisArg, body) {
4829
5234
  * @throws {HttpException} 400 when the consent interaction cannot be completed.
4830
5235
  */ function postConsent(uid, body, res) {
4831
5236
  return _async_to_generator$2(function() {
4832
- var _ref, redirectTo, interaction, prompt, params, session, grant, _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, _step_value, indicator, scopes, grantId, redirectTo1;
5237
+ var _ref, _ref1, _prompt_details_missingOIDCScope, _prompt_details_missingOIDCClaims, _prompt_details_missingResourceScopes, redirectTo, interaction, prompt, params, session, clientId, requestedRawTtl, clientPayload, clientMaxSessionTtl, expiresInSeconds, grant, missingOIDCScope, _resolveEffectiveSubset, granted, rejected, _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, value, missingOIDCClaims, _resolveEffectiveSubset1, granted1, rejected1, missingResourceScopes, _iteratorNormalCompletion1, _didIteratorError1, _iteratorError1, _iterator1, _step1, _step_value, indicator, scopes, _body_grantedResourceScopes, requestedSubset, _resolveEffectiveSubset2, granted2, rejected2, _iteratorNormalCompletion2, _didIteratorError2, _iteratorError2, _iterator2, _step2, value1, grantId, redirectTo1, err;
4833
5238
  return _ts_generator$2(this, function(_state) {
4834
5239
  switch(_state.label){
4835
5240
  case 0:
@@ -4843,9 +5248,9 @@ function _ts_generator$2(thisArg, body) {
4843
5248
  case 2:
4844
5249
  _state.trys.push([
4845
5250
  2,
4846
- 9,
5251
+ 10,
4847
5252
  ,
4848
- 10
5253
+ 11
4849
5254
  ]);
4850
5255
  if (!!body.approved) return [
4851
5256
  3,
@@ -4876,24 +5281,40 @@ function _ts_generator$2(thisArg, body) {
4876
5281
  case 5:
4877
5282
  interaction = _state.sent();
4878
5283
  prompt = interaction.prompt, params = interaction.params, session = interaction.session;
5284
+ clientId = params.client_id;
5285
+ // Resolve the requested login duration up-front. The configured Grant TTL function (in
5286
+ // OidcService.buildProviderConfiguration) only fires when oidc-provider's koa middleware
5287
+ // drives `grant.save()`, so its `ctx.oidc.params` lookup of `dbx_session_ttl` returns
5288
+ // undefined when the consent submit runs in this controller. We pre-set `expiresIn` on
5289
+ // newly-created grants so they persist with the correct TTL.
5290
+ requestedRawTtl = params[DBX_FIREBASE_SERVER_OIDC_SESSION_TTL_PARAM];
4879
5291
  return [
4880
5292
  4,
4881
- this.oidcInteractionService.findOrCreateGrant(interaction.grantId, (_ref = session === null || session === void 0 ? void 0 : session.accountId) !== null && _ref !== void 0 ? _ref : '', params.client_id)
5293
+ this.oidcService.findClientPayload(clientId)
4882
5294
  ];
4883
5295
  case 6:
5296
+ clientPayload = _state.sent();
5297
+ clientMaxSessionTtl = (_ref = clientPayload === null || clientPayload === void 0 ? void 0 : clientPayload.dbx_max_session_ttl) !== null && _ref !== void 0 ? _ref : undefined;
5298
+ expiresInSeconds = this.oidcService.resolveLoginDurationForGrant(requestedRawTtl, {
5299
+ dbx_max_session_ttl: clientMaxSessionTtl
5300
+ });
5301
+ return [
5302
+ 4,
5303
+ this.oidcInteractionService.findOrCreateGrant(interaction.grantId, (_ref1 = session === null || session === void 0 ? void 0 : session.accountId) !== null && _ref1 !== void 0 ? _ref1 : '', clientId, expiresInSeconds)
5304
+ ];
5305
+ case 7:
4884
5306
  grant = _state.sent();
4885
- if (prompt.details.missingOIDCScope) {
4886
- grant.addOIDCScope(prompt.details.missingOIDCScope.join(' '));
4887
- }
4888
- if (prompt.details.missingOIDCClaims) {
4889
- grant.addOIDCClaims(prompt.details.missingOIDCClaims);
4890
- }
4891
- if (prompt.details.missingResourceScopes) {
5307
+ missingOIDCScope = (_prompt_details_missingOIDCScope = prompt.details.missingOIDCScope) !== null && _prompt_details_missingOIDCScope !== void 0 ? _prompt_details_missingOIDCScope : [];
5308
+ if (missingOIDCScope.length > 0) {
5309
+ _resolveEffectiveSubset = resolveEffectiveSubset(missingOIDCScope, body.grantedOIDCScopes, ALWAYS_GRANTED_OIDC_SCOPES), granted = _resolveEffectiveSubset.granted, rejected = _resolveEffectiveSubset.rejected;
5310
+ if (granted.length > 0) {
5311
+ grant.addOIDCScope(granted.join(' '));
5312
+ }
4892
5313
  _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
4893
5314
  try {
4894
- for(_iterator = Object.entries(prompt.details.missingResourceScopes)[Symbol.iterator](); !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
4895
- _step_value = _sliced_to_array(_step.value, 2), indicator = _step_value[0], scopes = _step_value[1];
4896
- grant.addResourceScope(indicator, scopes.join(' '));
5315
+ for(_iterator = rejected[Symbol.iterator](); !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
5316
+ value = _step.value;
5317
+ grant.rejectOIDCScope(value);
4897
5318
  }
4898
5319
  } catch (err) {
4899
5320
  _didIteratorError = true;
@@ -4910,11 +5331,67 @@ function _ts_generator$2(thisArg, body) {
4910
5331
  }
4911
5332
  }
4912
5333
  }
5334
+ missingOIDCClaims = (_prompt_details_missingOIDCClaims = prompt.details.missingOIDCClaims) !== null && _prompt_details_missingOIDCClaims !== void 0 ? _prompt_details_missingOIDCClaims : [];
5335
+ if (missingOIDCClaims.length > 0) {
5336
+ _resolveEffectiveSubset1 = resolveEffectiveSubset(missingOIDCClaims, body.grantedOIDCClaims), granted1 = _resolveEffectiveSubset1.granted, rejected1 = _resolveEffectiveSubset1.rejected;
5337
+ if (granted1.length > 0) {
5338
+ grant.addOIDCClaims(granted1);
5339
+ }
5340
+ if (rejected1.length > 0) {
5341
+ grant.rejectOIDCClaims(rejected1);
5342
+ }
5343
+ }
5344
+ missingResourceScopes = (_prompt_details_missingResourceScopes = prompt.details.missingResourceScopes) !== null && _prompt_details_missingResourceScopes !== void 0 ? _prompt_details_missingResourceScopes : {};
5345
+ _iteratorNormalCompletion1 = true, _didIteratorError1 = false, _iteratorError1 = undefined;
5346
+ try {
5347
+ for(_iterator1 = Object.entries(missingResourceScopes)[Symbol.iterator](); !(_iteratorNormalCompletion1 = (_step1 = _iterator1.next()).done); _iteratorNormalCompletion1 = true){
5348
+ _step_value = _sliced_to_array(_step1.value, 2), indicator = _step_value[0], scopes = _step_value[1];
5349
+ ;
5350
+ requestedSubset = (_body_grantedResourceScopes = body.grantedResourceScopes) === null || _body_grantedResourceScopes === void 0 ? void 0 : _body_grantedResourceScopes[indicator];
5351
+ _resolveEffectiveSubset2 = resolveEffectiveSubset(scopes, requestedSubset), granted2 = _resolveEffectiveSubset2.granted, rejected2 = _resolveEffectiveSubset2.rejected;
5352
+ if (granted2.length > 0) {
5353
+ grant.addResourceScope(indicator, granted2.join(' '));
5354
+ }
5355
+ _iteratorNormalCompletion2 = true, _didIteratorError2 = false, _iteratorError2 = undefined;
5356
+ try {
5357
+ for(_iterator2 = rejected2[Symbol.iterator](); !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true){
5358
+ value1 = _step2.value;
5359
+ grant.rejectResourceScope(indicator, value1);
5360
+ }
5361
+ } catch (err) {
5362
+ _didIteratorError2 = true;
5363
+ _iteratorError2 = err;
5364
+ } finally{
5365
+ try {
5366
+ if (!_iteratorNormalCompletion2 && _iterator2.return != null) {
5367
+ _iterator2.return();
5368
+ }
5369
+ } finally{
5370
+ if (_didIteratorError2) {
5371
+ throw _iteratorError2;
5372
+ }
5373
+ }
5374
+ }
5375
+ }
5376
+ } catch (err) {
5377
+ _didIteratorError1 = true;
5378
+ _iteratorError1 = err;
5379
+ } finally{
5380
+ try {
5381
+ if (!_iteratorNormalCompletion1 && _iterator1.return != null) {
5382
+ _iterator1.return();
5383
+ }
5384
+ } finally{
5385
+ if (_didIteratorError1) {
5386
+ throw _iteratorError1;
5387
+ }
5388
+ }
5389
+ }
4913
5390
  return [
4914
5391
  4,
4915
5392
  grant.save()
4916
5393
  ];
4917
- case 7:
5394
+ case 8:
4918
5395
  grantId = _state.sent();
4919
5396
  return [
4920
5397
  4,
@@ -4926,19 +5403,22 @@ function _ts_generator$2(thisArg, body) {
4926
5403
  mergeWithLastSubmission: true
4927
5404
  })
4928
5405
  ];
4929
- case 8:
5406
+ case 9:
4930
5407
  redirectTo1 = _state.sent();
4931
5408
  res.json({
4932
5409
  redirectTo: redirectTo1
4933
5410
  });
4934
5411
  return [
4935
5412
  3,
4936
- 10
5413
+ 11
4937
5414
  ];
4938
- case 9:
4939
- _state.sent();
4940
- throw new common.HttpException('Consent interaction failed', common.HttpStatus.BAD_REQUEST);
4941
5415
  case 10:
5416
+ err = _state.sent();
5417
+ if (_instanceof(err, common.HttpException)) {
5418
+ throw err;
5419
+ }
5420
+ throw new common.HttpException('Consent interaction failed', common.HttpStatus.BAD_REQUEST);
5421
+ case 11:
4942
5422
  return [
4943
5423
  2
4944
5424
  ];
@@ -5017,8 +5497,108 @@ exports.OidcInteractionController = __decorate([
5017
5497
  common.Controller('interaction'),
5018
5498
  __param(0, common.Inject(exports.OidcInteractionService)),
5019
5499
  __param(1, common.Inject(exports.OidcProviderConfigService)),
5020
- __param(2, common.Inject(OidcAccountService))
5500
+ __param(2, common.Inject(OidcAccountService)),
5501
+ __param(3, common.Inject(exports.OidcService))
5021
5502
  ], exports.OidcInteractionController);
5503
+ /**
5504
+ * Resolves the effective set to grant — and the complementary set to
5505
+ * reject — from a missing set, an optional caller-provided subset, and
5506
+ * an optional list of always-granted entries.
5507
+ *
5508
+ * Throws `400 BAD_REQUEST` if the requested subset contains entries that
5509
+ * are not in the missing set (no privilege escalation).
5510
+ *
5511
+ * - When `requestedSubset` is undefined → grants everything in `missing` (back-compat); rejects nothing.
5512
+ * - When `requestedSubset` is provided → grants exactly that subset; rejects every other missing entry so
5513
+ * oidc-provider does not re-prompt for them.
5514
+ * - Always-granted entries are union'd into `granted` (clamped to `missing`).
5515
+ *
5516
+ * @param missing - Entries the OIDC prompt indicated were missing for this consent.
5517
+ * @param requestedSubset - Optional subset the caller wants to grant; must be a subset of `missing`.
5518
+ * @param alwaysGranted - Entries that must be granted whenever they appear in `missing`, regardless of `requestedSubset`.
5519
+ * @returns `granted` to add to the grant and `rejected` to record on the grant.
5520
+ */ function resolveEffectiveSubset(missing, requestedSubset) {
5521
+ var alwaysGranted = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : [];
5522
+ var missingSet = new Set(missing);
5523
+ var baseSelection;
5524
+ if (requestedSubset === undefined) {
5525
+ baseSelection = missing;
5526
+ } else {
5527
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
5528
+ try {
5529
+ for(var _iterator = requestedSubset[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
5530
+ var value = _step.value;
5531
+ if (!missingSet.has(value)) {
5532
+ throw new common.HttpException('Granted value "'.concat(value, '" is not in the requested set.'), common.HttpStatus.BAD_REQUEST);
5533
+ }
5534
+ }
5535
+ } catch (err) {
5536
+ _didIteratorError = true;
5537
+ _iteratorError = err;
5538
+ } finally{
5539
+ try {
5540
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
5541
+ _iterator.return();
5542
+ }
5543
+ } finally{
5544
+ if (_didIteratorError) {
5545
+ throw _iteratorError;
5546
+ }
5547
+ }
5548
+ }
5549
+ baseSelection = requestedSubset;
5550
+ }
5551
+ var grantedSet = new Set(baseSelection);
5552
+ var _iteratorNormalCompletion1 = true, _didIteratorError1 = false, _iteratorError1 = undefined;
5553
+ try {
5554
+ for(var _iterator1 = alwaysGranted[Symbol.iterator](), _step1; !(_iteratorNormalCompletion1 = (_step1 = _iterator1.next()).done); _iteratorNormalCompletion1 = true){
5555
+ var value1 = _step1.value;
5556
+ if (missingSet.has(value1)) {
5557
+ grantedSet.add(value1);
5558
+ }
5559
+ }
5560
+ } catch (err) {
5561
+ _didIteratorError1 = true;
5562
+ _iteratorError1 = err;
5563
+ } finally{
5564
+ try {
5565
+ if (!_iteratorNormalCompletion1 && _iterator1.return != null) {
5566
+ _iterator1.return();
5567
+ }
5568
+ } finally{
5569
+ if (_didIteratorError1) {
5570
+ throw _iteratorError1;
5571
+ }
5572
+ }
5573
+ }
5574
+ var rejected = [];
5575
+ var _iteratorNormalCompletion2 = true, _didIteratorError2 = false, _iteratorError2 = undefined;
5576
+ try {
5577
+ for(var _iterator2 = missing[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true){
5578
+ var value2 = _step2.value;
5579
+ if (!grantedSet.has(value2)) {
5580
+ rejected.push(value2);
5581
+ }
5582
+ }
5583
+ } catch (err) {
5584
+ _didIteratorError2 = true;
5585
+ _iteratorError2 = err;
5586
+ } finally{
5587
+ try {
5588
+ if (!_iteratorNormalCompletion2 && _iterator2.return != null) {
5589
+ _iterator2.return();
5590
+ }
5591
+ } finally{
5592
+ if (_didIteratorError2) {
5593
+ throw _iteratorError2;
5594
+ }
5595
+ }
5596
+ }
5597
+ return {
5598
+ granted: Array.from(grantedSet),
5599
+ rejected: rejected
5600
+ };
5601
+ }
5022
5602
 
5023
5603
  function asyncGeneratorStep$1(gen, resolve, reject, _next, _throw, key, arg) {
5024
5604
  try {
@@ -5689,6 +6269,8 @@ function _unsupported_iterable_to_array(o, minLen) {
5689
6269
  appOAuthConsentUrlPart: DEFAULT_APP_OAUTH_CONSENT_PATH_PART,
5690
6270
  appOAuthInteractionPath: DEFAULT_APP_OAUTH_INTERACTION_PATH,
5691
6271
  tokenLifetimes: DEFAULT_OIDC_TOKEN_LIFETIMES,
6272
+ trustProxy: envService.isProduction,
6273
+ trustProxyInNonProduction: false,
5692
6274
  jwksServiceConfig: {
5693
6275
  encryptionSecret: encryptionSecret
5694
6276
  },
@@ -5745,6 +6327,7 @@ function _unsupported_iterable_to_array(o, minLen) {
5745
6327
  ],
5746
6328
  exports: [
5747
6329
  OidcClientService,
6330
+ exports.OidcService,
5748
6331
  OidcModuleConfig,
5749
6332
  OidcAuthMiddlewareConfig,
5750
6333
  OidcServerFirestoreCollections
@@ -5761,6 +6344,9 @@ function _unsupported_iterable_to_array(o, minLen) {
5761
6344
  var result = moduleConfig;
5762
6345
  if (config$1) {
5763
6346
  result = _object_spread({}, moduleConfig, config$1);
6347
+ if (config$1.trustProxyInNonProduction && !envService.isProduction) {
6348
+ result.trustProxy = true;
6349
+ }
5764
6350
  }
5765
6351
  return result;
5766
6352
  }
@@ -5850,6 +6436,8 @@ function _unsupported_iterable_to_array(o, minLen) {
5850
6436
  exports.DEFAULT_APP_OAUTH_CONSENT_PATH_PART = DEFAULT_APP_OAUTH_CONSENT_PATH_PART;
5851
6437
  exports.DEFAULT_APP_OAUTH_INTERACTION_PATH = DEFAULT_APP_OAUTH_INTERACTION_PATH;
5852
6438
  exports.DEFAULT_APP_OAUTH_LOGIN_PATH_PART = DEFAULT_APP_OAUTH_LOGIN_PATH_PART;
6439
+ exports.DEFAULT_MAX_REQUESTED_LOGIN_DURATION_SECONDS = DEFAULT_MAX_REQUESTED_LOGIN_DURATION_SECONDS;
6440
+ exports.DEFAULT_MIN_REQUESTED_LOGIN_DURATION_SECONDS = DEFAULT_MIN_REQUESTED_LOGIN_DURATION_SECONDS;
5853
6441
  exports.DEFAULT_OIDC_CODE_CHALLENGE_METHODS = DEFAULT_OIDC_CODE_CHALLENGE_METHODS;
5854
6442
  exports.DEFAULT_OIDC_ID_TOKEN_SIGNING_ALG_VALUES = DEFAULT_OIDC_ID_TOKEN_SIGNING_ALG_VALUES;
5855
6443
  exports.DEFAULT_OIDC_ISSUER_PATH = DEFAULT_OIDC_ISSUER_PATH;
@@ -5859,6 +6447,7 @@ exports.DEFAULT_OIDC_TOKEN_ENDPOINT_AUTH_METHODS = DEFAULT_OIDC_TOKEN_ENDPOINT_A
5859
6447
  exports.DEFAULT_OIDC_TOKEN_LIFETIMES = DEFAULT_OIDC_TOKEN_LIFETIMES;
5860
6448
  exports.DEFAULT_ROTATED_KEY_MAX_AGE = DEFAULT_ROTATED_KEY_MAX_AGE;
5861
6449
  exports.FIREBASE_SERVER_OIDC_ROUTES_FOR_GLOBAL_ROUTE_EXCLUDE = FIREBASE_SERVER_OIDC_ROUTES_FOR_GLOBAL_ROUTE_EXCLUDE;
6450
+ exports.GRANTABLE_MODEL_NAMES = GRANTABLE_MODEL_NAMES;
5862
6451
  exports.JwksFirestoreCollections = JwksFirestoreCollections;
5863
6452
  exports.JwksKeyDocument = JwksKeyDocument;
5864
6453
  exports.JwksServiceConfig = JwksServiceConfig;
@@ -5880,6 +6469,7 @@ exports.applyOidcAuthMiddleware = applyOidcAuthMiddleware;
5880
6469
  exports.createAdapterFactory = createAdapterFactory;
5881
6470
  exports.createOidcClientFactory = createOidcClientFactory;
5882
6471
  exports.deleteOidcClientFactory = deleteOidcClientFactory;
6472
+ exports.deleteOidcTokenFactory = deleteOidcTokenFactory;
5883
6473
  exports.getOidcScopesFromRequest = getOidcScopesFromRequest;
5884
6474
  exports.jwksKeyCollectionReference = jwksKeyCollectionReference;
5885
6475
  exports.jwksKeyConverter = jwksKeyConverter;