Skip to content

fix: hash private API key before lookup to match stored HMAC value#155

Merged
Rieranthony merged 1 commit intocossistantcom:mainfrom
seemayr:fix/private-api-key-auth
Apr 8, 2026
Merged

fix: hash private API key before lookup to match stored HMAC value#155
Rieranthony merged 1 commit intocossistantcom:mainfrom
seemayr:fix/private-api-key-auth

Conversation

@seemayr
Copy link
Copy Markdown
Contributor

@seemayr seemayr commented Apr 8, 2026

Summary

  • Private API keys are HMAC-hashed with API_KEY_SECRET before storage in createApiKey(), but authenticateWithPrivateKey() was comparing the raw bearer token against the hashed DB value
  • This causes all private key authentication to fail with "Invalid API key"
  • Fix: hash the incoming private key before the DB/cache lookup, matching how it was stored

Root Cause

In apps/api/src/db/queries/api-keys.ts, createApiKey() (line ~209) hashes private keys:

storedKey = hashApiKey(rawKey, env.API_KEY_SECRET);

But in apps/api/src/lib/auth-validation.ts, authenticateWithPrivateKey() passed the raw key directly to the lookup, which would never match the hashed value in the database.

Change

apps/api/src/lib/auth-validation.ts — hash the private key before lookup:

const hashedKey = hashApiKey(privateKey, env.API_KEY_SECRET);
return await getApiKeyFromRedis(hashedKey, db);

Public key auth is unaffected (public keys are stored raw).

Test plan

  • Create a private API key via the dashboard (Settings > Developers)
  • Authenticate with Authorization: Bearer sk_... against /v1/websites
  • Verify public key auth still works unchanged
  • Consider adding an integration test for private key auth flow

🤖 Generated with Claude Code

Private keys are HMAC-hashed with API_KEY_SECRET before storage in
createApiKey(), but authenticateWithPrivateKey() was comparing the raw
key against the hashed DB value, causing all private key auth to fail.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 8, 2026

@seemayr is attempting to deploy a commit to the cossistant Team on Vercel.

A member of the Team first needs to authorize it.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Apr 8, 2026

Greptile Summary

This PR fixes a broken private API key authentication path: createApiKey() stores private keys as HMAC-SHA256 hashes, but authenticateWithPrivateKey() was passing the raw bearer token directly to the DB/cache lookup, causing every private key auth to fail with "Invalid API key". The fix hashes the incoming key before the lookup, making the two sides consistent.

The change is minimal, targeted, and correct — no other lookup paths are affected, and public key auth is unchanged.

Confidence Score: 5/5

Safe to merge — the fix is minimal, correct, and directly addresses a broken authentication path with no side effects.

The change is a single-function fix that aligns the lookup path with the storage path. All findings are P2 or lower; no correctness, data-integrity, or security regressions are introduced.

No files require special attention.

Vulnerabilities

No security concerns identified. The fix correctly ensures that raw private keys are never stored or compared in plaintext — only their HMAC-SHA256 digest (keyed with the required API_KEY_SECRET env var) is persisted and used for lookups.

Important Files Changed

Filename Overview
apps/api/src/lib/auth-validation.ts Adds hashApiKey + env imports and hashes the private key before the Redis/DB lookup in authenticateWithPrivateKey, matching the HMAC-SHA256 hash stored by createApiKey.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Client sends Bearer token] --> B{Valid sk_ format?}
    B -- No --> C[401 Invalid format]
    B -- Yes --> D[Compute HMAC digest of token]
    D --> E[Lookup digest in Redis cache]
    E --> F{Cache hit?}
    F -- Yes --> G[Return ApiKey record]
    F -- No --> H[Query DB by digest]
    H --> I{Row found?}
    I -- Yes --> J[Cache result, return ApiKey]
    I -- No --> K[Return null - 401 Invalid API key]
    G --> L[Auth success]
    J --> L
Loading

Reviews (1): Last reviewed commit: "fix: hash private API key before lookup ..." | Re-trigger Greptile

Copy link
Copy Markdown
Contributor

@Rieranthony Rieranthony left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perfect, thanks for the fix!

@Rieranthony Rieranthony merged commit a1bfd19 into cossistantcom:main Apr 8, 2026
0 of 3 checks passed
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.

2 participants