fix(auth): honor social disableSignUp for ID token sign-in (#11491)

This commit is contained in:
Taesu
2026-09-30 19:19:55 +00:00
committed by GitHub
parent 35d7cd3892
commit 55cb92e940
3 changed files with 132 additions and 0 deletions
@@ -0,0 +1,5 @@
---
"better-auth": patch
---
Respect social provider `disableSignUp` when signing in with an ID token.
@@ -342,6 +342,7 @@ export const signInSocial = <O extends BetterAuthOptions>() =>
callbackURL: c.body.callbackURL,
disableSignUp:
(provider.disableImplicitSignUp && !c.body.requestSignUp) ||
provider.options?.disableSignUp ||
provider.disableSignUp,
source: {
method: "oauth",
+126
View File
@@ -696,6 +696,132 @@ describe("Disable implicit signup", async () => {
});
describe("Disable signup", async () => {
/**
* @see https://github.com/better-auth/better-auth/issues/11480
*/
describe("ID-token sign-in", () => {
const googleIdTokenOptions = (email: string) => {
const profile: GoogleProfile = {
aud: "test",
azp: "test",
email,
email_verified: true,
exp: 2_000_000_000,
family_name: "User",
given_name: "Google",
iat: 1_900_000_000,
iss: "https://accounts.google.com",
name: "Google User",
picture: "https://example.com/avatar.png",
sub: "google-id-token-user",
};
return {
clientId: "test",
clientSecret: "test",
verifyIdToken: async () => true,
getUserInfo: async () => ({
user: { email, name: profile.name, emailVerified: true },
data: profile,
}),
};
};
it.each([
{ requestSignUp: undefined, scenario: "without requestSignUp" },
{ requestSignUp: true, scenario: "with requestSignUp" },
])("rejects ID-token sign-in for a new user $scenario", async ({
requestSignUp,
}) => {
const email = "new-id-token-user@example.com";
const { client, auth } = await getTestInstance(
{
socialProviders: {
google: {
...googleIdTokenOptions(email),
disableSignUp: true,
},
},
},
{ disableTestUser: true },
);
const response = await client.signIn.social({
provider: "google",
idToken: { token: "a.b.c" },
requestSignUp,
});
const user = await (await auth.$context).adapter.findOne({
model: "user",
where: [{ field: "email", value: email }],
});
expect(user).toBeNull();
expect(response.error).toMatchObject({
status: 401,
code: "OAUTH_LINK_ERROR",
message: "signup disabled",
});
});
it("allows an existing ID-token user to sign in when provider sign-up is disabled", async () => {
const email = "existing-id-token-user@example.com";
const { client, auth } = await getTestInstance(
{ socialProviders: { google: googleIdTokenOptions(email) } },
{ disableTestUser: true },
);
const signIn = () =>
client.signIn.social({
provider: "google",
idToken: { token: "a.b.c" },
});
const firstResponse = await signIn();
expect(firstResponse.error).toBeNull();
const provider = (await auth.$context).socialProviders.find(
(provider) => provider.id === "google",
);
expect(provider).toBeDefined();
provider!.options!.disableSignUp = true;
const response = await signIn();
expect(response.error).toBeNull();
expect(response.data).toMatchObject({
redirect: false,
user: { email },
});
});
it("keeps the top-level provider sign-up restriction for ID-token sign-in", async () => {
const { client, auth } = await getTestInstance(
{
socialProviders: {
google: {
...googleIdTokenOptions("top-level-id-token-user@example.com"),
disableSignUp: false,
},
},
},
{ disableTestUser: true },
);
const provider = (await auth.$context).socialProviders.find(
(provider) => provider.id === "google",
);
expect(provider).toBeDefined();
provider!.disableSignUp = true;
const response = await client.signIn.social({
provider: "google",
idToken: { token: "a.b.c" },
});
expect(response.error).toMatchObject({
status: 401,
code: "OAUTH_LINK_ERROR",
message: "signup disabled",
});
});
});
it("Should not create user when sign up is disabled", async () => {
const headers = new Headers();
const { client, cookieSetter } = await getTestInstance({