Coinbase CDP authentication — a signed Ed25519 JWT, built here rather than in the shared HTTP client.
This lived in Core.HttpClient as a :coinbase_cdp_jwt branch of a shared
build_auth_headers/5. It is a venue fact, and a venue fact in shared code is a
second place that can be wrong about the venue. Core keeps the generic schemes and
takes a function for anything else; this is that function.
Why it is public, and the incident behind it
The WebSocket side needs the same token. It once had its own stub that returned the
raw API key instead of a signed JWT, and Coinbase answers that with
{"type":"error","message":"authentication failure"} — which is how the level2
channel produced nothing while ticker, which is public and needs no auth, worked
fine. A venue half-delivering looks like a quiet market, not a broken credential.
A working implementation living one module away from a stub doing the same job is the kind of duplication that only ever shows up as a runtime failure. One implementation, used by both paths.
Summary
Functions
Builds a signed CDP JWT.
Builds authentication headers for a REST call, as Core.HttpClient's auth hook.
Functions
Builds a signed CDP JWT.
:uris
Scopes the token to specific REST calls, per Coinbase's documentation. The WebSocket token omits it — a socket is not one request — so the claim is included only when given rather than defaulted to something plausible. Defaulting it would produce a token that looks right and is rejected.
The two-minute expiry is deliberate
Short by design, and not cached: the streaming side rebuilds one per subscribe. A token that outlives its window fails in exactly the silent way the incident above was about — the connection is up, the subscribe is accepted, and no data arrives.
Builds authentication headers for a REST call, as Core.HttpClient's auth hook.
Pass it where an auth type would go:
HttpClient.build_auth_headers(:get, path, body, credentials, &Auth.rest_headers/4)On a signing failure it returns the content-type header alone rather than an
unsigned Authorization. An unsigned token is worse than none: it looks like a
credential problem at the venue rather than at us, and sends the reader looking in the
wrong place. The caller decides whether an unauthenticated request is acceptable.