Skip to content

Latest commit

 

History

History
128 lines (86 loc) · 3.98 KB

File metadata and controls

128 lines (86 loc) · 3.98 KB

Gateway API Key Authentication

Gateway routes that proxy upstream APIs now use a dedicated API key authentication middleware.

Supported headers

The middleware accepts either of these request formats:

Authorization: Bearer <api_key>
X-Api-Key: <api_key>

X-Api-Key is read first. If it is absent, the middleware parses Authorization: Bearer <api_key>. A malformed Authorization header returns 401 only when X-Api-Key is not present.

Validation flow

For each gateway request, the middleware:

  1. Extracts the presented API key from X-Api-Key, or from Authorization: Bearer <api_key> when X-Api-Key is absent.
  2. Derives the key prefix from the first 16 characters.
  3. Looks up candidate key records by prefix.
  4. Verifies the full key using a timing-safe hash comparison.
  5. Rejects revoked keys with 403 Forbidden.
  6. Resolves and attaches:
    • req.user
    • req.vault
    • req.api
    • req.endpoint
    • req.apiKeyRecord
    • req.apiKeyValue

Rate limiting and balance checks remain separate middleware or route concerns and run after authentication.

Scope-based authorization

The middleware supports optional scope enforcement. When a requiredScope is configured on the middleware factory, the middleware checks that the presented API key includes that scope before allowing the request through.

Scope resolution rules:

  1. If the key record has no scopes array or it is empty, it defaults to ['read'] (backward compatibility for legacy keys).
  2. If the key's scopes include '*', all scopes are allowed (wildcard).
  3. Otherwise, the required scope must appear literally in the key's scopes array.

Route configuration example:

// Only allow keys with the 'write' scope
createGatewayApiKeyAuthMiddleware({
  requiredScope: 'write',
  // ... other options
});

Scopes at key creation:

The POST /apis/:apiId/keys endpoint accepts a scopes field in the request body (defaults to ['*']). The setter can restrict this to any combination of read, write, gateway, or any custom string.

Database

Migration 0007_api_key_scopes.sql ensures the scopes TEXT[] column exists on api_keys and backfills existing keys with '{read}'.

Failure responses

The middleware returns clear 401 responses for common auth failures:

  • Unauthorized: missing API key
  • Unauthorized: malformed Authorization header
  • Unauthorized: API key not found
  • Unauthorized: invalid API key
  • Unauthorized: API key does not grant access to this API

If the API key has been revoked, it returns 403 Forbidden with this message:

  • Unauthorized: API key has been revoked

If the API key lacks the required scope, it returns 403 Forbidden:

  • Forbidden: API key lacks required scope

If the target API cannot be resolved, it returns:

  • 404 Not Found: unknown API

Route usage

The middleware is applied to the upstream proxy routes in:

  • src/routes/gatewayRoutes.ts
  • src/routes/proxyRoutes.ts

The route handlers then consume the attached request context instead of re-validating headers inline.

Database notes

The database-backed middleware supports:

  • prefix lookup from api_keys.prefix
  • full-key hash verification against api_keys.key_hash
  • revoked-key enforcement from api_keys.revoked
  • eager loading of related users and vaults

To support revocation in environments that do not yet have the column, apply:

  • migrations/0005_add_api_key_revocation.sql

Prefix uniqueness guarantee

Migration 0006_api_key_prefix_unique.sql adds a partial unique index on api_keys (prefix) WHERE revoked = FALSE. This guarantees that the prefix-based lookup in step 3 of the validation flow always returns at most one active candidate, eliminating any ambiguity before the full hash comparison.

Revoked keys are excluded from the index so a prefix can be legitimately reused after a key is revoked (e.g. after rotation).

Constraint regression tests live in: src/repositories/apiKeyRepository.prefix.test.ts