@@ -77,10 +77,8 @@ export async function authenticateApiKeyFromHeader(
7777 }
7878 }
7979
80- const hashCandidate = await lookupByHash ( apiKeyHeader )
81- if ( hashCandidate !== null ) {
82- return await applyHashGates ( hashCandidate , options , workspaceSettings )
83- }
80+ const hashResult = await authenticateApiKeyByHash ( apiKeyHeader , options , workspaceSettings )
81+ if ( hashResult !== null ) return hashResult
8482
8583 // LEGACY FALLBACK — delete once `logger.warn('API key matched via fallback
8684 // decrypt loop', ...)` count stays at zero in prod. The block below is the
@@ -189,9 +187,18 @@ export async function authenticateApiKeyFromHeader(
189187 }
190188}
191189
192- /** Hash-only lookup — scope gates are applied separately so this can run in
193- * parallel with `getWorkspaceBillingSettings`. */
194- async function lookupByHash ( apiKeyHeader : string ) : Promise < HashCandidate | null > {
190+ /**
191+ * Fast path: look up a single row by `sha256(apiKeyHeader)` and apply the
192+ * scope / expiry / permission gates. Returns `null` when no row matched the
193+ * hash (caller should fall through to the legacy scan+decrypt loop). A hash
194+ * hit that fails a gate returns a concrete `INVALID` — the key definitely
195+ * belongs to that row, it's just not authorized in this scope.
196+ */
197+ async function authenticateApiKeyByHash (
198+ apiKeyHeader : string ,
199+ options : ApiKeyAuthOptions ,
200+ workspaceSettings : WorkspaceBillingSettings | null
201+ ) : Promise < ApiKeyAuthResult | null > {
195202 const keyHash = hashApiKey ( apiKeyHeader )
196203 const rows : HashCandidate [ ] = await db
197204 . select ( {
@@ -204,14 +211,9 @@ async function lookupByHash(apiKeyHeader: string): Promise<HashCandidate | null>
204211 . from ( apiKeyTable )
205212 . where ( eq ( apiKeyTable . keyHash , keyHash ) )
206213
207- return rows . length === 0 ? null : rows [ 0 ]
208- }
214+ if ( rows . length === 0 ) return null
209215
210- async function applyHashGates (
211- record : HashCandidate ,
212- options : ApiKeyAuthOptions ,
213- workspaceSettings : WorkspaceBillingSettings | null
214- ) : Promise < ApiKeyAuthResult > {
216+ const record = rows [ 0 ]
215217 const keyType = record . type as 'personal' | 'workspace'
216218
217219 if ( options . userId && record . userId !== options . userId ) return INVALID
0 commit comments