From ca6191af37a17b9de342dc0696532a31b397c9ba Mon Sep 17 00:00:00 2001 From: umaru Date: Fri, 21 Aug 2026 23:09:41 +0800 Subject: [PATCH] =?UTF-8?q?perf(auth):=20=E5=BB=B6=E9=95=BF=20API=20Key=20?= =?UTF-8?q?=E6=A0=A1=E9=AA=8C=E7=BC=93=E5=AD=98=20TTL?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lib/utils/auth.ts | 2 +- tests/unit/utils/auth.test.ts | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/lib/utils/auth.ts b/src/lib/utils/auth.ts index fd89926d..85d82e37 100644 --- a/src/lib/utils/auth.ts +++ b/src/lib/utils/auth.ts @@ -15,7 +15,7 @@ const API_KEY_VERIFY_CACHE_KEY_PROMISE = webcrypto.subtle.importKey( // The proxy still loads the active key row before calling verifyApiKey, so this // cache only removes repeated bcrypt work; revocation, expiry, ownership, and // authorization changes remain database-authoritative on every request. -const API_KEY_VERIFY_CACHE_TTL_MS = 10_000; +const API_KEY_VERIFY_CACHE_TTL_MS = 120_000; const API_KEY_VERIFY_CACHE_MAX_ENTRIES = 2_048; const apiKeyVerificationCache = new Map(); diff --git a/tests/unit/utils/auth.test.ts b/tests/unit/utils/auth.test.ts index b41816bb..8abd1bfd 100644 --- a/tests/unit/utils/auth.test.ts +++ b/tests/unit/utils/auth.test.ts @@ -1,4 +1,5 @@ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; +import bcryptjs from "bcryptjs"; import { hashApiKey, verifyApiKey, @@ -60,6 +61,29 @@ describe("auth utilities", () => { const otherHash = await hashApiKey(otherKey); expect(await verifyApiKey(key, otherHash)).toBe(false); }); + it("should expire cached verification after the two-minute TTL", async () => { + const key = "sk-auto-cache-expiring-key12345678901234567890"; + const hash = await hashApiKey(key); + const compareSpy = vi.spyOn(bcryptjs, "compare"); + const baseTime = Date.now(); + const dateNowSpy = vi.spyOn(Date, "now").mockReturnValue(baseTime); + + try { + expect(await verifyApiKey(key, hash)).toBe(true); + expect(compareSpy).toHaveBeenCalledTimes(1); + + dateNowSpy.mockReturnValue(baseTime + 119_999); + expect(await verifyApiKey(key, hash)).toBe(true); + expect(compareSpy).toHaveBeenCalledTimes(1); + + dateNowSpy.mockReturnValue(baseTime + 120_000); + expect(await verifyApiKey(key, hash)).toBe(true); + expect(compareSpy).toHaveBeenCalledTimes(2); + } finally { + dateNowSpy.mockRestore(); + compareSpy.mockRestore(); + } + }); it("should handle invalid hash gracefully", async () => { const key = "sk-auto-testkey123456789012345678901234";