logicspike/docs

Authentication & Team

API Key Management — Technical Specification

Last Updated: 2026-02-28
Status: ✅ Fully Implemented (CRUD + Gateway Validation)


Overview

LogicSpike provides a full API key management system for machine-to-machine integrations and public SDK usage. Keys are workspace-scoped, SHA-256 hashed, and carry granular per-service permissions.


Architecture

Layer Responsibility Files
Manager CRUD routes for key lifecycle apps/manager/src/routes/api-keys.ts, apps/manager/src/auth/api-key.ts
Gateway Validates API keys on public routes apps/gateway/src/middleware/api-key.middleware.ts
Dashboard UI for key management apps/seller-dashboard/src/hooks/use-api-keys.ts, apps/seller-dashboard/src/app/dashboard/settings/api-keys/
Database api_keys table in core-database packages/core-database/src/schema.ts

Key Types

Type Prefix Usage Restrictions
Secret ls_ Server-to-server integrations Full read/write per permissions
Publishable pk_ Browser SDKs, public clients Read-only (GET/HEAD/OPTIONS), domain-locked

Domain Locking (Publishable Keys)

Publishable keys can be restricted to specific domains via allowedDomains. When set:

  • Browser requests must include an Origin header matching one of the allowed domains
  • Requests without a matching origin are rejected with 403 Domain not allowed
  • Empty allowedDomains = unrestricted (useful during development)

Key Generation & Storage

  1. Generation: 32-byte cryptographically random key via crypto.getRandomValues()
  2. Hashing: SHA-256 via Web Crypto API (crypto.subtle.digest)
  3. Storage: Only the hash is stored in DB; raw key returned once at creation
  4. Lookup: Gateway hashes incoming key and queries DB by key_hash
Raw Key:  ls_a1b2c3d4e5f6...  (68 chars)
Hash:     e3b0c44298fc1c14...  (64 hex chars)

Permission Model

Permissions are stored as a flat array of granular PBAC strings:

[
  "blog:posts.read",
  "blog:posts.write",
  "media:files.read"
]

The gateway middleware (api-key.middleware.ts) checks:

  1. Extract the service from the route (e.g., apiKeyMiddleware("blog")).
  2. Verify the key has at least one permission starting with ${service}:.
  3. If the request modifies data (POST/PUT/DELETE), verify the key has at least one permission that does not end with .read or :read.
  4. Downstream microservices enforce exact string checks (e.g., requirePermission("blog:posts.write")).

API Endpoints

Base URL: /manager/api-keys (proxied through Gateway, requires JWT auth)

GET /

List all API keys for the current workspace.

  • Auth: Bearer JWT
  • Response: Array of keys (without raw key or hash)

POST /

Generate a new API key.

  • Auth: Bearer JWT
  • Body:
    {
      "name": "Production Blog SDK",
      "description": "Read-only access for blog widget",
      "permissions": ["blog:posts.read"],
      "expiresIn": "90d",
      "type": "publishable",
      "allowedDomains": ["https://mysite.com"]
    }
  • Response: Key object including raw key field (shown only once)

PATCH /:id

Update a key's name or description.

  • Auth: Bearer JWT
  • Body: { "name": "New Name", "description": "..." }

DELETE /:id

Revoke a key (soft delete — sets status: "revoked").

  • Auth: Bearer JWT
  • Response: { "success": true }

Expiration Options

Value Duration
30d 30 days
90d 90 days
1y 1 year
never No expiration

The gateway checks expiration on every request. Expired keys return 403 API key expired.


Gateway Validation Flow

1. Extract key from x-api-key header or Authorization: Bearer
2. SHA-256 hash the key
3. DB lookup by key_hash
4. Check: status === "active"?
5. Check: not expired?
6. If publishable:
   a. Reject non-GET methods (read-only)
   b. Validate Origin against allowedDomains
7. Permission check: validates key has matching PBAC prefix strings for the target service and HTTP method.
8. Set context: { tenant_id, user_id: "system" }
9. Forward request to downstream service

Database Schema

See schema_specification.md — Table #22 api_keys.


Known Gaps & TODOs

Issue Priority Detail
No role guard on CRUD 🟠 High Any team member can create/revoke keys — should be Owner/Admin only
Hash function duplicated 🟡 Medium Same SHA-256 logic exists in both manager and gateway — extract to @repo/core-auth
No rate limiting on validation 🟡 Medium Brute-force key guessing possible without rate limiting
x-api-key not in CORS headers 🟡 Medium Browser SDK clients can only use Authorization: Bearer, not x-api-key
Limit enforcement (Phase 7) ⏳ Future POST /api-keys should check platform.api_keys limit
Audit logging ⏳ Future Key create/revoke events should be logged per audit_log_spec.md
Authentication & Team