rotate: Vercel token + SendGrid API-key drivers (create→verify→revoke)

Two more provider-API rotation drivers for the vibe-coder credential surface,
both following the create-new → verify → revoke-old pattern with the
self-contained blob form <provider>://<host>/?id=&secret=.

- vercel: POST /v3/user/tokens mints a new bearer; Verify lists tokens and
  asserts the new id is present; RevokeOld DELETEs the old token by id.
- sendgrid: clones the old key's scopes (GET /v3/api_keys/<id>) onto the new
  key so rotation is a faithful replacement, not a privilege downgrade; Verify
  is a GET /v3/scopes auth proof; RevokeOld DELETEs the old key by id.

Both ship Bearer-enforcing httptest emulators that prove a real cutover (old
credential dead, new alive after RevokeOld) plus a leak check. Recorded as
MOCK-ONLY in proofs.go + docs/ROTATION-PROOFS.md (no self-host). go build/vet
clean; 77 rotate tests pass, -race clean.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
leetcrypt
2026-06-20 12:12:20 -07:00
parent 80916c817a
commit 88e603f3c0
6 changed files with 936 additions and 5 deletions
+14 -5
View File
@@ -69,6 +69,8 @@ stops a mock-only driver from ever being mistaken for a live one.
| `twilio` | `httptest` emulator (Basic-auth Keys resource) | no self-host |
| `flyio` | `httptest` GraphQL emulator | no self-host |
| `resend` | `httptest` emulator (Bearer `/api-keys` resource) | no self-host |
| `vercel` | `httptest` emulator (Bearer `/v3/user/tokens` resource) | no self-host |
| `sendgrid` | `httptest` emulator (Bearer `/v3/api_keys` resource, scope clone + `/v3/scopes` auth proof) | no self-host |
## What the MOCK-ONLY emulators actually prove
@@ -83,11 +85,18 @@ protocol so the test exercises the driver's true cutover logic:
on `PUT` **decrypts the sealed value** and stores the plaintext; the test asserts the
decrypted value equals the new value the driver put in the rebuilt blob — proving the
sealed-box encryption is correct, not simulated.
- **npm / twilio / resend** — each emulator enforces credential validity (a token/key
authenticates only while it exists) and the test asserts `Verify(old)` fails after
`RevokeOld` — proving the create→verify→revoke cutover really happened, old credential
dead, new one alive. (Resend additionally proves `Verify(new)` by listing keys with the
new Bearer token and asserting the new id is present — auth success *and* the key exists.)
- **npm / twilio / resend / vercel** — each emulator enforces credential validity (a
token/key authenticates only while it exists) and the test asserts the old credential is
dead after `RevokeOld` while the new one still authenticates — proving the
create→verify→revoke cutover really happened. (Resend and Vercel additionally prove
`Verify(new)` by listing keys/tokens with the new Bearer token and asserting the new id
is present — auth success *and* the credential exists.)
- **sendgrid** — the emulator enforces Bearer validity *and* records each key's scopes; the
driver clones the old key's scopes onto the new one (`GET /v3/api_keys/<id>` → reuse on
`POST /v3/api_keys`) so rotation is a faithful replacement, not a privilege downgrade. The
test asserts the minted key carries the seeded scopes, that `Verify(new)` (a `GET /v3/scopes`
auth proof) succeeds, and that the old key stops authenticating after `RevokeOld` while the
new key still does — old key dead, new one alive with the same scopes.
- **gcp** — the emulator **verifies the RS256 JWT signature** the driver mints against the
public key recorded for the key's `kid`; it only issues an access token for a JWT that
actually verifies, and `DELETE` removes the key — so the test proves real service-account