mppx 0.15.0 → 0.16.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.
Files changed (90) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/dist/cli/cli.js +137 -131
  3. package/dist/cli/plugins/index.js +11 -11
  4. package/dist/cli/validate/payment.d.ts +12 -0
  5. package/dist/cli/validate/payment.js +113 -14
  6. package/dist/client/Mppx.d.ts +13 -3
  7. package/dist/client/Mppx.js +47 -22
  8. package/dist/client/internal/Fetch.d.ts +11 -0
  9. package/dist/client/internal/Fetch.js +46 -8
  10. package/dist/client/internal/ProofRealm.d.ts +21 -0
  11. package/dist/client/internal/ProofRealm.js +55 -0
  12. package/dist/{attestation → extensions/identity}/Constants.d.ts +2 -2
  13. package/dist/{attestation → extensions/identity}/Constants.js +2 -2
  14. package/dist/extensions/identity/Types.d.ts +21 -0
  15. package/dist/extensions/identity/index.d.ts +7 -0
  16. package/dist/extensions/identity/index.js +6 -0
  17. package/dist/{attestation → extensions/identity}/internal/HttpMessageSignature.d.ts +3 -3
  18. package/dist/{attestation → extensions/identity}/internal/HttpMessageSignature.js +10 -10
  19. package/dist/extensions/identity/internal/SigningContext.d.ts +6 -0
  20. package/dist/{attestation → extensions/identity}/internal/SigningContext.js +3 -3
  21. package/dist/extensions/identity/internal/Verification.d.ts +9 -0
  22. package/dist/extensions/identity/internal/Verification.js +10 -0
  23. package/dist/{tap → extensions/identity/tap}/Constants.js +1 -1
  24. package/dist/extensions/identity/tap/client/index.d.ts +32 -0
  25. package/dist/{tap → extensions/identity/tap}/client/index.js +13 -9
  26. package/dist/extensions/identity/tap/server/index.d.ts +27 -0
  27. package/dist/{tap → extensions/identity/tap}/server/index.js +12 -4
  28. package/dist/{web-bot-auth → extensions/identity/web-bot-auth}/Constants.js +1 -1
  29. package/dist/{web-bot-auth → extensions/identity/web-bot-auth}/client/index.d.ts +6 -5
  30. package/dist/{web-bot-auth → extensions/identity/web-bot-auth}/client/index.js +5 -5
  31. package/dist/{web-bot-auth → extensions/identity/web-bot-auth}/server/index.d.ts +6 -5
  32. package/dist/{web-bot-auth → extensions/identity/web-bot-auth}/server/index.js +6 -4
  33. package/dist/identity/Client.d.ts +34 -0
  34. package/dist/identity/Client.js +133 -0
  35. package/dist/identity/Server.d.ts +46 -0
  36. package/dist/identity/Server.js +145 -0
  37. package/dist/identity/Types.d.ts +90 -0
  38. package/dist/identity/Types.js +2 -0
  39. package/dist/{attestation → identity}/index.d.ts +2 -2
  40. package/dist/{attestation → identity}/index.js +1 -2
  41. package/dist/identity/internal/SigningContext.d.ts +6 -0
  42. package/dist/identity/internal/SigningContext.js +11 -0
  43. package/dist/integrations/x402/Evm.d.ts +1 -0
  44. package/dist/internal/version.d.ts +2 -2
  45. package/dist/internal/version.js +1 -1
  46. package/dist/mcp/client/McpClient.d.ts +5 -0
  47. package/dist/mcp/client/McpClient.js +2 -0
  48. package/dist/proxy/Proxy.js +10 -5
  49. package/dist/proxy/Service.d.ts +42 -4
  50. package/dist/proxy/Service.js +21 -5
  51. package/dist/server/Mppx.d.ts +27 -8
  52. package/dist/server/Mppx.js +136 -51
  53. package/dist/stripe/server/internal/html.gen.js +1 -1
  54. package/dist/tempo/internal/charge.js +2 -1
  55. package/dist/tempo/internal/fee-payer.d.ts +0 -2
  56. package/dist/tempo/internal/fee-payer.js +0 -4
  57. package/dist/tempo/server/Subscription.js +0 -1
  58. package/dist/tempo/server/internal/html.gen.js +1 -1
  59. package/dist/tempo/session/client/Runtime.d.ts +2 -0
  60. package/dist/tempo/session/client/Session.js +1 -1
  61. package/dist/tempo/session/client/SessionManager.d.ts +6 -0
  62. package/dist/tempo/session/client/SessionManager.js +14 -7
  63. package/dist/tempo/session/client/Transports.d.ts +12 -3
  64. package/dist/tempo/session/client/Transports.js +37 -11
  65. package/dist/tempo/session/precompile/Chain.js +0 -3
  66. package/dist/tempo/subscription/Store.js +10 -9
  67. package/package.json +15 -11
  68. package/dist/attestation/Client.d.ts +0 -17
  69. package/dist/attestation/Client.js +0 -28
  70. package/dist/attestation/Server.d.ts +0 -4
  71. package/dist/attestation/Server.js +0 -8
  72. package/dist/attestation/Types.d.ts +0 -76
  73. package/dist/attestation/internal/SigningContext.d.ts +0 -4
  74. package/dist/tap/client/index.d.ts +0 -26
  75. package/dist/tap/server/index.d.ts +0 -20
  76. /package/dist/{attestation → extensions/identity}/Types.js +0 -0
  77. /package/dist/{tap → extensions/identity/tap}/Constants.d.ts +0 -0
  78. /package/dist/{tap → extensions/identity/tap}/Types.d.ts +0 -0
  79. /package/dist/{tap → extensions/identity/tap}/Types.js +0 -0
  80. /package/dist/{tap → extensions/identity/tap}/index.d.ts +0 -0
  81. /package/dist/{tap → extensions/identity/tap}/index.js +0 -0
  82. /package/dist/{web-bot-auth → extensions/identity/web-bot-auth}/Constants.d.ts +0 -0
  83. /package/dist/{web-bot-auth → extensions/identity/web-bot-auth}/Types.d.ts +0 -0
  84. /package/dist/{web-bot-auth → extensions/identity/web-bot-auth}/Types.js +0 -0
  85. /package/dist/{web-bot-auth → extensions/identity/web-bot-auth}/index.d.ts +0 -0
  86. /package/dist/{web-bot-auth → extensions/identity/web-bot-auth}/index.js +0 -0
  87. /package/dist/{web-bot-auth → extensions/identity/web-bot-auth}/internal/JwkThumbprint.d.ts +0 -0
  88. /package/dist/{web-bot-auth → extensions/identity/web-bot-auth}/internal/JwkThumbprint.js +0 -0
  89. /package/dist/{web-bot-auth → extensions/identity/web-bot-auth}/internal/SignatureAgent.d.ts +0 -0
  90. /package/dist/{web-bot-auth → extensions/identity/web-bot-auth}/internal/SignatureAgent.js +0 -0
