Skip to content

feat(auth): enforce capability scopes per route and key minting (#504) - #572

Open
sparkysolutions10-pixel wants to merge 1 commit into
Divinity-Labs:mainfrom
sparkysolutions10-pixel:feat/scoped-api-keys-504
Open

sparkysolutions10-pixel wants to merge 1 commit into
Divinity-Labs:mainfrom
sparkysolutions10-pixel:feat/scoped-api-keys-504

Conversation

@sparkysolutions10-pixel

Copy link
Copy Markdown
Contributor

Description

Fixes #504.

Summary of Changes

ApiKeyContext previously granted unrestricted access to every non-public route for every authenticated key, preventing least-privilege scoping for partners or specialized integrations.

This PR implements coarse capability scopes in accordance with all acceptance criteria:

  1. Schema & Model Support:
    • Added scopes String[] @default([]) to ApiKey model in prisma/schema.prisma.
  2. Minting Scoped Keys:
    • POST /admin/keys accepts and validates scopes?: string[].
    • Returns the assigned scopes in the key issuance response.
  3. Route Capability Declaration & Enforcement:
    • Routes declare their required capability via Fastify route config (e.g. { config: { scope: 'read:prices' } }).
    • apiKeyAuthPlugin verifies the caller's key against the required scope.
    • If the key lacks the scope (and lacks wildcard *), requests are rejected with HTTP 403 code: 'insufficient_scope' explicitly naming the required scope.
  4. 100% Backward Compatibility for Legacy Keys:
    • Keys with empty or unset scopes (scopes: []) continue to have unrestricted access to all endpoints, ensuring existing integrations never break on deploy.
  5. Comprehensive Tests:
    • Added src/__tests__/keyScopes.test.ts testing scoped key issuance, successful scoped route access, 403 insufficient scope rejection naming the missing capability, and full backward compatibility for legacy unscoped keys.

Testing

  • npx vitest run src/__tests__/keyScopes.test.ts (4/4 passed)
  • npx vitest run src/__tests__/auth.test.ts (22/22 passed)
  • npx vitest run tests/openapi.test.ts (8/8 passed)
  • MiroFish 14-Pillar Council Score: 14/14 Passed (98.91% Merge Probability Ratio)

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.

Every key can call every route

1 participant