Skip to content

feat: enforce request-bound payment authorization v2#313

Open
AnkanMisra wants to merge 17 commits into
mainfrom
feat/gateway-payment-authorization-v2
Open

feat: enforce request-bound payment authorization v2#313
AnkanMisra wants to merge 17 commits into
mainfrom
feat/gateway-payment-authorization-v2

Conversation

@AnkanMisra

@AnkanMisra AnkanMisra commented Jul 16, 2026

Copy link
Copy Markdown
Owner

Summary

  • make the gateway issue v2 challenges bound to the trusted audience, HTTP method, encoded resource, content type, exact request bytes, and claimed payer
  • reconstruct the authorization context server-side on signed retries and require the verifier recovered address to match X-402-Payer
  • reject legacy authorization by default in the verifier, browser client, and TypeScript SDK, including custom-adapter downgrade attempts
  • preserve v2 authorization metadata in signed receipts and expose bounded authorization outcome metrics
  • replace credential-dependent E2E execution with a deterministic loopback provider covering success, replay, and one-byte tampering
  • synchronize OpenAPI, environment templates, Compose/Fly configuration, and protocol documentation

Security properties

  • audience comes only from required PAYGATE_AUDIENCE, never request host or forwarded headers
  • cache hits and misses use the same request-bound verification path
  • v1 is rejected by default through MIN_AUTHORIZATION_VERSION=2
  • malformed payer, verifier mismatch, tampered binding, replay, and verifier failure all fail before paid output

Validation

  • cd gateway && go test -count=1 ./... && go vet ./...
  • cd verifier && cargo fmt -- --check && cargo clippy -- -D warnings && cargo test — 51 passed
  • cd sdk/typescript && bun run typecheck && bun test && bun run build — 26 passed, 1 opt-in live test skipped
  • cd web && bun run lint && bun run typecheck && bun run test:unit && bun run build — 76 passed
  • VERIFIER_NONCE_STORE=memory ./run_e2e.sh — 4 passed
  • YAML parsing, shell syntax, diff checks, and an exhaustive security diff review — passed with no diff-introduced findings

Follow-up to #306.

Note

Enforce request-bound payment authorization v2 across gateway, verifier, and SDK

  • Payment authorization is now bound to the exact request (audience, method, resource, content type, request body hash, and claimed payer address) and signed as EIP-712 PaymentAuthorization v2; legacy v1 contexts are rejected by default.
  • The gateway reads the request body before verification, reconstructs the v2 PaymentContext server-side, validates X-402-Payer as a hex address, and requires the verifier's recovered address to match the claimed payer.
  • The verifier enforces MIN_AUTHORIZATION_VERSION (default 2) and returns authorization_version_too_old (400) for older contexts; new error codes signer_mismatch and invalid_authorization_context are also mapped.
  • Receipts are versioned (v1.0 / v2.0) and v2 receipts carry full authorization-binding metadata and a timestamp; validateReceiptFormat strictly rejects extra or missing fields.
  • PAYGATE_AUDIENCE is now a required, canonicalized gateway environment variable; startup fails if it is missing or malformed.
  • The TypeScript SDK and web client enforce v2-only payment contexts, validate receipts against the signed authorization and exact request/response bytes, and include X-402-Payer on signed retries.
  • E2E tests no longer require OPENROUTER_API_KEY and instead start a deterministic local mock on port 3100; the CI workflow always runs setup and test steps.
  • Risk: any client or verifier deployment still relying on v1 authorization will receive authorization_version_too_old rejections until MIN_AUTHORIZATION_VERSION is lowered to 1 during migration.

Macroscope summarized e9f55ed.

Summary by CodeRabbit

  • New Features
    • Request-bound authorization v2 now binds gateway audience, HTTP method, resource (+ raw query), content type, and the exact request-body SHA-256; signed retries include X-402-Payer.
    • Added MIN_AUTHORIZATION_VERSION (defaulting to 2) and stricter PAYGATE_AUDIENCE origin normalization; added a Prometheus metric for authorization outcomes.
  • Bug Fixes
    • Verifier enforces minimum authorization version and maps authorization/signature failures to clearer HTTP statuses; cached/timeout paths now validate the same request-body hash bindings, including payer vs recovered-address checks.
  • Documentation / Tests
    • Updated protocol, OpenAPI, SDK, and receipt documentation; expanded deterministic E2E coverage with the local OpenRouter-compatible mock and signed-retry/CORS preflight checks.

