Skip to content

fix(server): accept x-api-key on /v1/api-keys (close bootstrap footgun post-#34)#35

Merged
khaliqgant merged 1 commit intomainfrom
fix/api-keys-self-service-auth
Apr 24, 2026
Merged

fix(server): accept x-api-key on /v1/api-keys (close bootstrap footgun post-#34)#35
khaliqgant merged 1 commit intomainfrom
fix/api-keys-self-service-auth

Conversation

@kjgbot
Copy link
Copy Markdown
Contributor

@kjgbot kjgbot commented Apr 24, 2026

Why

Relayauth#34 purged HS256 but /v1/api-keys still only accepts Bearer tokens. Post-purge, that Bearer has to be RS256 — which means any operator rotating an api-key needs the prod RELAYAUTH_SIGNING_KEY_PEM exported locally before calling scripts/mint-api-key.sh. That's prod key material on laptops for a routine operation.

What

Mount apiKeyAuth() on /v1/api-keys{,/*} and swap the three handlers (POST /, GET /, POST /:id/revoke) from the bearer-only authenticateAndAuthorize(...) to authenticateAndAuthorizeFromContext(c, ...). That helper accepts either a Bearer token or a validated x-api-key (claims pre-populated by the middleware). Same pattern /v1/identities and /v1/tokens already use.

Bootstrap procedure

Before the purged server ships to prod:

  1. Use the pre-purge scripts/mint-api-key.sh (HS256 admin bearer) once to mint a long-lived operator key:
    SIGNING_KEY='<prod HS256>' ./scripts/mint-api-key.sh \
      --name relayauth-operator \
      --scopes-json '[\"*:*:*:*\"]' \
      --to-gh-secret AgentWorkforce/relayauth:RELAYAUTH_OPERATOR_API_KEY
    
    (*:*:*:* because validateSubset enforces parent-superset semantics; only the 4-part wildcard can mint keys with any downstream scope.)
  2. From then on, every rotation calls /v1/api-keys with x-api-key: $RELAYAUTH_OPERATOR_API_KEY. No more HS256, no prod RSA material needed.

What's not touched

  • validateSubset scope-escalation check is unchanged — caller's scopes must still be a superset of the new key's.
  • scripts/mint-api-key.sh / generate-dev-token.sh stay on RS256 (as chore(server): purge HS256 from relayauth (RS256-only) #34 left them). A follow-up can teach them to speak x-api-key + an operator key alongside the admin-bearer path if desired.

Tests

New: packages/server/src/__tests__/api-keys-apikey-auth.test.ts — mirrors identities-apikey-auth.test.ts, covers mint/list/revoke via x-api-key, insufficient-scope → 403, and revoked-key → 401. All 5 pass. Full server suite: 331 tests, 331 pass.

Rollback

Straight revert is safe — restores bearer-only behavior. Any operator keys minted in the meantime remain valid storage-side; they just lose their usable surface until re-applied.

…nt new ones

Pre-purge, /v1/api-keys was HS256-bearer only. After #34 (HS256 purge),
the admin-bearer path still exists but now requires a RS256-signed
token — meaning operators need the prod RELAYAUTH_SIGNING_KEY_PEM on
their laptops to rotate any api-key.

Fix: mount apiKeyAuth() on /v1/api-keys{,/*} so an existing api-key
scoped relayauth:api-key:manage:* (or * wildcard) can mint, list, and
revoke api-keys via x-api-key. This mirrors the pattern already used
for /v1/identities and /v1/tokens.

Bootstrap: mint one long-lived "operator" api-key once via the
existing HS256-bearer flow (before the purge ships to prod). From then
on every rotation runs through x-api-key — no prod key material on
laptops.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@khaliqgant khaliqgant merged commit 788534a into main Apr 24, 2026
2 checks passed
@khaliqgant khaliqgant deleted the fix/api-keys-self-service-auth branch April 24, 2026 10:18
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.

2 participants