Repository navigation
feat(api-keys): read-only API keys and expiry at creation - #9855
Conversation
Personal and workspace API keys gain an access level (full access or read only) and an expiry picked when the key is created (30 days, 90 days, 1 year, or never; bounded server-side to one day through one year). A read-only key is granted the api:read scope: the operation funnel refuses it any operation api:read does not cover, including operations that declare no scope, with a 403 READ_ONLY_API_KEY refusal rather than an OAuth insufficient_scope challenge. Surfaces that authorize outside the funnel refuse it too: v1 refuses every non-GET except knowledge search, the legacy execute route refuses it, and a workflow MCP server refuses tools/call. An expired key now answers 401 "API key has expired" on v2, v1, and hybrid auth. /api/v2/meta reports the credential's scopes. Migration 0455 adds api_key.access (enum api_key_access, NOT NULL DEFAULT 'write'), so existing keys and the deployed app keep full access. Write keys serialize without the field and older serialized principals parse as write, so queued runs survive a mixed-version deploy.
…, share key principal builder - Create modal defaults Expires to Never; docs say so and note read-only keys keep search chat - Client key list defaults a missing access to write during deploy rollout - apiKeyPrincipal builds the key principal once for v1, hybrid, and workflow route auth - Auth result carries expired: true instead of callers matching the message text - Integration fixture uses valid table names; v2 create answers 201 - Serialized-principal test uses the wire shape; migration snapshot formatted
…docs - api-key-read-only flag (global, fallback API_KEY_READ_ONLY) gates creating read keys: the server answers 400 and the create modal hides Access while it is off. Enforcement of existing read keys is never gated, since older pods would serve them as full access - Reserve the mothership-delegation: key-name prefix so delegation minting can never adopt a user's key - Expiry is checked after every other key gate, so a foreign or banned key reports invalid - Workflow MCP serve and MCP discover answer an expired key with API key has expired - Share DAY_MS from the api-keys contract; the year bound's day of slack is for clock skew - Docs: per-surface refusal shapes, what read keys can still spend, and where the expiry message appears - Integration: flag-off and reserved-name refusals, v1 search asserts 200
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
[High impact] The PR appears safe to merge with the documented rollout restriction. SummaryAdds read-only API keys and optional expiration at creation.
No new actionable issues were found. The three previous unnumbered findings are addressed in the current code.
|
|
@cubic-dev-ai review this PR |
@waleedlatif1 I have started the AI code review. It will take a few minutes to complete. |
There was a problem hiding this comment.
All reported issues were addressed across 195 files
Reply with feedback, questions, or to request a fix.
Turn on auto-fix | Re-trigger cubic
…meta scopes - The account settings layout resolves api-key-read-only and wraps its sections in FeatureFlagsProvider, as the workspace and organization layouts do, so personal API key settings no longer throw from the create modal's flag read - The /api/v2/meta scopes description says read-only keys are also refused every operation that declares no API scope; OpenAPI regenerated
|
@cubic-dev-ai review this PR |
@waleedlatif1 I have started the AI code review. It will take a few minutes to complete. |
There was a problem hiding this comment.
No issues found across 202 files
Confidence score: 5/5
- Automated review surfaced no issues in the provided summaries.
- No files require special attention.
Note: This PR contains a large number of files. cubic selects up to 200 of the highest-priority eligible files for this review, so some files may not have been reviewed.
Turn on auto-fix | Re-trigger cubic
|
@cubic-dev-ai review this PR |
@waleedlatif1 I have started the AI code review. It will take a few minutes to complete. |
There was a problem hiding this comment.
No issues found across 202 files
Confidence score: 5/5
- Automated review surfaced no issues in the provided summaries.
- No files require special attention.
Note: This PR contains a large number of files. cubic selects up to 200 of the highest-priority eligible files for this review, so some files may not have been reviewed.
Turn on auto-fix | Re-trigger cubic
Renumber the personal-organization migrations 0455-0459 to 0456-0460 after staging's 0455_api_key_access, rechaining their snapshots and journal. Port read-only API keys and creation-time expiry (#9855) into the Platform: the create-key modal offers access (behind api-key-read-only) and expiry, the deploy dialog and MCP server detail issue full-access keys only, and the keys list shows a Read only tag and expiry. The flag is seeded through the organization surface. Legacy layouts and settings modified on staging stay deleted.
Summary
api_key.access(read|write, defaultwrite, migration 0455, metadata-only) carried on personal and workspace key principalsapi:readin the operation funnel, the same classification OAuth tokens already use, and is refused with 403READ_ONLY_API_KEYon any write or unscoped operation. Surfaces that authorize outside the funnel refuse explicitly: v1 non-GET (except knowledge search), legacy workflow execute, and workflow MCPtools/call401 API key has expiredinstead of a generic invalid-key error, checked after validity so foreign or banned keys still read as invalid/api/v2/metareports the credential's scopes; themothership-delegation:key-name prefix is reservedapi-key-read-onlyfeature flag (off by default); enforcement of read keys is never gatedRollout: turn
api-key-read-onlyon only after every pod runs this release, since older pods ignoreaccessand would serve a read key with full access. Rolling back below this release after read keys exist widens them the same way.Type of Change
Testing
apps/sim/lib/api-key/__integration__/read-only-access.integration.ts(8 passing) against PostgreSQL: read keys refused on every write surface (v2, Sim MCP, v1, legacy execute, workflow MCP serve, resume/cancel) with no rows written, reads still succeed, pre-migration keys behave as write, expiry message on v2 and v1, flag-off creation and reserved names refused; reverting each guard turns its test redtscon apps/sim,check:test-types;check:migrations,schema-mock,openapi,cli-api,mcp-operations,cli-docs,api-validation:strict,principal-kind-parity,client-boundary,boundaries; root scripts and sim-cli suites; biomeChecklist
test-auditauthoring gate)