From 08a6c8edaf1b4317d8dfa86943a0f8efe5594d83 Mon Sep 17 00:00:00 2001 From: Dotta Date: Wed, 22 Jul 2026 18:48:52 +0000 Subject: [PATCH] fix: isolate agent key usage write failures Co-Authored-By: Paperclip --- .../__tests__/agent-auth-middleware.test.ts | 33 +++++++++++++++++++ server/src/middleware/auth.ts | 27 +++++++++------ 2 files changed, 50 insertions(+), 10 deletions(-) diff --git a/server/src/__tests__/agent-auth-middleware.test.ts b/server/src/__tests__/agent-auth-middleware.test.ts index 33359c458fc..68eebee7c95 100644 --- a/server/src/__tests__/agent-auth-middleware.test.ts +++ b/server/src/__tests__/agent-auth-middleware.test.ts @@ -384,6 +384,39 @@ describe("agent auth middleware", () => { ]); }); + it("keeps agent-key authentication available when the last-used update fails", async () => { + const companyId = randomUUID(); + const agentId = randomUUID(); + const token = "pcp_test_agent_key_update_failure"; + const { db } = createDbState({ + agent: { id: agentId, companyId }, + agentKey: { + id: randomUUID(), + agentId, + companyId, + keyHash: hashToken(token), + responsibleUserId: "user-key", + }, + }); + db.update = () => ({ + set: () => ({ + where: () => Promise.reject(new Error("database unavailable")), + }), + }); + + const res = await request(createApp(db)) + .get("/actor") + .set("Authorization", `Bearer ${token}`); + + expect(res.status).toBe(200); + expect(res.body).toMatchObject({ + type: "agent", + agentId, + companyId, + source: "agent_key", + }); + }); + it("rejects agent keys that lack a responsible user binding and audits the denial", async () => { const companyId = randomUUID(); const agentId = randomUUID(); diff --git a/server/src/middleware/auth.ts b/server/src/middleware/auth.ts index b27296b042b..4fa863dc297 100644 --- a/server/src/middleware/auth.ts +++ b/server/src/middleware/auth.ts @@ -363,16 +363,23 @@ export function actorMiddleware(db: Db, opts: ActorMiddlewareOptions): RequestHa }); const authenticatedAt = new Date(); if (shouldUpdateAgentApiKeyLastUsed(key.lastUsedAt, authenticatedAt)) { - await db - .update(agentApiKeys) - .set({ lastUsedAt: authenticatedAt }) - .where(and( - eq(agentApiKeys.id, key.id), - or( - isNull(agentApiKeys.lastUsedAt), - lt(agentApiKeys.lastUsedAt, new Date(authenticatedAt.getTime() - AGENT_API_KEY_LAST_USED_THROTTLE_MS)), - ), - )); + try { + await db + .update(agentApiKeys) + .set({ lastUsedAt: authenticatedAt }) + .where(and( + eq(agentApiKeys.id, key.id), + or( + isNull(agentApiKeys.lastUsedAt), + lt(agentApiKeys.lastUsedAt, new Date(authenticatedAt.getTime() - AGENT_API_KEY_LAST_USED_THROTTLE_MS)), + ), + )); + } catch (err) { + logger.warn( + { err, companyId: key.companyId, agentId: key.agentId, keyId: key.id }, + "Failed to update agent API key last-used timestamp", + ); + } } req.actor = {