Skip to Content
Deneva MCPComponentsApi Key Service

API key service

Source: src/security/api-key.service.ts

Three functions, all small. Together they cover the full lifecycle of a tenant’s bearer-style API key.

generateApiKey(): string

Returns a fresh 32-byte (256-bit) random key, base64url-encoded. The output is shown to the user once and never stored — only the hash goes into the database.

hashApiKey(rawKey: string): string

HMAC-SHA256 with API_KEY_HMAC_SECRET (loaded from the secrets store) over the raw key. Returns hex.

Why HMAC and not plain SHA-256? A database-only compromise must not yield usable keys. An attacker who steals the api_keys.key_hash column also needs API_KEY_HMAC_SECRET to brute-force candidate keys against the stored hashes. The HMAC secret lives in the secrets store, not in the database.

verifyApiKey(rawKey, storedHash): boolean

Constant-time comparison via crypto.timingSafeEqual. Length mismatch short-circuits to falsetimingSafeEqual throws on mismatched lengths, so we check first. The hash length is fixed (hex of SHA-256) and not secret, so this short-circuit doesn’t leak anything.

Where it’s used

  • tenant.middleware.ts — every authenticated request hashes the presented key and looks it up.
  • admin-routes.ts — rotation generates + hashes a new key.
  • scripts/seed-tenant.mjs — bootstrap script for the first key. The seed script does its own HMAC inline so it can stay zero-dependency on the build output.

Tests

tests/integration/api-key.test.ts covers:

  • key shape (43 base64url chars)
  • hash determinism
  • verify on match / mismatch / length mismatch (no throw)