feat(auth): implement closed-by-default API key auth, scoped service … - #482
feat(auth): implement closed-by-default API key auth, scoped service …#482Unclebaffa wants to merge 3 commits into
Conversation
b5971e4 to
8fe3cf3
Compare
…keys & edge rate limiting
8fe3cf3 to
b26377f
Compare
…surfacing, and timer flush - Item 1 (XFF spoofing): add TRUSTED_PROXY_COUNT env var; when set, pick ips[length - 1 - trustedProxyCount] instead of walking the private-filter list, eliminating forged public-hop injection attacks on known infra - Item 2 (silent write failure): replace outer catch swallow in writePersistedKeys with console.error so EROFS / permission errors surface in application logs (inner rename-fallback preserved for atomicity) - Item 3 (debounced timer drops counters): reduce default window 1000ms -> 200ms; add FLUSH_USAGE_SYNC=true escape hatch for synchronous flush on every authenticated request for accurate usage accounting - Item 4 (IPv6 casing + port suffixes): add normalizeIp() helper that lowercases and strips bracket+port ([::1]:80) and IPv4+port (1.2.3.4:443) before all private-range prefix checks; getClientIp normalises returned IPs - Tests: expand getClientIp suite with uppercase FE80::1, [::1]:80, 127.0.0.1:8080, and TRUSTED_PROXY_COUNT=2 cases (645/645 pass) - Docs: document TRUSTED_PROXY_COUNT and FLUSH_USAGE_SYNC in .env.local.example
Code Review ✅ Approved 13 resolved / 13 findingsImplements a closed-by-default API key authentication, scoped service keys, and edge rate limiting architecture, addressing 13 security and storage findings. All 640 unit tests and end-to-end suites passed successfully. ✅ 13 resolved✅ Bug: Scoped API keys stored only in per-runtime in-memory Map
✅ Security: hashKey fallback is a weak 64-bit non-cryptographic hash
✅ Bug: PATCH rotate on revoked key returns 500 instead of 400
✅ Quality: Documented X-RateLimit-* headers are not emitted
✅ Security: API key in query param is read but never stripped from URL
...and 8 more resolved from earlier reviews OptionsAuto-apply is off → Gitar will not commit updates to this branch. Comment with these commands to change the behavior for this request:
Was this helpful? React with 👍 / 👎 | Gitar |
|
|
@leocagli Please review |



🔒 feat(auth): Closed-by-Default API Key Authentication, Scoped Service Keys & Edge Rate Limiting
📋 Summary
This PR establishes an end-to-end, zero-trust security perimeter across Open-Stellar. It transitions the application from an open perimeter to a strict closed-by-default architecture, protecting all
/admin/*interfaces,/api/admin/*endpoints, and state-mutating API operations against unauthenticated access and brute-force abuse.The implementation features cryptographic CSPRNG key generation, SHA-256 zero-knowledge hashing, timing-safe equality checks, granular scope-based access control, sliding-window rate limiting across four tiers, and a dedicated retro-cyberpunk Key Management UI with one-time secret disclosure.
🎯 Key Objectives & Problem Statement
osk_live_abc12...).globalThis.crypto.subtle), avoiding Node-specific runtime modules in middleware.🏗 Architecture & Technical Specifications
1. Cryptographic Key Lifecycle (
lib/auth/api-keys.ts)osk_live_<48-hex-chars>(generated via CSPRNGglobalThis.crypto.getRandomValues).SHA-256(secret)is stored. Keys are looked up by computing the SHA-256 hash of incoming tokens and comparing digests usingtimingSafeEqualto prevent side-channel timing attacks.keyPrefix(e.g.osk_live_40e1b...) for administrative identification.revokedAttimestamps and cannot be revived. Rotation atomically revokes the previous key and provisions a fresh key.2. Edge-Compatible Middleware & Route Protection (
lib/auth/middleware.ts,middleware.ts)/admin/*routes require Admin privileges (ADMIN_API_KEYoradmin:*scope)./api/admin/*routes require Admin privileges.POST,PUT,PATCH,DELETE) on/api/agents/*,/api/webhooks/*,/api/quests/*, etc., require corresponding write scopes (agents:write,webhooks:write,quests:write).GET /api/prices,GET /api/openapi.json, static assets) remain accessible to anonymous callers subject to baseline tier rate limiting.Authorization: Bearer osk_live_...?apiKey=osk_live_...(for webhooks and embedded agent integrations).3. Tiered Sliding-Window Rate Limiting
Enforces
429 Too Many RequestswithRetry-After,X-RateLimit-Limit,X-RateLimit-Remaining, andX-RateLimit-Resetheaders.4. Admin Credentials Management UI (
app/admin/keys/page.tsx,components/admin/admin-console.tsx)/admin/keys.x402:quote,x402:settle,agents:read,agents:write,webhooks:*,quests:*,admin:*)./adminconsole are masked (osk_live_••••••••••••••••••••••••).📦 Files Changed
lib/auth/api-keys.tslib/auth/middleware.tsmiddleware.tsapp/api/admin/keys/route.tsPOST), listing (GET), and revocation (DELETE)app/admin/keys/page.tsxcomponents/admin/admin-console.tsxlib/admin-api-key.tse2e/admin-keys.spec.tsplaywright.config.ts__tests__/auth/api-keys.test.ts__tests__/auth/evidence.test.ts.env.local.example,README.md🖼 Visual Evidence (Mandatory)
Evidence 1: 401 Response on Admin Route Without Key
Unauthenticated request to
/adminreturns401 Unauthorized(Unauthorized: Admin API key required).Evidence 2: 200 Response on Admin Route With Valid Key
Same
/adminroute successfully loading with validADMIN_API_KEY(Bearer osk_...or query param).Evidence 3: Storage Showing SHA-256 Hash (Never Plaintext Secret)
Database/Store state demonstrating that keys are stored strictly as SHA-256 hashes and public listings only return truncated prefixes.
Evidence 4: Admin Key Management Dashboard (
/admin/keys)UI screenshot demonstrating key creation modal, scope selectors, and the one-time secret display banner.
🧪 Verification & Test Results
1. Playwright End-to-End Tests (
npx playwright test)e2e/admin-keys.spec.ts(API key creation, scope selection, one-time secret modal, copy & dismiss, key listing)e2e/admin-passport.spec.ts(ZK trust layer, proof generation, on-chain verification simulation)e2e/agent-wallet.spec.ts(Agent canvas selection and wallet modal workflow)e2e/onboarding-modal.spec.ts(First-visit tour navigation, multi-step panels, persistence)e2e/sidebar-tabs.spec.ts(Sidebar navigation, tab switching, localStorage persistence)2. Unit & Integration Tests (
npm test)__tests__/auth/api-keys.test.ts(15/15 security acceptance tests)__tests__/auth/evidence.test.ts(3/3 visual evidence verification tests)3. Static Analysis & Code Quality
npx tsc --noEmit): Clean (0 errors).npm run lint): Clean (0 errors).npx prettier --check): All files formatted according to repository code style.npm run build): Compiled successfully with static pages and Edge proxy middleware.🔒 Security Review Checklist
timingSafeEqual) used for hash verification.node:*modules imported in Edge middleware bundles./adminUI to resolve issue Admin console: display real ADMIN_API_KEY from server (not client-generated demo key) #225.Closes #39