@@ -377,6 +377,8 @@ export type SessionManagerRuntime = RuntimeState & {
377
377
  lastChallenge: TempoSessionChallenge | null;
378
378
  /** Last HTTP resource URL usable for management POSTs. */
379
379
  lastUrl: RequestInfo | URL | null;
380
+ /** Headers of the last HTTP request, whose application Authorization management POSTs keep. */
381
+ lastHeaders?: HeadersInit | undefined;
380
382
  /** Active WebSocket payment session bookkeeping, when a socket is open. */
381
383
  socketSession: ActiveSocketSession | null;
382
384
  };
@@ -323,7 +323,7 @@ export function session(parameters = {}) {
323
323
  },
324
324
  };
325
325
  return wrapSseResponse({
326
- onNeedVoucher: (event, streamSignal) => handleSseNeedVoucher({ challenge, driver, input, signal: streamSignal }, event),
326
+ onNeedVoucher: (event, streamSignal) => handleSseNeedVoucher({ challenge, driver, headers, input, signal: streamSignal }, event),
327
327
  onReceipt() { },
328
328
  response,
329
329
  signal,
@@ -1,5 +1,6 @@
1
1
  import type { Hex } from 'ox';
2
2
  import { type Address } from 'viem';
3
+ import * as ProofRealm from '../../../client/internal/ProofRealm.js';
3
4
  import * as Account from '../../../viem/Account.js';
4
5
  import * as Client from '../../../viem/Client.js';
5
6
  import { type ChannelStore } from '../client/ChannelStore.js';
@@ -73,6 +74,11 @@ export declare namespace sessionManager {
73
74
  autoSwap?: sessionPlugin.Parameters['autoSwap'];
74
75
  /** Enables same-route HEAD bootstrap from a server session snapshot before opening a new channel. */
75
76
  bootstrap?: boolean | undefined;
77
+ /**
78
+ * Server origins allowed to request bootstrap proofs for a realm other than their hostname,
79
+ * mapped to that realm, such as `{ 'https://api.example.com': 'MPP Payment' }`.
80
+ */
81
+ trustedTempoProofRealms?: ProofRealm.TrustedRealms | undefined;
76
82
  /** Viem client instance. Shorthand for `getClient: () => client`. */
77
83
  client?: import('viem').Client | undefined;
78
84
  /** Token decimals used to convert `maxDeposit` to raw units. Defaults to `6`. */
@@ -4,6 +4,8 @@ import * as Challenge from '../../../Challenge.js';
4
4
  import * as Fetch from '../../../client/internal/Fetch.js';
5
5
  import * as MethodChallenge from '../../../client/internal/MethodChallenge.js';
6
6
  import * as MethodResponse from '../../../client/internal/MethodResponse.js';
7
+ import * as ProofRealm from '../../../client/internal/ProofRealm.js';
8
+ import { setCredentialHeader } from '../../../client/internal/protocols/Shared.js';
7
9
  import * as Constants from '../../../Constants.js';
8
10
  import * as Account from '../../../viem/Account.js';
9
11
  import * as Client from '../../../viem/Client.js';
@@ -353,17 +355,19 @@ export function sessionManager(parameters) {
353
355
  return undefined;
354
356
  if (!isZeroAmountChargeChallenge(challenge))
355
357
  return undefined;
358
+ // The proof authenticates the payer at the challenge realm: sign only for the server it is
359
+ // sent to, and never after a redirect to another origin.
360
+ const target = new URL(String(bootstrapInput), typeof location !== 'undefined' ? location.href : undefined);
361
+ if (challengeResponse.url && new URL(challengeResponse.url).origin !== target.origin)
362
+ return undefined;
363
+ ProofRealm.assert(challenge, target, parameters.trustedTempoProofRealms);
356
364
  const credential = await chargeMethod.createCredential({
357
365
  challenge: challenge,
358
366
  context: {},
359
367
  });
360
- const response = await config.fetch(bootstrapInput, {
361
- ...headInit,
362
- headers: {
363
- ...Fetch.normalizeHeaders(headInit.headers),
364
- [Constants.Headers.authorization]: credential,
365
- },
366
- });
368
+ const response = await config.fetch(bootstrapInput,
369
+ // Runtimes do not strip custom payment headers on cross-origin redirects.
370
+ setCredentialHeader({ ...headInit, redirect: 'manual' }, Challenge.credentialHeader(challenge), credential));
367
371
  if (response.ok)
368
372
  return await storeSnapshotHeader(response);
369
373
  return undefined;
@@ -643,6 +647,7 @@ export function sessionManager(parameters) {
643
647
  if (channelUse)
644
648
  throw new Error('SessionManager: a request is already in flight; concurrent requests on one manager are not supported');
645
649
  runtime.lastUrl = input;
650
+ runtime.lastHeaders = init?.headers;
646
651
  const previous = captureRuntimeStateSnapshot({
647
652
  channel: runtime.channel,
648
653
  spent: runtime.spent,
@@ -732,6 +737,7 @@ export function sessionManager(parameters) {
732
737
  createSessionCredential,
733
738
  fetch: config.fetch,
734
739
  getMinimumSpent: () => runtime.spent,
740
+ headers: runtime.lastHeaders,
735
741
  lastUrl: runtime.lastUrl,
736
742
  resolveSignedCloseAmount: (challenge) => getValidatedFallbackCloseAmount(target, challenge, true),
737
743
  signedCloseAmount: getValidatedFallbackCloseAmount(target),
@@ -799,6 +805,7 @@ export function sessionManager(parameters) {
799
805
  additionalDeposit: target.additionalDeposit,
800
806
  challenge: target.challenge,
801
807
  channelId: target.channelId,
808
+ headers: runtime.lastHeaders,
802
809
  input: target.input,
803
810
  });
804
811
  });
@@ -78,6 +78,8 @@ export type TopUpRequirement = {
78
78
  deposit: bigint;
79
79
  /** Original paid resource URL; converted to a management URL by the caller. */
80
80
  input: RequestInfo | URL;
81
+ /** Headers of the paid request; management posts keep its application `Authorization`. */
82
+ headers?: HeadersInit | undefined;
81
83
  /** Minimum cumulative voucher amount the server needs. */
82
84
  requiredCumulative: bigint;
83
85
  /** Cancels nested management requests for this top-up. */
@@ -158,6 +160,8 @@ export type ResolveNeedVoucherContextParameters = {
158
160
  getChannel(): ChannelEntry | null;
159
161
  /** Original paid resource URL; converted to management URL by the caller. */
160
162
  input: RequestInfo | URL;
163
+ /** Headers of the paid request; management posts keep its application `Authorization`. */
164
+ headers?: HeadersInit | undefined;
161
165
  /** Cancels top-up work triggered by this event. */
162
166
  signal?: AbortSignal | undefined;
163
167
  /** Performs the deposit top-up when server-required cumulative exceeds deposit. */
@@ -211,6 +215,8 @@ export type PostTopUpParameters = {
211
215
  fetch: typeof globalThis.fetch;
212
216
  /** Original paid resource URL; normalized to a management URL before posting. */
213
217
  input: RequestInfo | URL;
218
+ /** Headers of the paid request; management posts keep its application `Authorization`. */
219
+ headers?: HeadersInit | undefined;
214
220
  /** Cancels the top-up management request. */
215
221
  signal?: AbortSignal | undefined;
216
222
  /** Persists an unresolved funding outcome before another caller can reuse the channel. */
@@ -254,6 +260,8 @@ export type CloseHttpSessionParameters = {
254
260
  fetch: typeof globalThis.fetch;
255
261
  /** Last paid resource URL; used as the management endpoint base. */
256
262
  lastUrl: RequestInfo | URL | null;
263
+ /** Headers of the last paid request; the close post keeps its application `Authorization`. */
264
+ headers?: HeadersInit | undefined;
257
265
  /** Returns the latest cumulative spend already confirmed by the client. */
258
266
  getMinimumSpent?: (() => bigint) | undefined;
259
267
  /** Resolves the close amount again when the server refreshes the challenge. */
@@ -271,8 +279,8 @@ export declare function isTempoSessionChallenge(challenge: Challenge.Challenge):
271
279
  export declare function isTip1034SessionChallenge(challenge: Challenge.Challenge): challenge is TempoSessionChallenge;
272
280
  /** Reads a server-provided session snapshot from challenge method details. */
273
281
  export declare function getSessionSnapshot(challenge: TempoSessionChallenge): SessionSnapshot | undefined;
274
- /** Merges request headers and sets the payment authorization header for a retry. */
275
- export declare function requestInitWithAuthorization(input: RequestInfo | URL, init: RequestInit | undefined, credential: string): RequestInit;
282
+ /** Merges request headers and sets the challenge's credential header for a retry. */
283
+ export declare function requestInitWithAuthorization(input: RequestInfo | URL, init: RequestInit | undefined, credential: string, challenge: TempoSessionChallenge): RequestInit;
276
284
  /** Returns the exact resource URL used for out-of-band management POSTs, without its fragment. */
277
285
  export declare function managementInput(input: RequestInfo | URL): RequestInfo | URL;
278
286
  /** Converts a WebSocket URL into the HTTP URL used for its payment challenge probe. */
@@ -335,7 +343,7 @@ export type SsePaymentDriver = Pick<OpenSseSessionParameters, 'assertVoucherWith
335
343
  */
336
344
  export declare function openSseSession(input: RequestInfo | URL, init: SseDriverOptions | undefined, driver: OpenSseSessionParameters): Promise<AsyncIterable<string>>;
337
345
  /** @internal Consumes an already-paid SSE response with the session transport driver. */
338
- export declare function consumeSseSessionResponse(input: RequestInfo | URL, response: Response, options: SseResponseOptions | undefined, driver: OpenSseSessionParameters): AsyncIterable<string>;
346
+ export declare function consumeSseSessionResponse(input: RequestInfo | URL, response: Response, options: SseResponseOptions | undefined, driver: OpenSseSessionParameters, headers?: HeadersInit | undefined): AsyncIterable<string>;
339
347
  /** Application SSE frame emitted after session payment events are handled. */
340
348
  export type SseResponseFrame = {
341
349
  /** Parsed application data, when present. */
@@ -362,6 +370,7 @@ export declare function wrapSseResponse(parameters: DriveSseResponseParameters):
362
370
  export declare function handleSseNeedVoucher(parameters: {
363
371
  challenge: TempoSessionChallenge | null;
364
372
  driver: SsePaymentDriver;
373
+ headers?: HeadersInit | undefined;
365
374
  input: RequestInfo | URL;
366
375
  signal?: AbortSignal | undefined;
367
376
  }, event: NeedVoucherEvent): Promise<void>;
@@ -1,6 +1,7 @@
1
1
  import { Hex } from 'ox';
2
2
  import * as Challenge from '../../../Challenge.js';
3
3
  import * as Fetch from '../../../client/internal/Fetch.js';
4
+ import { setCredentialHeader } from '../../../client/internal/protocols/Shared.js';
4
5
  import * as Constants from '../../../Constants.js';
5
6
  import * as PaymentCredential from '../../../Credential.js';
6
7
  import { deserializeSessionReceipt, isEventStream, parseEvent, readSessionChallengeAmount, uint96, } from '../precompile/Protocol.js';
@@ -211,6 +212,7 @@ export async function resolveNeedVoucherContext(parameters) {
211
212
  await parameters.topUpIfNeeded({
212
213
  challenge: parameters.challenge,
213
214
  input: parameters.input,
215
+ headers: parameters.headers,
214
216
  channelId: parameters.expectedChannelId,
215
217
  deposit: eventAmounts.deposit,
216
218
  requiredCumulative: eventAmounts.requiredCumulative,
@@ -246,17 +248,18 @@ export function isTip1034SessionChallenge(challenge) {
246
248
  export function getSessionSnapshot(challenge) {
247
249
  return Constants.getMethodDetail(challenge.request.methodDetails, Constants.MethodDetailKeys.sessionSnapshot);
248
250
  }
249
- /** Merges request headers and sets the payment authorization header for a retry. */
250
- export function requestInitWithAuthorization(input, init, credential) {
251
+ /** Merges request headers and sets the challenge's credential header for a retry. */
252
+ export function requestInitWithAuthorization(input, init, credential, challenge) {
251
253
  const requestHeaders = input instanceof Request ? input.headers : undefined;
252
- return {
254
+ return setCredentialHeader({
253
255
  ...init,
256
+ // Runtimes do not strip custom payment headers on cross-origin redirects.
257
+ redirect: 'manual',
254
258
  headers: {
255
259
  ...Fetch.normalizeHeaders(requestHeaders),
256
260
  ...Fetch.normalizeHeaders(init?.headers),
257
- [Constants.Headers.authorization]: credential,
258
261
  },
259
- };
262
+ }, Challenge.credentialHeader(challenge), credential);
260
263
  }
261
264
  /** Returns the exact resource URL used for out-of-band management POSTs, without its fragment. */
262
265
  export function managementInput(input) {
@@ -324,6 +327,7 @@ export async function postTopUp(parameters) {
324
327
  },
325
328
  createSessionCredential: parameters.createSessionCredential,
326
329
  fetch: parameters.fetch,
330
+ headers: parameters.headers,
327
331
  input: parameters.input,
328
332
  signal: parameters.signal,
329
333
  }).catch(async (error) => {
@@ -360,7 +364,8 @@ async function postManagementCredential(parameters) {
360
364
  parameters.fetch(managementInput(parameters.input), {
361
365
  method: 'POST',
362
366
  signal: controller.signal,
363
- headers: { [Constants.Headers.authorization]: authorization },
367
+ redirect: 'manual',
368
+ headers: managementHeaders(challenge, authorization, parameters.input, parameters.headers),
364
369
  }),
365
370
  aborted,
366
371
  ]);
@@ -397,6 +402,7 @@ export async function retryHttpPaymentRequired(parameters) {
397
402
  await parameters.topUpIfNeeded({
398
403
  challenge,
399
404
  input: parameters.input,
405
+ headers: parameters.init?.headers,
400
406
  channelId: snapshot.channelId,
401
407
  deposit: BigInt(snapshot.deposit),
402
408
  requiredCumulative,
@@ -419,7 +425,7 @@ export async function retryHttpPaymentRequired(parameters) {
419
425
  descriptor: currentChannel.descriptor,
420
426
  cumulativeAmountRaw: cumulativeAmount.toString(),
421
427
  });
422
- retry = await parameters.fetch(parameters.input, requestInitWithAuthorization(parameters.input, parameters.init, credential));
428
+ retry = await parameters.fetch(parameters.input, requestInitWithAuthorization(parameters.input, parameters.init, credential, challenge));
423
429
  }
424
430
  catch (error) {
425
431
  await restore();
@@ -463,7 +469,8 @@ export async function closeHttpSession(parameters) {
463
469
  });
464
470
  return parameters.fetch(parameters.lastUrl, {
465
471
  method: 'POST',
466
- headers: { [Constants.Headers.authorization]: credential },
472
+ redirect: 'manual',
473
+ headers: managementHeaders(challenge, credential, parameters.lastUrl, parameters.headers),
467
474
  });
468
475
  };
469
476
  let response = await postClose(parameters.target.challenge);
@@ -516,17 +523,17 @@ export async function openSseSession(input, init, driver) {
516
523
  let response = await driver.doFetch(input, sseInit);
517
524
  if (!isEventStream(response) && driver.getChannel()?.opened)
518
525
  response = await driver.doFetch(input, sseInit);
519
- return consumeSseSessionResponse(input, response, { onReceipt, signal }, driver);
526
+ return consumeSseSessionResponse(input, response, { onReceipt, signal }, driver, sseInit.headers);
520
527
  }
521
528
  /** @internal Consumes an already-paid SSE response with the session transport driver. */
522
- export function consumeSseSessionResponse(input, response, options, driver) {
529
+ export function consumeSseSessionResponse(input, response, options, driver, headers) {
523
530
  if (!isEventStream(response))
524
531
  throw new Error('SSE response is not an event stream.');
525
532
  if (!response.body)
526
533
  throw new Error('Response has no body.');
527
534
  const challenge = driver.getChallenge();
528
535
  return iterateSseMessages({
529
- onNeedVoucher: (event) => handleSseNeedVoucher({ challenge, driver, input, signal: options?.signal }, event),
536
+ onNeedVoucher: (event) => handleSseNeedVoucher({ challenge, driver, headers, input, signal: options?.signal }, event),
530
537
  onReceipt(receipt) {
531
538
  driver.acceptReceipt(receipt);
532
539
  options?.onReceipt?.(receipt);
@@ -682,6 +689,7 @@ export async function handleSseNeedVoucher(parameters, event) {
682
689
  event,
683
690
  expectedChannelId: channel.channelId,
684
691
  getChannel: parameters.driver.getChannel,
692
+ headers: parameters.headers,
685
693
  input: parameters.input,
686
694
  signal: parameters.signal,
687
695
  topUpIfNeeded: parameters.driver.topUpIfNeeded,
@@ -693,6 +701,7 @@ export async function handleSseNeedVoucher(parameters, event) {
693
701
  context: resolution.context,
694
702
  createSessionCredential: parameters.driver.createSessionCredential,
695
703
  fetch: parameters.driver.fetch,
704
+ headers: parameters.headers,
696
705
  input: parameters.input,
697
706
  signal: parameters.signal,
698
707
  });
@@ -979,4 +988,21 @@ function handleReceipt(parameters, receipt) {
979
988
  parameters.driver.options?.onReceipt?.(receipt);
980
989
  parameters.driver.settleReceipt(receipt);
981
990
  }
991
+ /**
992
+ * Builds the headers of a management or close post: the session credential in the header the
993
+ * challenge selects, plus the paid request's application `Authorization` when the credential
994
+ * goes elsewhere (`requiresAuth`), so the post passes the same application authentication.
995
+ */
996
+ function managementHeaders(challenge, credential, input, headers) {
997
+ const header = Challenge.credentialHeader(challenge);
998
+ const result = new Headers();
999
+ if (header.toLowerCase() !== Constants.Headers.authorization.toLowerCase()) {
1000
+ const authorization = new Headers(headers).get(Constants.Headers.authorization) ??
1001
+ (input instanceof Request ? input.headers.get(Constants.Headers.authorization) : null);
1002
+ if (authorization && !authorization.startsWith(`${Constants.Schemes.payment} `))
1003
+ result.set(Constants.Headers.authorization, authorization);
1004
+ }
1005
+ result.set(header, credential);
1006
+ return result;
1007
+ }
982
1008
  //# sourceMappingURL=Transports.js.map
@@ -300,9 +300,6 @@ function validateCredentialSponsorship(parameters) {
300
300
  return;
301
301
  if (feePayer === true) {
302
302
  assertPrecompileFeePayerPolicy({ prepared: transaction, policy: feePayerPolicy });
303
- if (feePayerPolicy?.allowKeyAuthorization === false &&
304
- transaction.keyAuthorization !== undefined)
305
- throw new BadRequestError({ reason: 'fee-payer policy keyAuthorization is not allowed' });
306
303
  FeePayer.assertAllowedFeeToken(transaction, allowedFeeTokens);
307
304
  FeePayer.assertTransactionPolicy({
308
305
  challengeExpires,
@@ -35,6 +35,14 @@ export function fromStore(store, options) {
35
35
  return null;
36
36
  return (await store.get(recordKey(subscriptionId)));
37
37
  }
38
+ async function ensureAccessKeyAddressIndex(record) {
39
+ await store.update(accessKeyAddressKey(record.accessKeyAddress), (current) => {
40
+ if (current)
41
+ return { op: 'noop', result: undefined };
42
+ return { op: 'set', value: record, result: undefined };
43
+ });
44
+ return record;
45
+ }
38
46
  async function clearRenewalState(subscriptionId, periodIndex, attempt) {
39
47
  await store.update(recordKey(subscriptionId), (current) => {
40
48
  const subscription = current;
@@ -216,7 +224,7 @@ export function fromStore(store, options) {
216
224
  async getOrCreateAccessKey(key) {
217
225
  const existing = (await store.get(accessKeyKey(key)));
218
226
  if (existing)
219
- return existing;
227
+ return ensureAccessKeyAddressIndex(existing);
220
228
  const privateKey = Secp256k1.randomPrivateKey();
221
229
  const account = TempoAccount.fromSecp256k1(privateKey);
222
230
  const candidate = {
@@ -231,14 +239,7 @@ export function fromStore(store, options) {
231
239
  }
232
240
  return { op: 'set', value: candidate, result: candidate };
233
241
  })
234
- .then(async (record) => {
235
- await store.update(accessKeyAddressKey(record.accessKeyAddress), (current) => {
236
- if (current)
237
- return { op: 'noop', result: undefined };
238
- return { op: 'set', value: record, result: undefined };
239
- });
240
- return record;
241
- });
242
+ .then(ensureAccessKeyAddressIndex);
242
243
  },
243
244
  async put(record) {
244
245
  await store.put(recordKey(record.subscriptionId), record);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mppx",
3
3
  "type": "module",
4
- "version": "0.15.0",
4
+ "version": "0.16.0",
5
5
  "main": "./dist/index.js",
6
6
  "license": "MIT",
7
7
  "homepage": "https://github.com/wevm/mppx#readme",
@@ -35,9 +35,9 @@
35
35
  "types": "./dist/client/node.d.ts",
36
36
  "default": "./dist/client/node.js"
37
37
  },
38
- "./attestation": {
39
- "types": "./dist/attestation/index.d.ts",
40
- "default": "./dist/attestation/index.js"
38
+ "./identity": {
39
+ "types": "./dist/identity/index.d.ts",
40
+ "default": "./dist/identity/index.js"
41
41
  },
42
42
  "./discovery": {
43
43
  "types": "./dist/discovery/index.d.ts",
@@ -123,13 +123,17 @@
123
123
  "types": "./dist/tempo/index.d.ts",
124
124
  "default": "./dist/tempo/index.js"
125
125
  },
126
- "./attestation/tap": {
127
- "types": "./dist/tap/index.d.ts",
128
- "default": "./dist/tap/index.js"
126
+ "./extensions/identity": {
127
+ "types": "./dist/extensions/identity/index.d.ts",
128
+ "default": "./dist/extensions/identity/index.js"
129
129
  },
130
- "./attestation/web-bot-auth": {
131
- "types": "./dist/web-bot-auth/index.d.ts",
132
- "default": "./dist/web-bot-auth/index.js"
130
+ "./extensions/identity/tap": {
131
+ "types": "./dist/extensions/identity/tap/index.d.ts",
132
+ "default": "./dist/extensions/identity/tap/index.js"
133
+ },
134
+ "./extensions/identity/web-bot-auth": {
135
+ "types": "./dist/extensions/identity/web-bot-auth/index.d.ts",
136
+ "default": "./dist/extensions/identity/web-bot-auth/index.js"
133
137
  },
134
138
  "./html": {
135
139
  "types": "./dist/Html.d.ts",
@@ -206,7 +210,7 @@
206
210
  "eventsource-parser": "4.1.1",
207
211
  "ox": "0.14.45",
208
212
  "structured-headers": "2.1.0",
209
- "zod": "^4.6.5"
213
+ "zod": "^4.6.7"
210
214
  },
211
215
  "repository": {
212
216
  "type": "git",
@@ -1,17 +0,0 @@
1
- import type * as Types from './Types.js';
2
- /**
3
- * Combines request signers using one shared context per call.
4
- *
5
- * The returned signer creates a fresh context unless its caller supplies one,
6
- * allowing every signature on one HTTP attempt to share a nonce and timestamp.
7
- */
8
- export declare function composeSigners(...signers: readonly [Types.Signer, ...Types.Signer[]]): Types.Signer<'composed'>;
9
- /**
10
- * Wraps a fetch implementation so every outbound request is attested.
11
- *
12
- * Configure this wrapper as the underlying `fetch` passed to `mppx/client`
13
- * `Fetch.from()`. MPPX then uses it for both the initial request and its
14
- * automatic request containing an `Authorization: Payment` credential.
15
- */
16
- export declare function wrapFetch(fetch: typeof globalThis.fetch, signer: Types.Signer): typeof globalThis.fetch;
17
- //# sourceMappingURL=Client.d.ts.map
@@ -1,28 +0,0 @@
1
- import * as SigningContext from './internal/SigningContext.js';
2
- /**
3
- * Combines request signers using one shared context per call.
4
- *
5
- * The returned signer creates a fresh context unless its caller supplies one,
6
- * allowing every signature on one HTTP attempt to share a nonce and timestamp.
7
- */
8
- export function composeSigners(...signers) {
9
- return {
10
- protocol: 'composed',
11
- async sign(request, context = SigningContext.create()) {
12
- for (const signer of signers)
13
- request = await signer.sign(request, context);
14
- return request;
15
- },
16
- };
17
- }
18
- /**
19
- * Wraps a fetch implementation so every outbound request is attested.
20
- *
21
- * Configure this wrapper as the underlying `fetch` passed to `mppx/client`
22
- * `Fetch.from()`. MPPX then uses it for both the initial request and its
23
- * automatic request containing an `Authorization: Payment` credential.
24
- */
25
- export function wrapFetch(fetch, signer) {
26
- return async (input, init) => fetch(await signer.sign(new Request(input, init)));
27
- }
28
- //# sourceMappingURL=Client.js.map
@@ -1,4 +0,0 @@
1
- import type * as Types from './Types.js';
2
- /** Verifies every configured request-attestation protocol. */
3
- export declare function verify<const verifiers extends Types.VerifierMap>(request: Request, verifiers: verifiers): Promise<Types.VerificationsFrom<verifiers>>;
4
- //# sourceMappingURL=Server.d.ts.map
@@ -1,8 +0,0 @@
1
- /** Verifies every configured request-attestation protocol. */
2
- export async function verify(request, verifiers) {
3
- const outcomes = {};
4
- for (const [name, verifier] of Object.entries(verifiers))
5
- outcomes[name] = await verifier.verify(request);
6
- return outcomes;
7
- }
8
- //# sourceMappingURL=Server.js.map
@@ -1,76 +0,0 @@
1
- import type { MaybePromise } from '../internal/types.js';
2
- import type { Algorithms } from './Constants.js';
3
- /** RFC 9421 algorithms implemented by the request-attestation signer and verifier. */
4
- export type SignatureAlgorithm = (typeof Algorithms)[keyof typeof Algorithms];
5
- /** Resolves a trusted public key for a parsed request signature. */
6
- export type KeyResolver = (parameters: {
7
- /** RFC 9421 algorithm declared by the signature. */
8
- algorithm: SignatureAlgorithm;
9
- /** Key identifier declared by the signature. */
10
- keyId: string;
11
- /** Request carrying the signature. */
12
- request: Request;
13
- }) => MaybePromise<CryptoKey | undefined>;
14
- /**
15
- * The complete outcome of attempting one protocol verifier.
16
- *
17
- * `absent` means the protocol did not appear on the request. `invalid` means
18
- * it appeared but failed validation and must reject the request. `unverified`
19
- * means key material or another required verification dependency was unavailable.
20
- * `verified` carries protocol-specific data that is safe for callers to inspect.
21
- */
22
- export type Verification<value = unknown> = {
23
- status: 'absent';
24
- } | {
25
- status: 'invalid';
26
- reason: string;
27
- } | {
28
- status: 'unverified';
29
- reason: string;
30
- } | {
31
- status: 'verified';
32
- value: value;
33
- };
34
- /**
35
- * Verifies one request-attestation protocol carried by an HTTP request.
36
- *
37
- * Verifiers are registered in a {@link VerifierMap}; each map key names the
38
- * result returned to the caller.
39
- */
40
- export type Verifier<value = unknown> = {
41
- /** Validates the request and returns a complete protocol verification outcome. */
42
- verify: (request: Request) => MaybePromise<Verification<value>>;
43
- };
44
- /** Maps each recognized protocol name to its verifier. */
45
- export type VerifierMap = Readonly<Record<string, Verifier>>;
46
- /** Preserves each configured protocol's complete verification outcome. */
47
- export type VerificationsFrom<verifiers extends VerifierMap> = {
48
- readonly [protocol in keyof verifiers]: Awaited<ReturnType<verifiers[protocol]['verify']>>;
49
- };
50
- /**
51
- * Values shared by every signer contributing to one HTTP request attempt.
52
- *
53
- * A caller may reuse this context across independent signature protocols so
54
- * they describe the same attempt. A later network retry must use a new context.
55
- */
56
- export type SigningContext = {
57
- /** Unix timestamp, in seconds, at which the request attempt was created. */
58
- readonly created: number;
59
- /** Base64url-encoded random value identifying this request attempt. */
60
- readonly nonce: string;
61
- };
62
- /**
63
- * Signs each outbound HTTP request, including MPP payment retries.
64
- *
65
- * The protocol type preserves which adapter produced the signer for diagnostics
66
- * and composition, without asserting that the signer alone authorizes payment.
67
- */
68
- export type Signer<protocol extends string = string> = {
69
- /** Protocol identifier used for diagnostics. */
70
- protocol: protocol;
71
- /** Returns a new request containing the protocol's signature material. */
72
- sign: (request: Request, context?: SigningContext | undefined) => MaybePromise<Request>;
73
- };
74
- /** Maps application-defined names to request-attestation signers. */
75
- export type SignerMap = Readonly<Record<string, Signer>>;
76
- //# sourceMappingURL=Types.d.ts.map
@@ -1,4 +0,0 @@
1
- import type * as Types from '../Types.js';
2
- /** Creates the signing context for one outbound HTTP request attempt. */
3
- export declare function create(): Types.SigningContext;
4
- //# sourceMappingURL=SigningContext.d.ts.map
@@ -1,26 +0,0 @@
1
- import type * as AttestationTypes from '../../attestation/Types.js';
2
- import { Constants } from '../Constants.js';
3
- /**
4
- * Creates a TAP request signer.
5
- *
6
- * The signer emits RFC 9421 HTTP message signatures with TAP's
7
- * `agent-browser-auth` or `agent-payer-auth` tag. Pass it to
8
- * `Attestation.Client.wrapFetch` so MPPX signs both the initial request and
9
- * its automatic paid retry.
10
- */
11
- export declare function signer(config: signer.Config): AttestationTypes.Signer<typeof Constants.protocol>;
12
- export declare namespace signer {
13
- type Config = {
14
- /** TAP signature type for the request. */
15
- intent: (typeof Constants.intents)[keyof typeof Constants.intents];
16
- /** Ed25519 or RSA-PSS SHA-512 private key provisioned to the agent provider. */
17
- key: CryptoKey;
18
- /** Identifier the merchant uses to resolve the signing public key. */
19
- keyId: string;
20
- /** Signature lifetime in seconds. TAP permits at most eight minutes. @default 480 */
21
- expiresIn?: number | undefined;
22
- /** RFC 9421 dictionary label. @default 'tap' */
23
- label?: string | undefined;
24
- };
25
- }
26
- //# sourceMappingURL=index.d.ts.map
@@ -1,20 +0,0 @@
1
- import type * as Attestation from '../../attestation/Types.js';
2
- import type * as Store from '../../Store.js';
3
- import type * as Types from '../Types.js';
4
- /**
5
- * Creates a verifier for TAP agent-recognition signatures.
6
- *
7
- * TAP verifies trusted agent participation and request integrity. Consumer
8
- * recognition and payment-container verification stay protocol-specific and
9
- * are intentionally not inferred from this header-only verifier.
10
- */
11
- export declare function verifier(config: verifier.Config): Attestation.Verifier<Types.VerifiedRequest>;
12
- export declare namespace verifier {
13
- type Config = {
14
- /** Resolves a TAP-approved public key by its `keyid` and signature algorithm. */
15
- keyResolver: Attestation.KeyResolver;
16
- /** Atomically consumes each nonce in shared storage for multi-instance deployments. */
17
- nonceStore: Store.AtomicStore;
18
- };
19
- }
20
- //# sourceMappingURL=index.d.ts.map
File without changes
File without changes