feat: enforce API key scopes#356
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (7)
🚧 Files skipped from review as they are similar to previous changes (3)
WalkthroughAdds ChangesAPI Key Scope Enforcement
Sequence Diagram(s)sequenceDiagram
participant Client
participant keyAuthorization
participant scopeAuthorization
participant RouteHandler
Client->>keyAuthorization: API request (method + path + API key)
keyAuthorization->>keyAuthorization: verify key, set apiKeyScopes and apiKeyType in context
keyAuthorization->>scopeAuthorization: next()
scopeAuthorization->>scopeAuthorization: isDraftPostRead(method, pathname, status)?
alt draft/all posts and non-private key
scopeAuthorization-->>Client: 403 JSON "Private key required for draft access"
else other route
scopeAuthorization->>scopeAuthorization: derive requiredScope via API_KEY_SCOPE_BY_RESOURCE
scopeAuthorization->>scopeAuthorization: hasScope(apiKeyScopes, requiredScope)?
alt scope missing
scopeAuthorization-->>Client: 403 JSON "Missing scope: {requiredScope}"
else scope present
scopeAuthorization->>RouteHandler: next()
RouteHandler-->>Client: 200 response
end
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
marble-jobs | 90f9533 | Commit Preview URL Branch Preview URL |
Jun 17 2026, 06:29 PM |
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
marble-api | 90f9533 | Commit Preview URL Branch Preview URL |
Jun 17 2026, 06:28 PM |
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
marble-mcp | 90f9533 | Commit Preview URL Branch Preview URL |
Jun 17 2026, 06:28 PM |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
packages/utils/src/constants/api-key.ts (1)
24-37: ⚡ Quick winDerive
API_KEY_SCOPESfrom read/write arrays to avoid drift.Keeping a third manual list duplicates source-of-truth and can desync from
API_KEY_READ_SCOPES/API_KEY_WRITE_SCOPES.Suggested refactor
-export const API_KEY_SCOPES = [ - "posts_read", - "posts_write", - "authors_read", - "authors_write", - "categories_read", - "categories_write", - "tags_read", - "tags_write", - "media_read", - "media_write", - "fields_read", - "fields_write", -] as const; +export const API_KEY_SCOPES = [ + ...API_KEY_READ_SCOPES, + ...API_KEY_WRITE_SCOPES, +] as const;🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/utils/src/constants/api-key.ts` around lines 24 - 37, The API_KEY_SCOPES constant is manually defined as a hardcoded array which duplicates data already existing in API_KEY_READ_SCOPES and API_KEY_WRITE_SCOPES arrays, risking drift between them. Instead of maintaining API_KEY_SCOPES as a separate manual list, derive it by combining the API_KEY_READ_SCOPES and API_KEY_WRITE_SCOPES arrays using the spread operator to ensure a single source of truth for all available scopes.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@packages/utils/src/constants/api-key.ts`:
- Around line 24-37: The API_KEY_SCOPES constant is manually defined as a
hardcoded array which duplicates data already existing in API_KEY_READ_SCOPES
and API_KEY_WRITE_SCOPES arrays, risking drift between them. Instead of
maintaining API_KEY_SCOPES as a separate manual list, derive it by combining the
API_KEY_READ_SCOPES and API_KEY_WRITE_SCOPES arrays using the spread operator to
ensure a single source of truth for all available scopes.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 12bfb9da-814d-42b3-aad9-861f8cf52090
📒 Files selected for processing (14)
apps/api/src/app.tsapps/api/src/lib/constants.tsapps/api/src/middleware/key-authorization.tsapps/api/src/middleware/scope-authorization.tsapps/api/src/routes/invalidate.tsapps/api/src/types/env.tsapps/cms/src/app/api/keys/[id]/route.tsapps/cms/src/app/api/keys/route.tsapps/cms/src/utils/keys.tspackages/db/prisma/migrations/20260617144443_add_field_api_key_scopes/migration.sqlpackages/db/prisma/schema.prismapackages/utils/package.jsonpackages/utils/src/constants/api-key.tspackages/utils/src/types/api-key.ts
💤 Files with no reviewable changes (1)
- apps/api/src/routes/invalidate.ts
3d4fdc0 to
a3eb89e
Compare
|
@coderabbitai review |
✅ Action performedReview finished.
|
…at/enforce-api-scopes # Conflicts: # packages/utils/src/constants/api-key.ts
Description
Enforces stored API key scopes across the v1 API routes. The API key middleware now exposes key scopes on the request context, and a new scope authorization middleware rejects requests that do not have the required read or write scope for the target resource.
This also centralizes API key scope constants in
@marble/utils, addsfields_readandfields_writefor upcoming custom fields API work, rejects write scopes on public API keys in CMS key management, and removes the undocumented/v1/cache/invalidateAPI-key route while keeping the system-authenticated/cache/invalidateroute.Motivation and Context
API key scopes were being collected and stored, but API requests were not enforcing them. This meant public/read-only keys could still reach write endpoints if they were otherwise valid.
The cache invalidation change keeps invalidation as a system-only path. Repo references call
/cache/invalidatewithX-System-Secret, and the live production OpenAPI spec does not advertise/v1/cache/invalidate.How to Test
pnpm lint.pnpm --filter @marble/db db:generate.403for write endpoints.403for restricted post reads such asGET /v1/posts?status=draftandGET /v1/posts?status=all.POST /cache/invalidaterequiresX-System-SecretandPOST /v1/cache/invalidatereturns404.Screenshots (if applicable)
N/A
Video Demo (if applicable)
N/A
Types of Changes
Summary by CodeRabbit
New Features
Improvements