@oxy.so/core 1.3.1 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -109,5 +109,46 @@ function OxyServicesAgencyMixin(Base) {
109
109
  throw this.handleError(error);
110
110
  }
111
111
  }
112
+ /**
113
+ * Product backend → Oxy: trade the signed-in requester's access token for a
114
+ * one-use assertion naming `agentId` (ADR 0025). Authenticated with THIS
115
+ * instance's service credential (`configureServiceAuth`). `subjectToken` is
116
+ * sent to Oxy only, in the body; never forward it anywhere else.
117
+ *
118
+ * Not retried: a refusal is an answer, and the caller mints per turn.
119
+ */
120
+ async mintRequesterAssertion(input) {
121
+ try {
122
+ const serviceToken = await this.getServiceToken();
123
+ return await this.makeRequest('POST', '/internal/native-agents/requester-assertions', { agentId: input.agentId, subjectToken: input.subjectToken }, {
124
+ cache: false,
125
+ retry: false,
126
+ timeout: 5000,
127
+ headers: { Authorization: `Bearer ${serviceToken}` },
128
+ });
129
+ }
130
+ catch (error) {
131
+ throw this.handleError(error);
132
+ }
133
+ }
134
+ /**
135
+ * Audience → Oxy: verify, live-revalidate and CONSUME an assertion. A second
136
+ * call with the same assertion answers `active: false`. Authenticated with
137
+ * the audience's own service credential.
138
+ */
139
+ async introspectRequesterAssertion(input) {
140
+ try {
141
+ const serviceToken = await this.getServiceToken();
142
+ return await this.makeRequest('POST', '/internal/native-agents/requester-assertions/introspect', { assertion: input.assertion, presenter: input.presenter }, {
143
+ cache: false,
144
+ retry: false,
145
+ timeout: 5000,
146
+ headers: { Authorization: `Bearer ${serviceToken}` },
147
+ });
148
+ }
149
+ catch (error) {
150
+ throw this.handleError(error);
151
+ }
152
+ }
112
153
  };
113
154
  }