Co-authored-by: codex <codex@users.noreply.github.com>
@vercel

vercel Bot commented Jul 16, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
microai-paygate Ready Ready Preview, Comment Jul 20, 2026 2:15pm

@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

This PR makes authorization v2 request-bound by adding audience, method, resource, content type, body hash, and payer validation. It enforces minimum authorization version 2 by default, propagates binding data into receipts, updates SDK/web validation, and adds deterministic E2E and deployment coverage.

Changes

Request-bound authorization

Layer / File(s) Summary
Binding and configuration
gateway/main.go, gateway/request_binding.go, gateway/config.go, gateway/config_test.go
Adds v2 payment-context fields, canonical PAYGATE_AUDIENCE validation, and request binding derived from the exact HTTP request and body.
Gateway verification
gateway/payment_flow.go, gateway/cache.go, gateway/errors.go, gateway/metrics.go
Requires X-402-Payer, reconstructs verifier context, validates recovered signer addresses, maps authorization failures, and records outcomes.
Verifier enforcement
verifier/src/main.rs
Adds MIN_AUTHORIZATION_VERSION, defaults it to 2, and rejects older authorization versions.
Receipts and contracts
gateway/receipt.go, gateway/openapi.yaml, sdk/typescript/src/receipts.ts, web/src/lib/verify-receipt.ts
Carries request-bound authorization metadata through receipts and public schemas.
SDK and web validation
sdk/typescript/src/protocol/*, web/src/lib/x402-client.ts
Rejects legacy or incomplete payment contexts and requires valid v2 request bindings.
E2E and smoke validation
run_e2e.sh, tests/e2e.test.ts, tests/mock-openrouter.ts, web/scripts/deployed-smoke-test.ts
Uses v2 typed-data signing, deterministic provider execution, readiness polling, payer headers, tampering checks, and CORS validation.
Deployment and protocol documentation
.env*, docker-compose.yml, deploy/fly/*, DEPLOY.md, README.md, gateway/README.md, verifier/README.md, web/src/app/docs/protocol/page.mdx
Documents and configures the new audience and minimum authorization-version settings and v2 retry behavior.

Estimated code review effort: 5 (Critical) | ~90 minutes

Possibly related issues

Possibly related PRs

Suggested labels: level:advanced

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 19.39% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the main change: enforcing request-bound payment authorization v2.
Description check ✅ Passed The description covers summary, security properties, validation, and reviewer notes; only some optional template sections are omitted.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/gateway-payment-authorization-v2

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added documentation Improvements or additions to documentation go Pull requests that update go code github_actions Pull requests that update GitHub Actions code rust Pull requests that update rust code TypeScript TypeScript code type:docs Documentation, API docs, examples, or contributor docs. type:testing Tests, coverage, fixtures, or validation-only work. type:devops Deployment, observability, operations, CI, or infrastructure work. labels Jul 16, 2026
@AnkanMisra AnkanMisra added type:feature New user-facing or API-facing capability. type:security Security, abuse resistance, secret safety, or payment integrity work. labels Jul 16, 2026
Co-authored-by: codex <codex@users.noreply.github.com>
Comment thread gateway/config.go
Comment thread .github/workflows/e2e.yml
Co-authored-by: codex <codex@users.noreply.github.com>
Comment thread run_e2e.sh
Co-authored-by: codex <codex@users.noreply.github.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4b5abb0d44

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .github/workflows/e2e.yml Outdated
Comment thread gateway/config.go Outdated
Co-authored-by: codex <codex@users.noreply.github.com>
@AnkanMisra

Copy link
Copy Markdown
Owner Author

@codex review Please perform a deep review of the latest commit. Inspect every changed file and trace the payment authorization flow across gateway, verifier, web, SDK, E2E tests, receipts, cache behavior, configuration, deployment files, and documentation. Focus on correctness, security, replay resistance, request binding, cross-service contract parity, failure paths, compatibility, CI coverage, and missing regression tests.

@chatgpt-codex-connector

Copy link
Copy Markdown
Contributor

Codex Review: Didn't find any major issues. Breezy!

Reviewed commit: 65d3b33858

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@AnkanMisra

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

if (res.status === 502) {
const text = await res.text();
if (text.includes("upstream_unavailable")) {
expect(true).toBe(true);
return;

P2 Badge Require deterministic E2E to hit the mock provider

In the new deterministic CI path where run_e2e.sh starts the OpenRouter mock when no key is set, the signed happy path should require HTTP 200. Keeping this upstream_unavailable escape means CI still passes if the mock never starts or OPENROUTER_URL wiring breaks, so the new E2E no longer proves the loopback provider path works; remove this fallback or gate it behind an explicit live-provider mode.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread gateway/config.go
Comment thread gateway/payment_flow.go
Comment thread run_e2e.sh
@AnkanMisra

Copy link
Copy Markdown
Owner Author

@codex review Please perform another deep review of the latest commit. Re-check every changed file and the complete request-bound authorization v2 flow, including cache-hit body binding, audience normalization, verifier error mapping, deterministic readiness, CORS challenge/preflight behavior, deployment configuration, cross-service parity, and regression coverage.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
gateway/cache_integration_test.go (1)

205-217: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Assert the complete 402 payment context in the response body.

As per coding guidelines: "gateway/**/*.go: X402 payment flow: missing signatures should produce a complete 402 payment context".

