Skip to content

feat: CDP mainnet facilitator auth (per-request signed EdDSA JWT) - #5

Open
Robin Schulte (relativvv) wants to merge 3 commits into
mainfrom
feat/cdp-facilitator-jwt-auth
Open

feat: CDP mainnet facilitator auth (per-request signed EdDSA JWT)#5
Robin Schulte (relativvv) wants to merge 3 commits into
mainfrom
feat/cdp-facilitator-jwt-auth

Conversation

@relativvv

Copy link
Copy Markdown

Draft — extracted from the agent-shop integration project for upstream review.

Problem

X402FacilitatorClient only ever sent a static Authorization: Bearer <facilitatorApiKey>. That works for testnet / static-token facilitators but not for Coinbase's CDP mainnet facilitator, which requires a short-lived JWT signed with the CDP API key on every request, with a uri claim bound to the exact method + host + path. Without it, mainnet settlement through the fee-free CDP facilitator is impossible without standing up a signing proxy.

Root cause

The facilitator auth had a single, hardcoded mode (static bearer). There was no way to plug in a request-signing scheme, so CDP's per-request signed-JWT requirement could not be satisfied.

What changed

  • src/Core/X402/X402CdpJwtFactory.php (new) — mints a CDP Bearer JWT with no external JWT library:
    • header alg=EdDSA, kid, nonce
    • claims sub=keyId, iss=cdp, nbf, exp=+120s, uri="POST <host><path>"
    • signed with the CDP Ed25519 secret via libsodium (accepts a 32-byte seed or a 64-byte key)
  • src/Core/X402/X402FacilitatorClient.php — when facilitatorCdpKeyId + facilitatorCdpKeySecret are configured, signs a per-request JWT for the exact URL; otherwise falls back to the existing static bearer.
  • src/Core/X402/Config/X402Config.php + X402ConfigService.php + src/Resources/config/config.xml — new config fields facilitatorCdpKeyId (text) and facilitatorCdpKeySecret (password, Ed25519).
  • src/Resources/config/services.xml — register the JWT factory and inject it into the facilitator client.
  • tests/Unit/Core/X402/X402CdpJwtFactoryTest.php (new) — verifies the minted JWT against a test Ed25519 public key and asserts the CDP claim shape.
  • tests/Unit/Core/X402/X402FacilitatorClientTest.php — extended for the CDP-signing branch.

Testing

  • php -l clean on all changed PHP files.
  • vendor/bin/phpunit --filter 'X402CdpJwtFactory|X402FacilitatorClient'11 tests, 37 assertions, OK (PHP 8.3.30, PHPUnit 11.5.56).

Notes for reviewers

  • The design keeps facilitator auth pluggable: static-bearer mode is unchanged; the CDP mode activates only when both CDP config fields are set. This is a purely additive, opt-in path.
  • EdDSA only for now — CDP's recommended key type. ES256/EC could be added behind the same seam if needed.
  • No new runtime dependency: signing uses the bundled libsodium extension.

… — local only, no PR

X402FacilitatorClient now signs a Coinbase CDP Bearer JWT (Ed25519, uri-bound, 120s)
when facilitatorCdpKeyId+Secret are configured; static bearer otherwise. New
X402CdpJwtFactory (libsodium) + config fields + unit tests. 92/92 X402 tests pass.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant