Skip to content

feat(api): platform API keys for headless webhook management (#376)#579

Open
ClintEastman02 wants to merge 5 commits into
aws-samples:mainfrom
ClintEastman02:feat/376-platform-api-keys
Open

feat(api): platform API keys for headless webhook management (#376)#579
ClintEastman02 wants to merge 5 commits into
aws-samples:mainfrom
ClintEastman02:feat/376-platform-api-keys

Conversation

@ClintEastman02

Copy link
Copy Markdown
Contributor

Summary

Closes #376.

Adds platform API keys as a first-class auth mechanism so CI/automation can create and manage webhooks without a Cognito user. Webhook invocation was already Cognito-free (HMAC), but webhook setup still required a Cognito JWT — this closes that gap for headless flows (GitHub Actions, workshops, service-to-service).

What changed

  • Unified authorizer — a REQUEST authorizer on /v1/webhooks* accepts either a Cognito JWT or a webhooks:manage API key. Existing Cognito flows are unchanged.
  • New /v1/api-keys endpoints (Cognito-gated) to mint / list / revoke keys.
  • Key formatbgak_<key_id>_<secret>. Only the secret hash is stored in DynamoDB (ApiKeyTable); lookup is a direct GetItem by key_id, so revocation has no GSI staleness window.
  • CLIbgagent api-key create|list|revoke, plus --api-key flag / BGAGENT_API_KEY env injection so headless callers skip bgagent login.
  • Docs — IDENTITY_AND_AUTH.md and SECURITY.md updated (+ synced Starlight mirrors).

Scope

Phase 1 only (webhook management via keys). Broader tasks:read/tasks:cancel scoping and replacing Cognito for interactive CLI are explicitly out of scope for v1, per the issue.

Testing

  • mise //cdk:test — 2261 pass (incl. new api-key-table, api-key-authorizer, create/list/delete-api-key, shared/api-key, gateway tests)
  • mise //cli:test — 575 pass (incl. api-key command + api-client tests)
  • mise //docs:sync — no drift
  • scripts/check-types-sync.ts — CDK ↔ CLI type sync OK

Notes for reviewers

  • Draft because upstream main has advanced ~40 commits since this branched; two files conflict on rebase and need resolution before this is review-ready:
    • cdk/package.json (dependency block)
    • cli/src/format.ts
    • Everything else auto-merges cleanly.
  • Governance: API: Allow webhook management with platform API keys (skip Cognito for automation) #376 does not yet carry the approved label and has no "Starting implementation" comment (ADR-003). Flagging so an admin can confirm approval before this leaves draft.

🤖 Generated with Claude Code

bgagent and others added 3 commits July 10, 2026 12:13
…ples#376)

Add long-lived, scoped platform API keys so CI/automation can manage
webhooks without a Cognito user. A unified REQUEST authorizer accepts
either a Cognito JWT or a `webhooks:manage` API key on /v1/webhooks*;
new Cognito-gated /v1/api-keys endpoints mint/list/revoke keys. Keys are
`bgak_<key_id>_<secret>` with only the secret hash stored in DynamoDB
(direct GetItem by key_id, no GSI staleness on revoke). CLI gains
`bgagent api-key` commands and `--api-key`/BGAGENT_API_KEY injection.
)

The new ApiKeyTable makes 19 DynamoDB tables in the agent stack.
…pi-keys

# Conflicts:
#	cdk/package.json
#	cli/src/format.ts
@codecov-commenter

codecov-commenter commented Jul 13, 2026

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 97.62511% with 28 lines in your changes missing coverage. Please review.
⚠️ Please upload report for BASE (main@22705e8). Learn more about missing BASE report.

Files with missing lines Patch % Lines
cli/src/api-client.ts 64.15% 19 Missing ⚠️
cli/src/commands/api-key.ts 96.33% 4 Missing ⚠️
cdk/src/handlers/delete-api-key.ts 96.51% 3 Missing ⚠️
cli/src/format.ts 96.22% 2 Missing ⚠️
❗ Your organization needs to install the Codecov GitHub app to enable full functionality.
Additional details and impacted files
@@           Coverage Diff           @@
##             main     #579   +/-   ##
=======================================
  Coverage        ?   89.52%           
=======================================
  Files           ?      231           
  Lines           ?    55517           
  Branches        ?     5025           
=======================================
  Hits            ?    49700           
  Misses          ?     5817           
  Partials        ?        0           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@ClintEastman02
ClintEastman02 marked this pull request as ready for review July 17, 2026 17:20
@ClintEastman02
ClintEastman02 requested review from a team as code owners July 17, 2026 17:20

@isadeks isadeks 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.

Review — Platform API keys for headless webhook management (#376)

Reviewed by reading + running the code at the merge head (9f2a5e9): the 7 new security suites pass (80 tests), and I verified the auth-critical paths empirically. The security core is sound — this is a careful design. Findings are minor + process, no blocking security defect. (Draft, so treating this as a pre-merge pass.)

Security core — checked, sound

  • Key hashing — a 256-bit crypto.randomBytes secret, stored as a plain SHA-256 hex digest. Correct for a high-entropy random token: there's nothing to brute-force, so bcrypt/argon aren't needed (and would only slow the hot auth path). Plaintext is returned once; only the hash is persisted (test asserts the secret never lands in the Put item).
  • Constant-time comparetimingSafeHashEqual length-guards then crypto.timingSafeEqual; both operands are fixed-length 64-hex, so the length branch isn't a timing signal. Good.
  • Custom authorizer doesn't downgrade the native one — it uses aws-jwt-verify with clientId + tokenUse: 'id', which validates signature/exp/iss/aud/token_use per request (the CLI sends id_token, so the token type matches). The native CognitoUserPoolsAuthorizer it replaces on /webhooks* had no scope checks to lose. The HMAC invoke route (/webhooks/tasks) is untouched (webhookAuthOptions), and requestValidator is body-only.
  • Revocation is immediate — strongly-consistent GetItem by key_id (no GSI window), and status !== 'active' → Deny. Ownership on delete returns 404 not 403 (no existence oracle), matching the webhook pattern.
  • parseApiKey 3-part split is safekey_id is a ULID (Crockford base32, no _) and the secret is hex, so bgak_<id>_<secret> always splits cleanly; malformed input denies without a lookup. X-API-Key preferred over Authorization; unexpected errors fail closed to Deny (tested).
  • TTL/expiry — active keys carry no ttl (expire only via expires_at); revoked keys get a 30d ttl and are denied by status before deletion, and by absence after. No auth gap in the window.
  • Verbose-log redactionkey + x-api-key added to SENSITIVE_FIELDS. Good catch by the author.

Minor

  • 1. list-api-keys default page can return short/empty results with a next_token (pagination UX)list-api-keys.ts:72 sends Limit and FilterExpression together. DynamoDB applies Limit to scanned items before the filter, so a user with revoked keys can get a page of <20 (or 0) active keys while has_more is true — a confusing "empty but paginated" result. Not a security or correctness bug (the filter is still applied; nothing leaks). Consider over-fetching then trimming to limit, or documenting that callers must follow next_token until it's null.
  • 2. key_hash is read then stripped, not projected outlist-api-keys queries with ProjectionType.ALL (GSI) and relies on toApiKeyDetail to drop key_hash/user_id. Correct today, but a future response-shape change could leak the hash. A ProjectionExpression/GSI projection excluding key_hash would be defense-in-depth. Low priority (the hash isn't the secret, but least-exposure is cheap here).

Process (author already flagged both — confirming)

  • 3. Governance gate not met. #376 has no approved label (labels: enhancement/orchestration/cli/security/P0) and no "Starting implementation" comment — the author flagged this per ADR-003. An admin should confirm approval before this leaves draft. This is a P0 security surface (a new long-lived credential type), so the governance step matters more than usual.
  • 4. Rebase. The head is now a merge commit (9f2a5e9) and GitHub shows MERGEABLE, so the cdk/package.json / cli/src/format.ts conflicts the description warns about appear resolved — worth confirming the description's "Draft because…" note is now stale and flipping to ready if an approval lands.

Design notes (non-blocking)

  • Scopes vocabulary ships all four values but only webhooks:manage is wired; tasks:read/tasks:cancel validate but gate nothing yet (documented as Phase 2). Fine — minting keys now against a stable vocabulary is the right call.
  • api-key create/list/revoke are Cognito-only (no --api-key flag), correctly — minting a credential shouldn't be possible with a credential. Consistent with the endpoints being Cognito-gated.

Recommendation: no blocking security issue — the crypto, authorizer, and revocation model are all correct and well-tested. Land the governance approval (#3) first; fold in #1 (or document the pagination contract) and optionally #2; confirm the rebase note is stale. Nicely scoped Phase 1.

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.

API: Allow webhook management with platform API keys (skip Cognito for automation)

3 participants