While the test currently asserts the 402 status code, consider parsing the response body and verifying that the complete payment context is actually returned, ensuring full regression coverage for this specific requirement.

♻️ Proposed refactor to assert context
 	if w4.Code != 402 {
 		t.Errorf("Expected status 402 for missing signature, got %d", w4.Code)
 	}
+
+	var w4Resp map[string]interface{}
+	if err := json.Unmarshal(w4.Body.Bytes(), &w4Resp); err == nil {
+		if _, hasCtx := w4Resp["paymentContext"]; !hasCtx {
+			t.Errorf("Expected 402 response to contain a complete paymentContext")
+		}
+	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@gateway/cache_integration_test.go` around lines 205 - 217, Extend the
missing-signature case for reqNoSig in the cache integration test to parse w4’s
response body and assert that the complete x402 payment context is present, not
just the 402 status. Reuse the existing response schema or payment-context
symbols from the test suite, and retain the current status assertion.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@gateway/cache_integration_test.go`:
- Around line 205-217: Extend the missing-signature case for reqNoSig in the
cache integration test to parse w4’s response body and assert that the complete
x402 payment context is present, not just the 402 status. Reuse the existing
response schema or payment-context symbols from the test suite, and retain the
current status assertion.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4105c87a-9c9f-4845-856b-c21f2ae7b46d

📥 Commits

Reviewing files that changed from the base of the PR and between 58190f9 and 2e75343.

📒 Files selected for processing (12)
  • .github/workflows/e2e.yml
  • DEPLOY.md
  • gateway/README.md
  • gateway/cache_integration_test.go
  • gateway/config.go
  • gateway/config_test.go
  • gateway/errors_test.go
  • run_e2e.sh
  • tests/README.md
  • tests/mock-openrouter.ts
  • web/scripts/deployed-smoke-test.ts
  • web/src/app/docs/protocol/page.mdx
🚧 Files skipped from review as they are similar to previous changes (11)
  • tests/mock-openrouter.ts
  • gateway/config.go
  • gateway/config_test.go
  • run_e2e.sh
  • DEPLOY.md
  • tests/README.md
  • web/src/app/docs/protocol/page.mdx
  • .github/workflows/e2e.yml
  • gateway/errors_test.go
  • gateway/README.md
  • web/scripts/deployed-smoke-test.ts

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

"X-402-Payer",

P2 Badge Add an automated preflight regression test

No CI test exercises the gateway's CORS middleware with a browser-style OPTIONS request containing Access-Control-Request-Headers: ...x-402-payer; the Bun E2E requests do not enforce CORS, and deployed-smoke-test.ts is not run by a workflow. If this allowlist entry or the middleware ordering regresses, every browser v2 signed retry will be blocked before reaching the gateway despite the service tests passing. The allowlist change is correct; please add a router-level test asserting the allowed origin, POST method, and all signed-retry headers.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread gateway/config.go
Comment thread web/src/app/docs/protocol/page.mdx
Comment thread run_e2e.sh
Co-authored-by: codex <codex@users.noreply.github.com>
@AnkanMisra

Copy link
Copy Markdown
Owner Author

Addressed the preflight regression-test finding in 68239b7. The gateway now exposes its real CORS middleware setup to a router-level test that sends a browser-style OPTIONS request and asserts the exact origin, POST, and all signed-retry headers including X-402-Payer.

@AnkanMisra

Copy link
Copy Markdown
Owner Author

@codex review Please perform another deep review of the latest commit. Verify the router-level CORS preflight regression, numeric audience-port canonicalization, authorization v2 documentation parity, deterministic E2E guidance, cache body binding, cross-service contracts, security failure paths, and all affected tests.

Co-authored-by: codex <codex@users.noreply.github.com>
@AnkanMisra

Copy link
Copy Markdown
Owner Author

Addressed the remaining cache-challenge coverage suggestion in 006520a. The Redis-backed cache integration test now parses the missing-signature response and verifies every required authorization v2 field, including the exact request-body hash. Focused and full gateway tests plus go vet pass.

@AnkanMisra

Copy link
Copy Markdown
Owner Author

@codex review Please perform another deep review of the latest commit. Re-check every changed file and trace authorization v2 end to end, with special attention to cache-hit and missing-signature behavior, exact request-body binding, CORS preflight coverage, audience normalization, verifier parity, receipts, deployment configuration, documentation, and regression tests.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 006520acd6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .github/workflows/e2e.yml Outdated
Comment thread gateway/config.go
Comment thread DEPLOY.md Outdated
Comment thread gateway/receipt.go
Comment thread gateway/cache_integration_test.go Outdated
Co-authored-by: codex <codex@users.noreply.github.com>
@AnkanMisra

Copy link
Copy Markdown
Owner Author

@codex review Please perform another deep review of the latest commit. Verify the cold-run E2E budget, browser-compatible IP audience normalization, staged v1-to-v2 deployment order, receipt schema 2.0 with legacy 1.0 verification, fresh nonce and exact-body cache authorization, cross-service serialization parity, security failure paths, and all regression tests.

@AnkanMisra AnkanMisra self-assigned this Jul 20, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4ee9ebdd0a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread sdk/typescript/src/receipts.ts
Comment thread gateway/openapi.yaml Outdated
Comment thread gateway/config.go
Co-authored-by: codex <codex@users.noreply.github.com>
@AnkanMisra

Copy link
Copy Markdown
Owner Author

@codex review Please perform another deep review of the latest commit. Verify exact v2 receipt-to-authorization binding and substitution resistance, version-discriminated OpenAPI receipt schemas, trailing-dot and ambiguous IP audience handling, cross-service receipt serialization, compatibility with stored v1 receipts, security failure paths, and all regression tests.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c705e6dd94

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread gateway/receipt.go
Comment thread sdk/typescript/src/receipts.ts
Comment thread gateway/config.go
Comment thread web/src/lib/verify-receipt.ts
Comment thread gateway/config.go
Comment thread sdk/typescript/src/receipts.ts
Co-authored-by: codex <codex@users.noreply.github.com>
@AnkanMisra

Copy link
Copy Markdown
Owner Author

@codex review

Please perform a deep review of the current head. Trace the payment authorization and receipt contract end to end across gateway, verifier, SDK, web, E2E tests, OpenAPI, configuration, and deployment compatibility. Focus on correctness, security, canonicalization, replay behavior, backward compatibility, and missing negative tests.

Comment thread gateway/config.go
if endsInIPv4Number(asciiHostname) {
return "", fmt.Errorf("PAYGATE_AUDIENCE must contain a canonical IP address")
}
return strings.ToLower(asciiHostname), nil

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium gateway/config.go:143

normalizeAudienceHostname returns a hostname with a trailing dot for input like gateway.example。, bypassing the rejection of trailing-dot hosts. The early check on line 120 only catches an ASCII ., but idna.Lookup.ToASCII normalizes Unicode dot-equivalent characters such as (U+3002) to ., so the trailing dot is introduced after the check. The returned canonical hostname then ends with ., violating the canonical-origin policy. Consider rejecting a trailing dot again after the ToASCII call.

 	asciiHostname, err := idna.Lookup.ToASCII(hostname)
 	if err != nil {
 		return "", fmt.Errorf("PAYGATE_AUDIENCE must contain a valid hostname: %w", err)
 	}
+	if strings.HasSuffix(asciiHostname, ".") {
+		return "", fmt.Errorf("PAYGATE_AUDIENCE hostname must not end with a dot")
+	}
 	if address, err := netip.ParseAddr(asciiHostname); err == nil {
 		return address.String(), nil
 	}
 	if endsInIPv4Number(asciiHostname) {
 		return "", fmt.Errorf("PAYGATE_AUDIENCE must contain a canonical IP address")
 	}
 	return strings.ToLower(asciiHostname), nil
🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @gateway/config.go around line 143:

`normalizeAudienceHostname` returns a hostname with a trailing dot for input like `gateway.example。`, bypassing the rejection of trailing-dot hosts. The early check on line 120 only catches an ASCII `.`, but `idna.Lookup.ToASCII` normalizes Unicode dot-equivalent characters such as `。` (U+3002) to `.`, so the trailing dot is introduced after the check. The returned canonical hostname then ends with `.`, violating the canonical-origin policy. Consider rejecting a trailing dot again after the `ToASCII` call.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e564accc6e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread sdk/typescript/src/client.ts
Comment thread web/src/hooks/use-x402.ts Outdated
Co-authored-by: codex <codex@users.noreply.github.com>
@AnkanMisra

Copy link
Copy Markdown
Owner Author

@codex review

Please perform another deep review of the current head. Trace custom protocol-adapter payer resolution and exact receipt binding across the SDK and browser, including request/response byte hashes, raw-body parsing, signature trust, malformed inputs, backward compatibility, and all cross-service authorization v2 behavior.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e9f55ed81b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +169 to +170
// Serialize according to the versioned Go receipt contract.
const receiptJSON = serializeReceiptForGateway(signedReceipt.receipt);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Pin the gateway key before accepting v2 receipts

The new v2 serialization makes these receipts pass verifyReceipt, but the function still authenticates the signature only against server_public_key supplied inside the same untrusted receipt. A forged receipt can therefore include an attacker-controlled key and matching signature, pass the new authorization/body-hash comparisons, be saved by use-x402.ts, and later be displayed as “Receipt verified.” Require an out-of-band trusted gateway public key, as the TypeScript SDK already does, and compare the recovered key against it before accepting the receipt.

Useful? React with 👍 / 👎.

Comment thread web/src/hooks/use-x402.ts
receiptMatchesPaymentAuthorization(receipt, context, payer) &&
receiptMatchesExchange(
receipt,
new URL(getSummarizeUrl()).pathname,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Derive receipt endpoints consistently for prefixed gateways

When NEXT_PUBLIC_GATEWAY_URL contains a path prefix, which x402-client.test.ts explicitly supports, this comparison expects the receipt endpoint to include that public prefix. The Go gateway records c.Request.URL.Path, so a reverse proxy that strips /paygate before forwarding—the only way the current unprefixed Gin route can handle that deployment—produces /api/ai/summarize in the receipt, and the browser rejects an otherwise successful paid response. Derive the expected receipt endpoint from the API route as the SDK does, rather than from the full prefixed public URL.

Useful? React with 👍 / 👎.

Comment on lines +90 to +92
if (isPaymentContextV2(paymentContext) && payer === undefined) {
payer = await this.signer.getAddress?.();
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Respect a custom adapter's undefined payer result

When a custom adapter implements getPayer but deliberately returns undefined—for example because its signing implementation resolves a delegated or protocol-specific payer internally—this fallback silently replaces that decision with signer.getAddress(). The substituted EOA is then passed into signing, header construction, and the new receipt-authorization comparison, so an otherwise valid custom adapter can create the wrong authorization or have its successful receipt rejected. Use the signer fallback only when the adapter does not implement getPayer, rather than whenever the implemented hook returns undefined.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation github_actions Pull requests that update GitHub Actions code go Pull requests that update go code rust Pull requests that update rust code type:devops Deployment, observability, operations, CI, or infrastructure work. type:docs Documentation, API docs, examples, or contributor docs. type:feature New user-facing or API-facing capability. type:security Security, abuse resistance, secret safety, or payment integrity work. type:testing Tests, coverage, fixtures, or validation-only work. TypeScript TypeScript code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant