diff --git a/packages/better-auth/src/plugins/username/index.ts b/packages/better-auth/src/plugins/username/index.ts index 9629e3760e..96c8a6a052 100644 --- a/packages/better-auth/src/plugins/username/index.ts +++ b/packages/better-auth/src/plugins/username/index.ts @@ -274,7 +274,14 @@ export const username = (options?: UsernameOptions) => { }, ], }); - if (user) { + + const blockChangeSignUp = ctx.path === "/sign-up/email" && user; + const blockChangeUpdateUser = + ctx.path === "/update-user" && + user && + ctx.context.session && + user.id !== ctx.context.session.session.userId; + if (blockChangeSignUp || blockChangeUpdateUser) { throw new APIError("UNPROCESSABLE_ENTITY", { message: ERROR_CODES.USERNAME_IS_ALREADY_TAKEN, }); diff --git a/packages/better-auth/src/plugins/username/username.test.ts b/packages/better-auth/src/plugins/username/username.test.ts index 127fc102e9..9d260837a7 100644 --- a/packages/better-auth/src/plugins/username/username.test.ts +++ b/packages/better-auth/src/plugins/username/username.test.ts @@ -4,7 +4,7 @@ import { username } from "."; import { usernameClient } from "./client"; describe("username", async (it) => { - const { client, sessionSetter } = await getTestInstance( + const { client, sessionSetter, signInWithTestUser } = await getTestInstance( { plugins: [ username({ @@ -70,7 +70,7 @@ describe("username", async (it) => { expect(session?.user.username).toBe("new_username_2.1"); }); - it("should fail on duplicate username", async () => { + it("should fail on duplicate username in sign-up", async () => { const res = await client.signUp.email({ email: "new-email-2@gamil.com", username: "New_username_2.1", @@ -80,6 +80,45 @@ describe("username", async (it) => { expect(res.error?.status).toBe(422); }); + it("should fail on duplicate username in update-user if user is different", async () => { + const newHeaders = new Headers(); + await client.signUp.email({ + email: "new-email-2@gamil.com", + username: "duplicate-username", + password: "new_password", + name: "new-name", + fetchOptions: { + headers: newHeaders, + }, + }); + + const { headers: testUserHeaders } = await signInWithTestUser(); + const res = await client.updateUser({ + username: "duplicate-username", + fetchOptions: { + headers: testUserHeaders, + }, + }); + expect(res.error?.status).toBe(422); + }); + + it("should succeed on duplicate username in update-user if user is the same", async () => { + await client.updateUser({ + username: "New_username_2.1", + fetchOptions: { + headers, + }, + }); + + const session = await client.getSession({ + fetchOptions: { + headers, + throw: true, + }, + }); + expect(session?.user.username).toBe("new_username_2.1"); + }); + it("should fail on invalid username", async () => { const res = await client.signUp.email({ email: "email-4@email.com",