@@ -212,10 +212,51 @@ function OxyServicesUtilityMixin(Base) {
212
212
  * @returns Express middleware function
213
213
  */
214
214
  auth(options = {}) {
215
- const { debug = false, onError, loadUser = false, optional = false, jwtSecret, serviceTokenJwksUrl = new URL('/.well-known/jwks.json', this.getBaseURL()).toString(), expectedIssuer = OXY_JWT_ISSUER, expectedAudience = OXY_JWT_AUDIENCE, } = options;
215
+ const { debug = false, onError, onRefusal, loadUser = false, optional = false, jwtSecret, serviceTokenJwksUrl = new URL('/.well-known/jwks.json', this.getBaseURL()).toString(), expectedIssuer = OXY_JWT_ISSUER, expectedAudience = OXY_JWT_AUDIENCE, } = options;
216
216
  // Cross-mixin method access: typed as a structural subset of the
217
217
  // composed OxyServices we know we have at runtime.
218
218
  const oxyInstance = this;
219
+ /**
220
+ * Make a refusal observable to the HOST, without telling the client
221
+ * anything it is not already told.
222
+ *
223
+ * Called at the top of every branch that rejects a PRESENTED credential,
224
+ * including the ones `optional` then swallows — those are the expensive
225
+ * ones, because they leave no trace anywhere else. The response bodies
226
+ * below are untouched by design: what a client sees does not change.
227
+ *
228
+ * `warn`, not `debug`: a debug-gated reason is only there for whoever
229
+ * already suspects this middleware, and the failure mode this exists for
230
+ * is the one where nobody does. The volume is bounded by refusals, and a
231
+ * host drowning in them has a fault worth the lines.
232
+ */
233
+ const recordRefusal = (req, refusal) => {
234
+ const recorded = { ...refusal, optional };
235
+ req.oxyAuthRefusal = recorded;
236
+ logger_1.logger.warn(`[oxy.auth] refused ${recorded.code}: ${recorded.reason}`, {
237
+ component: 'auth',
238
+ method: 'auth',
239
+ code: recorded.code,
240
+ stage: recorded.stage,
241
+ reason: recorded.reason,
242
+ status: recorded.status,
243
+ // Says whether the client will see this status or the host's own
244
+ // generic answer — the difference that made the JWKS outage invisible.
245
+ optional: recorded.optional,
246
+ path: req.path,
247
+ });
248
+ if (onRefusal) {
249
+ try {
250
+ onRefusal(recorded);
251
+ }
252
+ catch (observerError) {
253
+ logger_1.logger.warn('[oxy.auth] onRefusal observer threw', {
254
+ component: 'auth',
255
+ method: 'auth',
256
+ }, observerError);
257
+ }
258
+ }
259
+ };
219
260
  // Return an async middleware function
220
261
  return async (req, res, next) => {
221
262
  try {
@@ -253,6 +294,12 @@ function OxyServicesUtilityMixin(Base) {
253
294
  decoded = (0, jwt_decode_1.jwtDecode)(token);
254
295
  }
255
296
  catch (decodeError) {
297
+ recordRefusal(req, {
298
+ code: 'INVALID_TOKEN_FORMAT',
299
+ stage: 'token',
300
+ reason: 'Bearer credential is not a decodable JWT',
301
+ status: 401,
302
+ });
256
303
  if (debug) {
257
304
  logger_1.logger.debug('[oxy.auth] Token decode failed', {
258
305
  component: 'auth',
@@ -299,6 +346,12 @@ function OxyServicesUtilityMixin(Base) {
299
346
  // else (e.g. Node crypto failing to load on a misconfigured host)
300
347
  // genuinely IS a 500.
301
348
  if (verifyError instanceof ServiceTokenConfigurationError) {
349
+ recordRefusal(req, {
350
+ code: 'SERVICE_TOKEN_NOT_CONFIGURED',
351
+ stage: 'service-token',
352
+ reason: verifyError.message,
353
+ status: 403,
354
+ });
302
355
  if (optional) {
303
356
  req.userId = null;
304
357
  req.user = null;
@@ -317,6 +370,20 @@ function OxyServicesUtilityMixin(Base) {
317
370
  if (verifyError instanceof ServiceTokenStructureError ||
318
371
  verifyError instanceof ServiceTokenSignatureError ||
319
372
  verifyError instanceof ServiceTokenClaimError) {
373
+ const code = verifyError instanceof ServiceTokenClaimError
374
+ ? 'INVALID_SERVICE_TOKEN_CLAIMS'
375
+ : 'INVALID_SERVICE_TOKEN';
376
+ // The SDK's own message is the whole diagnostic value here —
377
+ // "signing key is unknown" (rotated/unpublished kid), "key set
378
+ // is unavailable" (JWKS empty, unreachable or malformed) and
379
+ // "audience mismatch" are three different outages that answer
380
+ // the client identically.
381
+ recordRefusal(req, {
382
+ code,
383
+ stage: 'service-token',
384
+ reason: `${verifyError.name}: ${verifyError.message}`,
385
+ status: 401,
386
+ });
320
387
  if (debug) {
321
388
  logger_1.logger.debug('[oxy.auth] Service token rejected', {
322
389
  component: 'auth',
@@ -330,11 +397,6 @@ function OxyServicesUtilityMixin(Base) {
330
397
  req.user = null;
331
398
  return next();
332
399
  }
333
- const code = verifyError instanceof ServiceTokenSignatureError
334
- ? 'INVALID_SERVICE_TOKEN'
335
- : verifyError instanceof ServiceTokenStructureError
336
- ? 'INVALID_SERVICE_TOKEN'
337
- : 'INVALID_SERVICE_TOKEN_CLAIMS';
338
400
  const error = {
339
401
  error: code,
340
402
  message: verifyError.message,
@@ -362,6 +424,14 @@ function OxyServicesUtilityMixin(Base) {
362
424
  // Check expiration — reject tokens at exact expiry second (use <=)
363
425
  const now = Math.floor(Date.now() / 1000);
364
426
  if (!Number.isInteger(decoded.exp) || decoded.exp <= now) {
427
+ recordRefusal(req, {
428
+ code: 'TOKEN_EXPIRED',
429
+ stage: 'service-token',
430
+ reason: Number.isInteger(decoded.exp)
431
+ ? 'Service token expired'
432
+ : 'Service token has no integer exp claim',
433
+ status: 401,
434
+ });
365
435
  if (optional) {
366
436
  req.userId = null;
367
437
  req.user = null;
@@ -373,6 +443,12 @@ function OxyServicesUtilityMixin(Base) {
373
443
  return res.status(401).json(error);
374
444
  }
375
445
  if (decoded.nbf !== undefined && (!Number.isInteger(decoded.nbf) || decoded.nbf > now)) {
446
+ recordRefusal(req, {
447
+ code: 'INVALID_SERVICE_TOKEN_CLAIMS',
448
+ stage: 'service-token',
449
+ reason: 'Service token is not yet valid (nbf)',
450
+ status: 401,
451
+ });
376
452
  const error = { error: 'INVALID_SERVICE_TOKEN_CLAIMS', message: 'Service token is not yet valid', code: 'INVALID_SERVICE_TOKEN_CLAIMS', status: 401 };
377
453
  if (onError)
378
454
  return onError(error);
@@ -394,6 +470,22 @@ function OxyServicesUtilityMixin(Base) {
394
470
  || !Array.isArray(decoded.scopes)
395
471
  || !decoded.scopes.every((scope) => typeof scope === 'string' && scope.length > 0 && scope === scope.trim())
396
472
  || new Set(decoded.scopes).size !== decoded.scopes.length) {
473
+ recordRefusal(req, {
474
+ code: 'INVALID_SERVICE_TOKEN',
475
+ stage: 'service-token',
476
+ // Names the field rather than its value: an exact-match claim
477
+ // failing on invisible whitespace reads as "present" in a log
478
+ // that only says which claims were missing.
479
+ reason: `Service token claims unusable (${[
480
+ !isExactNonEmptyServiceClaim(appId) ? 'appId' : null,
481
+ !isExactNonEmptyServiceClaim(decoded.appName) ? 'appName' : null,
482
+ !isExactNonEmptyServiceClaim(credentialId) ? 'credentialId' : null,
483
+ !isExactNonEmptyServiceClaim(ownerAccountId) ? 'ownerAccountId' : null,
484
+ !isOxyServiceEnvironment(environment) ? 'environment' : null,
485
+ !Array.isArray(decoded.scopes) ? 'scopes' : null,
486
+ ].filter((field) => field !== null).join(', ') || 'scopes'})`,
487
+ status: 401,
488
+ });
397
489
  if (optional) {
398
490
  req.userId = null;
399
491
  req.user = null;
@@ -408,6 +500,12 @@ function OxyServicesUtilityMixin(Base) {
408
500
  const oxyUserIdRaw = req.headers['x-oxy-user-id'];
409
501
  const oxyUserId = isExactNonEmptyServiceClaim(oxyUserIdRaw) ? oxyUserIdRaw : null;
410
502
  if (oxyUserIdRaw !== undefined && oxyUserId === null) {
503
+ recordRefusal(req, {
504
+ code: 'INVALID_SERVICE_TOKEN_CLAIMS',
505
+ stage: 'service-token',
506
+ reason: 'X-Oxy-User-Id is not an exact non-empty id',
507
+ status: 401,
508
+ });
411
509
  const error = {
412
510
  error: 'INVALID_SERVICE_TOKEN_CLAIMS',
413
511
  message: 'Delegated user id must be an exact non-empty id',
@@ -432,6 +530,16 @@ function OxyServicesUtilityMixin(Base) {
432
530
  appId,
433
531
  attemptedUserId: oxyUserId,
434
532
  });
533
+ // A verifier with no service credentials of its own cannot
534
+ // reach the verify endpoint at all, so `null` here can mean
535
+ // "no grant" OR "this host cannot ask". Both refuse, and both
536
+ // are worth naming on the request.
537
+ recordRefusal(req, {
538
+ code: 'SERVICE_ACTING_AS_UNAUTHORIZED',
539
+ stage: 'service-token',
540
+ reason: `No delegation grant for app ${appId} acting as ${oxyUserId} (or this verifier could not reach the grant check)`,
541
+ status: 403,
542
+ });
435
543
  const error = {
436
544
  error: 'SERVICE_ACTING_AS_UNAUTHORIZED',
437
545
  message: 'Service not authorized to act as this user',
@@ -477,6 +585,12 @@ function OxyServicesUtilityMixin(Base) {
477
585
  // compared against the id the validated session resolves to.
478
586
  const claimedUserId = readStringClaim(decoded.userId) ?? readStringClaim(decoded.id);
479
587
  if (!claimedUserId) {
588
+ recordRefusal(req, {
589
+ code: 'INVALID_TOKEN_PAYLOAD',
590
+ stage: 'token',
591
+ reason: 'Token carries no usable user id claim',
592
+ status: 401,
593
+ });
480
594
  if (optional) {
481
595
  req.userId = null;
482
596
  req.user = null;
@@ -495,6 +609,12 @@ function OxyServicesUtilityMixin(Base) {
495
609
  // Check token expiration locally first (fast path)
496
610
  // Reject tokens at exact expiry second (use <=)
497
611
  if (decoded.exp && decoded.exp <= Math.floor(Date.now() / 1000)) {
612
+ recordRefusal(req, {
613
+ code: 'TOKEN_EXPIRED',
614
+ stage: 'token',
615
+ reason: 'User access token expired',
616
+ status: 401,
617
+ });
498
618
  if (optional) {
499
619
  req.userId = null;
500
620
  req.user = null;
@@ -517,6 +637,12 @@ function OxyServicesUtilityMixin(Base) {
517
637
  // required this.
518
638
  const sessionId = readStringClaim(decoded.sessionId);
519
639
  if (!sessionId) {
640
+ recordRefusal(req, {
641
+ code: 'SESSION_REQUIRED',
642
+ stage: 'token',
643
+ reason: 'User access token is not bound to a session',
644
+ status: 401,
645
+ });
520
646
  if (optional) {
521
647
  req.userId = null;
522
648
  req.user = null;
@@ -539,6 +665,12 @@ function OxyServicesUtilityMixin(Base) {
539
665
  useHeaderValidation: true,
540
666
  });
541
667
  if (!validationResult || !validationResult.valid || !validationResult.user) {
668
+ recordRefusal(req, {
669
+ code: 'INVALID_SESSION',
670
+ stage: 'session',
671
+ reason: 'Session is invalid, revoked or expired',
672
+ status: 401,
673
+ });
542
674
  if (optional) {
543
675
  req.userId = null;
544
676
  req.user = null;
@@ -557,6 +689,12 @@ function OxyServicesUtilityMixin(Base) {
557
689
  // The session — not the token — is the source of truth for identity.
558
690
  const validatedUserId = getUserIdentityId(validationResult.user);
559
691
  if (!validatedUserId) {
692
+ recordRefusal(req, {
693
+ code: 'INVALID_SESSION',
694
+ stage: 'session',
695
+ reason: 'Session did not resolve to a usable identity',
696
+ status: 401,
697
+ });
560
698
  if (optional) {
561
699
  req.userId = null;
562
700
  req.user = null;
@@ -582,6 +720,12 @@ function OxyServicesUtilityMixin(Base) {
582
720
  claimedUserId,
583
721
  validatedUserId,
584
722
  });
723
+ recordRefusal(req, {
724
+ code: 'SESSION_USER_MISMATCH',
725
+ stage: 'session',
726
+ reason: 'Token user claim does not own the presented session',
727
+ status: 401,
728
+ });
585
729
  if (optional) {
586
730
  req.userId = null;
587
731
  req.user = null;
@@ -612,6 +756,15 @@ function OxyServicesUtilityMixin(Base) {
612
756
  return next();
613
757
  }
614
758
  catch (validationError) {
759
+ recordRefusal(req, {
760
+ code: 'SESSION_VALIDATION_ERROR',
761
+ stage: 'session',
762
+ // Name and transport code only. The message of a failed
763
+ // `validateSession` can carry the request URL, and that URL
764
+ // contains the session id — which is a credential.
765
+ reason: `Session validation call failed (${describeErrorSafely(validationError)})`,
766
+ status: 401,
767
+ });
615
768
  if (debug) {
616
769
  logger_1.logger.debug('[oxy.auth] Session validation failed', {
617
770
  component: 'auth',
@@ -1037,6 +1190,20 @@ async function verifyServiceTokenSignature(token, options) {
1037
1190
  * those values out of URL construction and identity comparison, so an
1038
1191
  * unexpected shape becomes a 401 rather than a stringified surprise.
1039
1192
  */
1193
+ /**
1194
+ * A short, log-safe description of a thrown value: its class name and, when the
1195
+ * transport attached one, its `code` (`ECONNREFUSED`, `ETIMEDOUT`, `ENOTFOUND`
1196
+ * …). Never the message — a failed request's message can quote the URL it was
1197
+ * made against, and a session-validation URL contains the session id.
1198
+ */
1199
+ function describeErrorSafely(error) {
1200
+ if (!(error instanceof Error))
1201
+ return typeof error;
1202
+ const code = error.code;
1203
+ return typeof code === 'string' && /^[A-Za-z0-9_]{1,40}$/.test(code)
1204
+ ? `${error.name}: ${code}`
1205
+ : error.name;
1206
+ }
1040
1207
  function readStringClaim(value) {
1041
1208
  return typeof value === 'string' && value.length > 0 ? value : null;
1042
1209
  }
@@ -8,9 +8,11 @@ exports.getRequiredOxyBillingPrincipal = getRequiredOxyBillingPrincipal;
8
8
  exports.getOxyDelegatedUserId = getOxyDelegatedUserId;
9
9
  exports.getOxyRequestAttribution = getOxyRequestAttribution;
10
10
  exports.getRequiredOxyUserId = getRequiredOxyUserId;
11
+ exports.getOxyAuthRefusal = getOxyAuthRefusal;
11
12
  exports.requireOxyAuth = requireOxyAuth;
12
13
  exports.createOptionalOxyAuth = createOptionalOxyAuth;
13
14
  exports.createOxyAuthMiddleware = createOxyAuthMiddleware;
15
+ const logger_1 = require("../logger");
14
16
  const oxyServiceEnvironment_1 = require("../utils/oxyServiceEnvironment");
15
17
  Object.defineProperty(exports, "OXY_SERVICE_ENVIRONMENTS", { enumerable: true, get: function () { return oxyServiceEnvironment_1.OXY_SERVICE_ENVIRONMENTS; } });
16
18
  function normalizeId(value) {
@@ -119,9 +121,36 @@ function getRequiredOxyUserId(req) {
119
121
  }
120
122
  return userId;
121
123
  }
124
+ /**
125
+ * Why the credential on this request was refused, or `null` when none was
126
+ * presented (or it was accepted).
127
+ *
128
+ * Set by `oxy.auth()` — including the `optional: true` mount the composed
129
+ * helpers use, where a refusal otherwise leaves no trace: the request simply
130
+ * arrives unauthenticated and the host answers its own generic 401. Hosts log
131
+ * this beside their own 401; it is diagnostic and **must not be put into a
132
+ * response body**, which is exactly what it is kept out of.
133
+ */
134
+ function getOxyAuthRefusal(req) {
135
+ return req.oxyAuthRefusal ?? null;
136
+ }
122
137
  function requireOxyAuth(req, res, next) {
123
138
  const userId = getOxyUserId(req);
124
139
  if (!userId) {
140
+ // The body stays exactly as generic as it was. The LOG does not: this 401
141
+ // is the one a caller sees when the optional resolver already refused a
142
+ // real credential, and printing that code here is what turns "Homiio got
143
+ // 401, Alia logged nothing" into one greppable line.
144
+ const refusal = getOxyAuthRefusal(req);
145
+ if (refusal) {
146
+ logger_1.logger.warn(`[oxy.auth] 401 after refusal ${refusal.code}: ${refusal.reason}`, {
147
+ component: 'auth',
148
+ method: 'requireOxyAuth',
149
+ code: refusal.code,
150
+ stage: refusal.stage,
151
+ reason: refusal.reason,
152
+ });
153
+ }
125
154
  res.status(401).json({
126
155
  error: 'Unauthorized',
127
156
  message: 'Authentication required',
@@ -16,10 +16,12 @@
16
16
  * ```
17
17
  */
18
18
  Object.defineProperty(exports, "__esModule", { value: true });
19
- exports.withoutNodeHttpObservation = exports.observeNodeHttp = exports.createEcosystemTraffic = exports.isOfficialWebOrigin = exports.registrableApex = exports.OXY_IDENTITY_CACHE_PREFIXES = exports.oxyUserByIdCacheKey = exports.evictOxyIdentityCache = exports.publishOxyUserInvalidation = exports.createOxyUserInvalidationHandler = exports.verifySecret = exports.verifyCapabilityTicket = exports.readCapabilityAuthorization = exports.issueCapabilityTicket = exports.inputSatisfiesCapabilityLimits = exports.createCapabilityTicketMiddleware = exports.CapabilityTicketError = exports.OXY_CSP_BASELINE = exports.inlineScriptCspHash = exports.formatOxyCspPolicy = exports.extractInlineScripts = exports.cspSourcesFor = exports.createOxySecurityHeaders = exports.buildOxyPagesHeaders = exports.buildOxyCspDirectives = exports.createOxyCors = exports.UPSTREAM_HEADERS_TIMEOUT_MS = exports.MAX_URL_LENGTH = exports.MAX_REDIRECTS = exports.DEFAULT_USER_AGENT = exports.BLOCKED_HOSTNAMES = exports.ALLOWED_PROTOCOLS = exports.ALLOWED_PORTS = exports.UpstreamError = exports.SsrfRejection = exports.safeFetch = exports.isBlockedIp = exports.assertSafePublicUrl = exports.createOxyRateLimit = exports.OXY_SERVICE_ENVIRONMENTS = exports.requireOxyAuth = exports.isOxyAuthenticated = exports.getRequiredOxyUserId = exports.getRequiredOxyBillingPrincipal = exports.getOxyUserId = exports.getOxyRequestAttribution = exports.getOxyDelegatedUserId = exports.getOxyBillingPrincipal = exports.createOxyAuthMiddleware = exports.createOptionalOxyAuth = void 0;
19
+ exports.signOxyRequesterAssertion = exports.readOxyRequesterAssertionKeyId = exports.oxyRequesterAssertionClaimsSchema = exports.OxyRequesterAssertionError = exports.OXY_REQUESTER_ASSERTION_TYPE = exports.OXY_REQUESTER_ASSERTION_MAX_TTL_SECONDS = exports.OXY_REQUESTER_ASSERTION_HEADER = exports.OXY_REQUESTER_ASSERTION_DEFAULT_ISSUER = exports.createOxyRequesterAssertionAuth = exports.createOxyJwksKeyResolver = exports.verifyCapabilityTicket = exports.readCapabilityAuthorization = exports.issueCapabilityTicket = exports.inputSatisfiesCapabilityLimits = exports.createCapabilityTicketMiddleware = exports.CapabilityTicketError = exports.OXY_CSP_BASELINE = exports.inlineScriptCspHash = exports.formatOxyCspPolicy = exports.extractInlineScripts = exports.cspSourcesFor = exports.createOxySecurityHeaders = exports.buildOxyPagesHeaders = exports.buildOxyCspDirectives = exports.createOxyCors = exports.UPSTREAM_HEADERS_TIMEOUT_MS = exports.MAX_URL_LENGTH = exports.MAX_REDIRECTS = exports.DEFAULT_USER_AGENT = exports.BLOCKED_HOSTNAMES = exports.ALLOWED_PROTOCOLS = exports.ALLOWED_PORTS = exports.UpstreamError = exports.SsrfRejection = exports.safeFetch = exports.isBlockedIp = exports.assertSafePublicUrl = exports.createOxyRateLimit = exports.OXY_SERVICE_ENVIRONMENTS = exports.requireOxyAuth = exports.isOxyAuthenticated = exports.getRequiredOxyUserId = exports.getRequiredOxyBillingPrincipal = exports.getOxyUserId = exports.getOxyRequestAttribution = exports.getOxyDelegatedUserId = exports.getOxyBillingPrincipal = exports.getOxyAuthRefusal = exports.createOxyAuthMiddleware = exports.createOptionalOxyAuth = void 0;
20
+ exports.withoutNodeHttpObservation = exports.observeNodeHttp = exports.createEcosystemTraffic = exports.isOfficialWebOrigin = exports.registrableApex = exports.OXY_IDENTITY_CACHE_PREFIXES = exports.oxyUserByIdCacheKey = exports.evictOxyIdentityCache = exports.publishOxyUserInvalidation = exports.createOxyUserInvalidationHandler = exports.verifySecret = exports.verifyOxyRequesterAssertion = void 0;
20
21
  var auth_1 = require("./auth");
21
22
  Object.defineProperty(exports, "createOptionalOxyAuth", { enumerable: true, get: function () { return auth_1.createOptionalOxyAuth; } });
22
23
  Object.defineProperty(exports, "createOxyAuthMiddleware", { enumerable: true, get: function () { return auth_1.createOxyAuthMiddleware; } });
24
+ Object.defineProperty(exports, "getOxyAuthRefusal", { enumerable: true, get: function () { return auth_1.getOxyAuthRefusal; } });
23
25
  Object.defineProperty(exports, "getOxyBillingPrincipal", { enumerable: true, get: function () { return auth_1.getOxyBillingPrincipal; } });
24
26
  Object.defineProperty(exports, "getOxyDelegatedUserId", { enumerable: true, get: function () { return auth_1.getOxyDelegatedUserId; } });
25
27
  Object.defineProperty(exports, "getOxyRequestAttribution", { enumerable: true, get: function () { return auth_1.getOxyRequestAttribution; } });
@@ -72,6 +74,20 @@ Object.defineProperty(exports, "inputSatisfiesCapabilityLimits", { enumerable: t
72
74
  Object.defineProperty(exports, "issueCapabilityTicket", { enumerable: true, get: function () { return capabilityTicket_1.issueCapabilityTicket; } });
73
75
  Object.defineProperty(exports, "readCapabilityAuthorization", { enumerable: true, get: function () { return capabilityTicket_1.readCapabilityAuthorization; } });
74
76
  Object.defineProperty(exports, "verifyCapabilityTicket", { enumerable: true, get: function () { return capabilityTicket_1.verifyCapabilityTicket; } });
77
+ // Present-requester assertions (ADR 0025): a product backend trades a signed-in
78
+ // person's live session with Oxy for a one-use assertion its audience consumes.
79
+ var requesterAssertion_1 = require("./requesterAssertion");
80
+ Object.defineProperty(exports, "createOxyJwksKeyResolver", { enumerable: true, get: function () { return requesterAssertion_1.createOxyJwksKeyResolver; } });
81
+ Object.defineProperty(exports, "createOxyRequesterAssertionAuth", { enumerable: true, get: function () { return requesterAssertion_1.createOxyRequesterAssertionAuth; } });
82
+ Object.defineProperty(exports, "OXY_REQUESTER_ASSERTION_DEFAULT_ISSUER", { enumerable: true, get: function () { return requesterAssertion_1.OXY_REQUESTER_ASSERTION_DEFAULT_ISSUER; } });
83
+ Object.defineProperty(exports, "OXY_REQUESTER_ASSERTION_HEADER", { enumerable: true, get: function () { return requesterAssertion_1.OXY_REQUESTER_ASSERTION_HEADER; } });
84
+ Object.defineProperty(exports, "OXY_REQUESTER_ASSERTION_MAX_TTL_SECONDS", { enumerable: true, get: function () { return requesterAssertion_1.OXY_REQUESTER_ASSERTION_MAX_TTL_SECONDS; } });
85
+ Object.defineProperty(exports, "OXY_REQUESTER_ASSERTION_TYPE", { enumerable: true, get: function () { return requesterAssertion_1.OXY_REQUESTER_ASSERTION_TYPE; } });
86
+ Object.defineProperty(exports, "OxyRequesterAssertionError", { enumerable: true, get: function () { return requesterAssertion_1.OxyRequesterAssertionError; } });
87
+ Object.defineProperty(exports, "oxyRequesterAssertionClaimsSchema", { enumerable: true, get: function () { return requesterAssertion_1.oxyRequesterAssertionClaimsSchema; } });
88
+ Object.defineProperty(exports, "readOxyRequesterAssertionKeyId", { enumerable: true, get: function () { return requesterAssertion_1.readOxyRequesterAssertionKeyId; } });
89
+ Object.defineProperty(exports, "signOxyRequesterAssertion", { enumerable: true, get: function () { return requesterAssertion_1.signOxyRequesterAssertion; } });
90
+ Object.defineProperty(exports, "verifyOxyRequesterAssertion", { enumerable: true, get: function () { return requesterAssertion_1.verifyOxyRequesterAssertion; } });
75
91
  // Constant-time secret comparison.
76
92
  var verifySecret_1 = require("./verifySecret");
77
93
  Object.defineProperty(exports, "verifySecret", { enumerable: true, get: function () { return verifySecret_1.verifySecret; } });