diff --git a/packages/better-auth/src/plugins/organization/team.test.ts b/packages/better-auth/src/plugins/organization/team.test.ts index 3d3df8242b..c45d9dd645 100644 --- a/packages/better-auth/src/plugins/organization/team.test.ts +++ b/packages/better-auth/src/plugins/organization/team.test.ts @@ -23,6 +23,15 @@ describe("team", async () => { logger: { level: "error", }, + databaseHooks: { + user: { + create: { + before: async (user) => ({ + data: { ...user, emailVerified: true }, + }), + }, + }, + }, }); const { headers } = await signInWithTestUser(); @@ -418,6 +427,15 @@ describe("team", async () => { logger: { level: "error", }, + databaseHooks: { + user: { + create: { + before: async (user) => ({ + data: { ...user, emailVerified: true }, + }), + }, + }, + }, }); const { headers } = await signInWithTestUser(); @@ -518,6 +536,15 @@ describe("setActiveTeam org scoping", async () => { logger: { level: "error", }, + databaseHooks: { + user: { + create: { + before: async (user) => ({ + data: { ...user, emailVerified: true }, + }), + }, + }, + }, }); const { headers } = await signInWithTestUser(); @@ -773,6 +800,15 @@ describe("multi team support", async () => { logger: { level: "error", }, + databaseHooks: { + user: { + create: { + before: async (user) => ({ + data: { ...user, emailVerified: true }, + }), + }, + }, + }, }, { testWith: "sqlite", @@ -1608,3 +1644,75 @@ describe("listUserTeams with organizationId parameter", async () => { expect(res).toBeInstanceOf(Error); }); }); + +describe("invitation with team id containing comma", async () => { + const { auth, signInWithTestUser } = await getTestInstance( + { + advanced: { + database: { + generateId: ({ model }) => { + if (model === "team") { + return `team,id,${crypto.randomUUID()}`; + } + return crypto.randomUUID(); + }, + }, + }, + plugins: [ + organization({ + async sendInvitationEmail() {}, + teams: { enabled: true }, + }), + ], + logger: { level: "error" }, + databaseHooks: { + user: { + create: { + before: async (user) => ({ + data: { ...user, emailVerified: true }, + }), + }, + }, + }, + }, + { testWith: "sqlite" }, + ); + + const admin = await signInWithTestUser(); + + const org = await auth.api.createOrganization({ + headers: admin.headers, + body: { name: "Comma Org", slug: "comma-org" }, + }); + if (!org) throw new Error("failed to create organization"); + const organizationId = org.id; + + const team = await auth.api.createTeam({ + headers: admin.headers, + body: { name: "Comma Team", organizationId }, + }); + + it("seeds a team whose generated id contains the storage separator", () => { + expect(team.id).toContain(","); + }); + + it.each([ + { name: "string body", teamId: team.id }, + { name: "array body", teamId: [team.id] }, + ])("rejects createInvitation with $name", async ({ teamId }) => { + await expect( + auth.api.createInvitation({ + headers: admin.headers, + body: { + email: "comma-invitee@email.com", + role: "member", + organizationId, + teamId, + }, + }), + ).rejects.toMatchObject({ + status: "BAD_REQUEST", + body: { code: "INVALID_TEAM_ID" }, + }); + }); +}); diff --git a/packages/better-auth/src/plugins/username/username.test.ts b/packages/better-auth/src/plugins/username/username.test.ts index 42923c869d..6babdbd32d 100644 --- a/packages/better-auth/src/plugins/username/username.test.ts +++ b/packages/better-auth/src/plugins/username/username.test.ts @@ -819,3 +819,52 @@ describe("immutable username", async () => { expect(session?.user.displayUsername).toBe("Updated Display Name"); }); }); + +describe("username sign-in verify-email callbackURL", async () => { + /** + * The verify-email link sent on username sign-in for an unverified user must + * keep the caller's `callbackURL` intact. A raw interpolation truncates any + * value containing `&` at the first ampersand. + * + * @see https://github.com/better-auth/better-auth/issues/6086 + */ + it("encodes callbackURL in the verify-email link on username sign-in", async () => { + let capturedUrl = ""; + const { client } = await getTestInstance( + { + emailAndPassword: { enabled: true, requireEmailVerification: true }, + emailVerification: { + sendOnSignIn: true, + async sendVerificationEmail({ url }) { + capturedUrl = url; + }, + }, + plugins: [username()], + }, + { + clientOptions: { + plugins: [usernameClient()], + }, + }, + ); + + await client.signUp.email({ + email: "encode-username@example.com", + username: "encode_username", + password: "correct-password", + name: "Encode Username", + }); + + const callbackURL = "/welcome?ref=username&plan=pro"; + await client.signIn.username({ + username: "encode_username", + password: "correct-password", + callbackURL, + }); + + expect(capturedUrl).not.toBe(""); + expect(new URL(capturedUrl).searchParams.get("callbackURL")).toBe( + callbackURL, + ); + }); +});