fix(admin): return USER_NOT_FOUND for missing users before update (#9875)

Co-authored-by: Maxwell <145994855+ping-maxwell@users.noreply.github.com>
This commit is contained in:
Wilson Angelie Tan
2026-06-03 23:07:47 +07:00
committed by GitHub
parent ad60333d15
commit 1012b69046
3 changed files with 84 additions and 0 deletions

View File

@@ -0,0 +1,5 @@
---
"better-auth": patch
---
The admin plugin's `unbanUser`, `setRole` and `adminUpdateUser` endpoints used to call `internalAdapter.updateUser` without checking that the target user existed, so when the caller passed an unknown id the underlying database error (for example Prisma's `P2025`) bubbled up as a generic HTTP 500. those endpoints now mirror the existing guard in `banUser`: look the user up via `findUserById`, and throw a clean `NOT_FOUND` (`USER_NOT_FOUND`) when no row is returned. Closes #9800.

View File

@@ -719,6 +719,62 @@ describe("Admin plugin", async () => {
expect(res.data?.sessions.length).toBe(1);
});
it("should return 404 from unbanUser when the target user does not exist", async () => {
const res = await client.admin.unbanUser(
{
userId: "nonexistent-user-id",
},
{
headers: adminHeaders,
},
);
expect(res.error?.status).toBe(404);
expect(res.error?.code).toBe("USER_NOT_FOUND");
});
it("should return 404 from banUser when the target user does not exist", async () => {
const res = await client.admin.banUser(
{
userId: "nonexistent-user-id",
},
{
headers: adminHeaders,
},
);
expect(res.error?.status).toBe(404);
expect(res.error?.code).toBe("USER_NOT_FOUND");
});
it("should return 404 from setRole when the target user does not exist", async () => {
const res = await client.admin.setRole(
{
userId: "nonexistent-user-id",
role: "admin",
},
{
headers: adminHeaders,
},
);
expect(res.error?.status).toBe(404);
expect(res.error?.code).toBe("USER_NOT_FOUND");
});
it("should return 404 from admin.updateUser when the target user does not exist", async () => {
const res = await client.admin.updateUser(
{
userId: "nonexistent-user-id",
data: {
name: "John Doe",
},
},
{
headers: adminHeaders,
},
);
expect(res.error?.status).toBe(404);
expect(res.error?.code).toBe("USER_NOT_FOUND");
});
it("should not allow non-admin to list user sessions", async () => {
const res = await client.admin.listUserSessions(
{

View File

@@ -153,6 +153,14 @@ export const setRole = <O extends AdminOptions>(opts: O) =>
}
}
}
const isUserExist = await ctx.context.internalAdapter.findUserById(
ctx.body.userId,
);
if (!isUserExist) {
throw APIError.from("NOT_FOUND", BASE_ERROR_CODES.USER_NOT_FOUND);
}
const updatedUser = await ctx.context.internalAdapter.updateUser(
ctx.body.userId,
{
@@ -507,6 +515,14 @@ export const adminUpdateUser = (opts: AdminOptions) =>
inputRoles as string[],
);
}
const isUserExist = await ctx.context.internalAdapter.findUserById(
ctx.body.userId,
);
if (!isUserExist) {
throw APIError.from("NOT_FOUND", BASE_ERROR_CODES.USER_NOT_FOUND);
}
const updatedUser = await ctx.context.internalAdapter.updateUser(
ctx.body.userId,
ctx.body.data,
@@ -851,6 +867,13 @@ export const unbanUser = (opts: AdminOptions) =>
);
}
const isUserExist = await ctx.context.internalAdapter.findUserById(
ctx.body.userId,
);
if (!isUserExist) {
throw APIError.from("NOT_FOUND", BASE_ERROR_CODES.USER_NOT_FOUND);
}
const user = await ctx.context.internalAdapter.updateUser(
ctx.body.userId,
{