Skip to content

task: enforce unique active api_key prefixes#371

Open
elijah4196 wants to merge 1 commit into
CalloraOrg:mainfrom
elijah4196:task/api-key-prefix-unique-index
Open

task: enforce unique active api_key prefixes#371
elijah4196 wants to merge 1 commit into
CalloraOrg:mainfrom
elijah4196:task/api-key-prefix-unique-index

Conversation

@elijah4196
Copy link
Copy Markdown

task: enforce unique active api_key prefixes

Summary

The gateway auth flow in src/middleware/gatewayApiKeyAuth.ts performs a
prefix-based lookup before a timing-safe full-key hash comparison. Without a
database-level guarantee, two active keys could share the same prefix, making
the lookup ambiguous and potentially allowing one key to shadow another.

This PR adds the missing constraint and its regression tests.


Changes

migrations/0006_api_key_prefix_unique.sql (new)

Adds a partial unique index on api_keys (prefix) WHERE revoked = FALSE.

CREATE UNIQUE INDEX IF NOT EXISTS uq_api_keys_prefix_active
  ON api_keys (prefix)
  WHERE revoked = FALSE;
  • Active keys are guaranteed to have unique prefixes at the database level.
  • Revoked keys are excluded so a prefix can be reused after revocation.

migrations/0006_api_key_prefix_unique.down.sql (new)

Rollback: DROP INDEX IF EXISTS uq_api_keys_prefix_active;

src/repositories/apiKeyRepository.prefix.test.ts (new)

Constraint regression tests using pg-mem (no external DB required):

Test Covers
Allows unique active prefix Happy path
Allows two active keys with different prefixes Happy path
Rejects duplicate active prefix Collision — acceptance criterion 1
Rejects duplicate prefix for different api_id Collision variant
Allows prefix reuse after revocation Revocation — acceptance criterion 2
Allows multiple revoked keys with same prefix Revocation variant
Does not block new active key when revoked key exists Revocation — acceptance criterion 3
Allows multiple NULL-prefix rows Documents NULL semantics
Prefix lookup returns exactly one row Mirrors gateway middleware query
Prefix lookup returns zero rows after revocation Mirrors gateway middleware query

tests/helpers/db.ts (updated)

Added prefix VARCHAR(16) column and the partial unique index to the shared
pg-mem schema used by integration tests, keeping it in sync with the real
PostgreSQL schema.

docs/gateway-api-key-auth.md (updated)

Added a Prefix uniqueness guarantee section documenting the new index and
pointing to the regression tests.

migrations/README.md (updated)

Added migration 0006 to the table.


Acceptance criteria

  • Inserting a duplicate active prefix fails at the database level
  • Revoked keys do not block reuse of a prefix
  • Tests cover collision and revocation cases

Testing

Tests run with Jest + pg-mem (no external PostgreSQL required):

npm test -- --testPathPattern="apiKeyRepository.prefix.test"

All 10 constraint regression tests pass.


closes #309

@drips-wave
Copy link
Copy Markdown

drips-wave Bot commented Jun 1, 2026

@elijah4196 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

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.

Add a unique partial index and constraint test for api_keys.prefix to guarantee prefix-lookup correctness

1 participant