Skip to content

Commit 9130c0d

Browse files
committed
fix(webapp): treat cascade-deleted PAT as auth miss, not stale success
1 parent 1235a6f commit 9130c0d

1 file changed

Lines changed: 14 additions & 15 deletions

File tree

apps/webapp/app/services/personalAccessToken.server.ts

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -205,23 +205,22 @@ export async function authenticatePersonalAccessToken(
205205
return;
206206
}
207207

208-
// Best-effort touch. The token can vanish between the findFirst above and
209-
// this update if a User cascade-delete happens concurrently (admin delete
210-
// flow), so swallow not-found errors rather than 500-ing the auth path.
211-
try {
212-
await prisma.personalAccessToken.update({
213-
where: {
214-
id: personalAccessToken.id,
215-
},
216-
data: {
217-
lastAccessedAt: new Date(),
218-
},
219-
});
220-
} catch (error) {
221-
logger.warn("Failed to touch PersonalAccessToken.lastAccessedAt", {
208+
// Touch lastAccessedAt with updateMany rather than update so a missing
209+
// row (e.g. the PAT was cascade-deleted by a concurrent User delete
210+
// between the findFirst above and this call) yields count = 0 instead
211+
// of throwing. count = 0 means the token no longer exists — treat that
212+
// as an authentication miss rather than handing a userId for a deleted
213+
// user back to callers that don't re-verify the user.
214+
const touchResult = await prisma.personalAccessToken.updateMany({
215+
where: { id: personalAccessToken.id },
216+
data: { lastAccessedAt: new Date() },
217+
});
218+
219+
if (touchResult.count === 0) {
220+
logger.warn("PersonalAccessToken vanished between findFirst and update", {
222221
personalAccessTokenId: personalAccessToken.id,
223-
error,
224222
});
223+
return;
225224
}
226225

227226
const decryptedToken = decryptPersonalAccessToken(personalAccessToken);

0 commit comments

Comments
 (0)