diff --git a/.changeset/calm-tigers-smile.md b/.changeset/calm-tigers-smile.md new file mode 100644 index 0000000000..d98a464088 --- /dev/null +++ b/.changeset/calm-tigers-smile.md @@ -0,0 +1,5 @@ +--- +"better-auth": patch +--- + +Prevent Google One Tap from creating new users when sign-up is disabled for the Google provider. diff --git a/packages/better-auth/src/plugins/one-tap/index.ts b/packages/better-auth/src/plugins/one-tap/index.ts index d18b8bbb6f..8e526cc3f0 100644 --- a/packages/better-auth/src/plugins/one-tap/index.ts +++ b/packages/better-auth/src/plugins/one-tap/index.ts @@ -179,7 +179,8 @@ export const oneTap = (options?: OneTapOptions | undefined) => idToken, scope: "openid,profile,email", }, - disableSignUp: options?.disableSignup, + disableSignUp: + options?.disableSignup || googleProvider?.disableSignUp, }); if (result.error) { throw new APIError("UNAUTHORIZED", { diff --git a/packages/better-auth/src/plugins/one-tap/one-tap.test.ts b/packages/better-auth/src/plugins/one-tap/one-tap.test.ts index 94a0177a03..5a3ec62ac2 100644 --- a/packages/better-auth/src/plugins/one-tap/one-tap.test.ts +++ b/packages/better-auth/src/plugins/one-tap/one-tap.test.ts @@ -637,3 +637,78 @@ describe("one-tap hosted domain (hd)", async () => { expect(res.data?.token).toBeTruthy(); }); }); + +/** + * @see https://github.com/better-auth/better-auth/issues/10478 + */ +describe("one-tap disableSignUp", () => { + afterEach(() => { + Object.assign(verifiedPayload, defaultVerifiedPayload); + }); + + it("rejects provider-disabled sign-up without creating a user", async () => { + verifiedPayload.email = "one-tap-disable-signup@example.com"; + verifiedPayload.sub = "one-tap-disable-signup-sub"; + + const { auth } = await getTestInstance({ + socialProviders: { + google: { + clientId: "test-client", + clientSecret: "test-secret", + enabled: true, + disableSignUp: true, + }, + }, + plugins: [oneTap()], + }); + + await expect( + auth.api.oneTapCallback({ + body: { idToken: "stub-id-token" }, + }), + ).rejects.toMatchObject({ + statusCode: 401, + status: "UNAUTHORIZED", + body: { message: "signup disabled" }, + }); + + const ctx = await auth.$context; + const users = await ctx.adapter.findMany<{ email: string }>({ + model: "user", + where: [ + { + field: "email", + value: verifiedPayload.email, + }, + ], + }); + expect(users).toHaveLength(0); + }); + + it("keeps provider sign-up disabled when One Tap enables it", async () => { + verifiedPayload.email = "one-tap-signup-override@example.com"; + verifiedPayload.sub = "one-tap-signup-override-sub"; + + const { auth } = await getTestInstance({ + socialProviders: { + google: { + clientId: "test-client", + clientSecret: "test-secret", + enabled: true, + disableSignUp: true, + }, + }, + plugins: [oneTap({ disableSignup: false })], + }); + + await expect( + auth.api.oneTapCallback({ + body: { idToken: "stub-id-token" }, + }), + ).rejects.toMatchObject({ + statusCode: 401, + status: "UNAUTHORIZED", + body: { message: "signup disabled" }, + }); + }); +});