[GH-ISSUE #1142] Ability to disable email signups completely #8614

Closed
opened 2026-04-13 03:44:21 -05:00 by GiteaMirror · 13 comments
Owner

Originally created by @madebyfabian on GitHub (Jan 5, 2025).
Original GitHub issue: https://github.com/better-auth/better-auth/issues/1142

Is this suited for github?

  • Yes, this is suited for github

I wanted to disable email signups for the client-side, but keep email & password logins enabled and did not find a solution for it.

Describe the solution you'd like

Some config like:

export const auth = betterAuth({
  emailAndPassword: {
    enabled: true,
    signupEnabled: false,
  },

  // ...
});

Describe alternatives you've considered

I added

requireEmailVerification: true,
autoSignIn: false,

to the config to make sure that even if someone uses the api endpoint to signup, they will never be able to login because I do not send these emails for now.

Additional context

No response

Originally created by @madebyfabian on GitHub (Jan 5, 2025). Original GitHub issue: https://github.com/better-auth/better-auth/issues/1142 ### Is this suited for github? - [X] Yes, this is suited for github ### Is your feature request related to a problem? Please describe. I wanted to disable email signups for the client-side, but keep email & password logins enabled and did not find a solution for it. ### Describe the solution you'd like Some config like: ```ts export const auth = betterAuth({ emailAndPassword: { enabled: true, signupEnabled: false, }, // ... }); ``` ### Describe alternatives you've considered I added ```ts requireEmailVerification: true, autoSignIn: false, ``` to the config to make sure that even if someone uses the api endpoint to signup, they will never be able to login because I do not send these emails for now. ### Additional context _No response_
GiteaMirror added the locked label 2026-04-13 03:44:21 -05:00
Author
Owner

@JosipPardon commented on GitHub (Jan 5, 2025):

So, essentially you want to prevent new people from creating accounts, but let existing users sign in?

I suggest you to comment part of code with authClient.signUp.email (but leave authClient.signIn.email) and throw error in sendVerificationEmail.

<!-- gh-comment-id:2571673491 --> @JosipPardon commented on GitHub (Jan 5, 2025): So, essentially you want to prevent new people from creating accounts, but let existing users sign in? I suggest you to comment part of code with `authClient.signUp.email` (but leave `authClient.signIn.email`) and throw error in `sendVerificationEmail`.
Author
Owner

@Nicolab commented on GitHub (Jan 6, 2025):

Also, "before" hook maybe good for that https://www.better-auth.com/docs/concepts/hooks#before-hooks

With APIError raised on closed endpoints (signup, etc)

<!-- gh-comment-id:2572741988 --> @Nicolab commented on GitHub (Jan 6, 2025): Also, "before" hook maybe good for that https://www.better-auth.com/docs/concepts/hooks#before-hooks With APIError raised on closed endpoints (signup, etc)
Author
Owner

@igomonteiro commented on GitHub (Jan 15, 2025):

I added this code to my betterAuth config to disable the sign-up endpoint:


hooks: {
    before: createAuthMiddleware(async (ctx) => {
      if (ctx.path.startsWith("/sign-up")) {
        throw new APIError("BAD_REQUEST", {
          message: "Endpoint not allowed",
        });
      }
    }),
  },
  
<!-- gh-comment-id:2591533890 --> @igomonteiro commented on GitHub (Jan 15, 2025): I added this code to my betterAuth config to disable the sign-up endpoint: ``` hooks: { before: createAuthMiddleware(async (ctx) => { if (ctx.path.startsWith("/sign-up")) { throw new APIError("BAD_REQUEST", { message: "Endpoint not allowed", }); } }), }, ```
Author
Owner

@hco commented on GitHub (Jan 27, 2025):

I also have a similar requirement, but I want to keep the ability to create (or "invite") new users from the backend.
With the suggested hook, that's not possible anymore without fiddeling in the database, right?

Oh, and one more remark: I kind of did not expect an open signup endpoint enabled "by default". While it kind of makes sense, it kind of ruined the "roll your own auth with confidence" moment for me.

<!-- gh-comment-id:2617055211 --> @hco commented on GitHub (Jan 27, 2025): I also have a similar requirement, but I want to keep the ability to create (or "invite") new users from the backend. With the suggested hook, that's not possible anymore without fiddeling in the database, right? Oh, and one more remark: I kind of did not expect an open signup endpoint enabled "by default". While it kind of makes sense, it kind of ruined the "roll your own auth with **confidence**" moment for me.
Author
Owner

@anthonyhoegberg commented on GitHub (Feb 6, 2025):

Some internal systems that companies uses needs this functionality, an example would be to only allow admins etc to create accounts and give them to users, but not allow new users to signup by themselves since this would allow outsiders into the site.

<!-- gh-comment-id:2639116632 --> @anthonyhoegberg commented on GitHub (Feb 6, 2025): Some internal systems that companies uses needs this functionality, an example would be to only allow admins etc to create accounts and give them to users, but not allow new users to signup by themselves since this would allow outsiders into the site.
Author
Owner

@MGSimard commented on GitHub (Feb 13, 2025):

Talked about it on the Better Auth discord, another user submitted a PR on my behalf here, hoping it goes through: https://github.com/better-auth/better-auth/pull/1428

This would ideally disable sign ups by adding a condition in the /sign-up API endpoint which would refer to a new signupsDisabled flag in config, while still(?) allowing admins to create users (docs example).

Was juggling with the idea of the flag also taking in an array of roles to partly limit sign-ups to certain roles (to create on behalf of users as other permitted roles), but the same person also has a standing RBAC PR which would introduce that level of role granularity for admin actions (like creating users). This would keep admin-related logic localized to that plugin while still accomplishing additional role permission granularity, which is arguably better: https://github.com/better-auth/better-auth/pull/1424

<!-- gh-comment-id:2656319993 --> @MGSimard commented on GitHub (Feb 13, 2025): Talked about it on the Better Auth discord, another user submitted a PR on my behalf here, hoping it goes through: https://github.com/better-auth/better-auth/pull/1428 This would ideally disable sign ups by adding a condition in the /sign-up API endpoint which would refer to a new signupsDisabled flag in config, while still(?) allowing admins to create users [(docs example)](https://www.better-auth.com/docs/plugins/admin#create-user). Was juggling with the idea of the flag also taking in an array of roles to partly limit sign-ups to certain roles *(to create on behalf of users as other permitted roles)*, but the same person also has a standing RBAC PR which would introduce that level of role granularity for admin actions (like creating users). This would keep admin-related logic localized to that plugin while still accomplishing additional role permission granularity, which is arguably better: https://github.com/better-auth/better-auth/pull/1424
Author
Owner

@dennisjnnh commented on GitHub (Feb 28, 2025):

i am wondering if there would be a possibility to do that with a provider (google) aswell? to restrict new sign ups but allow signins for already existing users?

<!-- gh-comment-id:2691292915 --> @dennisjnnh commented on GitHub (Feb 28, 2025): i am wondering if there would be a possibility to do that with a provider (google) aswell? to restrict new sign ups but allow signins for already existing users?
Author
Owner

@jslno commented on GitHub (Feb 28, 2025):

i am wondering if there would be a possibility to do that with a provider (google) aswell? to restrict new sign ups but allow signins for already existing users?

You just have to add an before hook to /sign-in/social that checks whether the user exists. If not throw an APIError.

<!-- gh-comment-id:2691326523 --> @jslno commented on GitHub (Feb 28, 2025): > i am wondering if there would be a possibility to do that with a provider (google) aswell? to restrict new sign ups but allow signins for already existing users? You just have to add an before hook to `/sign-in/social` that checks whether the user exists. If not throw an `APIError`.
Author
Owner

@dennisjnnh commented on GitHub (Feb 28, 2025):

i am wondering if there would be a possibility to do that with a provider (google) aswell? to restrict new sign ups but allow signins for already existing users?

You just have to add an before hook to /sign-in/social that checks whether the user exists. If not throw an APIError.

    before: createAuthMiddleware(async (ctx) => {
      const allowRegistrations = await getAllowRegistration()

      switch (ctx.path) {
        case "/callback/:id": {
          if (!allowRegistrations) {
            /* if user does not exist yet */
            throw new APIError("BAD_REQUEST", {
              message: "Registering is currently not allowed",
            })
          }
          break
        }
        case "/sign-up/email": {
          if (!allowRegistrations) {
            throw new APIError("BAD_REQUEST", {
              message: "Registering is currently not allowed",
            })
          }
          break
        }
      }
    }),

This middleware works now for email users. But for google accounts, i cannot seem to get any information about the user out of the ctx. Wether on /sign-in/social or /callback/:id.

Does anyone know if its possible to retreive / decrypt the user info from a google callback url?

<!-- gh-comment-id:2691431350 --> @dennisjnnh commented on GitHub (Feb 28, 2025): > > i am wondering if there would be a possibility to do that with a provider (google) aswell? to restrict new sign ups but allow signins for already existing users? > > You just have to add an before hook to `/sign-in/social` that checks whether the user exists. If not throw an `APIError`. ``` before: createAuthMiddleware(async (ctx) => { const allowRegistrations = await getAllowRegistration() switch (ctx.path) { case "/callback/:id": { if (!allowRegistrations) { /* if user does not exist yet */ throw new APIError("BAD_REQUEST", { message: "Registering is currently not allowed", }) } break } case "/sign-up/email": { if (!allowRegistrations) { throw new APIError("BAD_REQUEST", { message: "Registering is currently not allowed", }) } break } } }), ``` This middleware works now for email users. But for google accounts, i cannot seem to get any information about the user out of the ctx. Wether on /sign-in/social or /callback/:id. Does anyone know if its possible to retreive / decrypt the user info from a google callback url?
Author
Owner

@jgtavarez commented on GitHub (Aug 7, 2025):

i am wondering if there would be a possibility to do that with a provider (google) aswell? to restrict new sign ups but allow signins for already existing users?

You just have to add an before hook to /sign-in/social that checks whether the user exists. If not throw an APIError.

    before: createAuthMiddleware(async (ctx) => {
      const allowRegistrations = await getAllowRegistration()

      switch (ctx.path) {
        case "/callback/:id": {
          if (!allowRegistrations) {
            /* if user does not exist yet */
            throw new APIError("BAD_REQUEST", {
              message: "Registering is currently not allowed",
            })
          }
          break
        }
        case "/sign-up/email": {
          if (!allowRegistrations) {
            throw new APIError("BAD_REQUEST", {
              message: "Registering is currently not allowed",
            })
          }
          break
        }
      }
    }),

This middleware works now for email users. But for google accounts, i cannot seem to get any information about the user out of the ctx. Wether on /sign-in/social or /callback/:id.

Does anyone know if its possible to retreive / decrypt the user info from a google callback url?

google: {
			clientId: env.GOOGLE_CLIENT_ID,
			clientSecret: env.GOOGLE_CLIENT_SECRET,
			disableSignUp: true,
},
<!-- gh-comment-id:3166045135 --> @jgtavarez commented on GitHub (Aug 7, 2025): > > > i am wondering if there would be a possibility to do that with a provider (google) aswell? to restrict new sign ups but allow signins for already existing users? > > > > > > You just have to add an before hook to `/sign-in/social` that checks whether the user exists. If not throw an `APIError`. > > ``` > before: createAuthMiddleware(async (ctx) => { > const allowRegistrations = await getAllowRegistration() > > switch (ctx.path) { > case "/callback/:id": { > if (!allowRegistrations) { > /* if user does not exist yet */ > throw new APIError("BAD_REQUEST", { > message: "Registering is currently not allowed", > }) > } > break > } > case "/sign-up/email": { > if (!allowRegistrations) { > throw new APIError("BAD_REQUEST", { > message: "Registering is currently not allowed", > }) > } > break > } > } > }), > ``` > > This middleware works now for email users. But for google accounts, i cannot seem to get any information about the user out of the ctx. Wether on /sign-in/social or /callback/:id. > > Does anyone know if its possible to retreive / decrypt the user info from a google callback url? ``` google: { clientId: env.GOOGLE_CLIENT_ID, clientSecret: env.GOOGLE_CLIENT_SECRET, disableSignUp: true, }, ```
Author
Owner

@dennisjnnh commented on GitHub (Aug 8, 2025):

i am wondering if there would be a possibility to do that with a provider (google) aswell? to restrict new sign ups but allow signins for already existing users?

You just have to add an before hook to /sign-in/social that checks whether the user exists. If not throw an APIError.

    before: createAuthMiddleware(async (ctx) => {
      const allowRegistrations = await getAllowRegistration()

      switch (ctx.path) {
        case "/callback/:id": {
          if (!allowRegistrations) {
            /* if user does not exist yet */
            throw new APIError("BAD_REQUEST", {
              message: "Registering is currently not allowed",
            })
          }
          break
        }
        case "/sign-up/email": {
          if (!allowRegistrations) {
            throw new APIError("BAD_REQUEST", {
              message: "Registering is currently not allowed",
            })
          }
          break
        }
      }
    }),

This middleware works now for email users. But for google accounts, i cannot seem to get any information about the user out of the ctx. Wether on /sign-in/social or /callback/:id.
Does anyone know if its possible to retreive / decrypt the user info from a google callback url?

google: {
			clientId: env.GOOGLE_CLIENT_ID,
			clientSecret: env.GOOGLE_CLIENT_SECRET,
			disableSignUp: true,
},

this is good, but as i wanted to control disableSignUp programatically the static prop did not seem to work - in combination with a function, i got a pretty specific and odd nextjs error.

<!-- gh-comment-id:3168801281 --> @dennisjnnh commented on GitHub (Aug 8, 2025): > > > > i am wondering if there would be a possibility to do that with a provider (google) aswell? to restrict new sign ups but allow signins for already existing users? > > > > > > > > > You just have to add an before hook to `/sign-in/social` that checks whether the user exists. If not throw an `APIError`. > > > > > > ``` > > before: createAuthMiddleware(async (ctx) => { > > const allowRegistrations = await getAllowRegistration() > > > > switch (ctx.path) { > > case "/callback/:id": { > > if (!allowRegistrations) { > > /* if user does not exist yet */ > > throw new APIError("BAD_REQUEST", { > > message: "Registering is currently not allowed", > > }) > > } > > break > > } > > case "/sign-up/email": { > > if (!allowRegistrations) { > > throw new APIError("BAD_REQUEST", { > > message: "Registering is currently not allowed", > > }) > > } > > break > > } > > } > > }), > > ``` > > > > > > > > > > > > > > > > > > > > > > > > This middleware works now for email users. But for google accounts, i cannot seem to get any information about the user out of the ctx. Wether on /sign-in/social or /callback/:id. > > Does anyone know if its possible to retreive / decrypt the user info from a google callback url? > > ``` > google: { > clientId: env.GOOGLE_CLIENT_ID, > clientSecret: env.GOOGLE_CLIENT_SECRET, > disableSignUp: true, > }, > ``` this is good, but as i wanted to control disableSignUp programatically the static prop did not seem to work - in combination with a function, i got a pretty specific and odd nextjs error.
Author
Owner

@jgtavarez commented on GitHub (Aug 9, 2025):

i am wondering if there would be a possibility to do that with a provider (google) aswell? to restrict new sign ups but allow signins for already existing users?

You just have to add an before hook to /sign-in/social that checks whether the user exists. If not throw an APIError.

    before: createAuthMiddleware(async (ctx) => {
      const allowRegistrations = await getAllowRegistration()

      switch (ctx.path) {
        case "/callback/:id": {
          if (!allowRegistrations) {
            /* if user does not exist yet */
            throw new APIError("BAD_REQUEST", {
              message: "Registering is currently not allowed",
            })
          }
          break
        }
        case "/sign-up/email": {
          if (!allowRegistrations) {
            throw new APIError("BAD_REQUEST", {
              message: "Registering is currently not allowed",
            })
          }
          break
        }
      }
    }),

This middleware works now for email users. But for google accounts, i cannot seem to get any information about the user out of the ctx. Wether on /sign-in/social or /callback/:id.
Does anyone know if its possible to retreive / decrypt the user info from a google callback url?

google: {
			clientId: env.GOOGLE_CLIENT_ID,
			clientSecret: env.GOOGLE_CLIENT_SECRET,
			disableSignUp: true,
},

this is good, but as i wanted to control disableSignUp programatically the static prop did not seem to work - in combination with a function, i got a pretty specific and odd nextjs error.

if you wanna control the disableSignUp programatically, i think you still can

if you set this in the global config

socialProviders: {
		google: {
			clientId: env.GOOGLE_CLIENT_ID,
			clientSecret: env.GOOGLE_CLIENT_SECRET,
			disableImplicitSignUp: true,
		},
	},

and then when using the client you pass a requestSignUp based on your conditions, you should be able to enable the signup or disable when you want

await authClient.signIn.social(
    {
        provider: "google",
        callbackURL,
        requestSignUp: true, <- true to allow signup
    },
);

ref: https://www.better-auth.com/docs/concepts/oauth#other-provider-configurations

<!-- gh-comment-id:3169646613 --> @jgtavarez commented on GitHub (Aug 9, 2025): > > > > > i am wondering if there would be a possibility to do that with a provider (google) aswell? to restrict new sign ups but allow signins for already existing users? > > > > > > > > > > > > You just have to add an before hook to `/sign-in/social` that checks whether the user exists. If not throw an `APIError`. > > > > > > > > > ``` > > > before: createAuthMiddleware(async (ctx) => { > > > const allowRegistrations = await getAllowRegistration() > > > > > > switch (ctx.path) { > > > case "/callback/:id": { > > > if (!allowRegistrations) { > > > /* if user does not exist yet */ > > > throw new APIError("BAD_REQUEST", { > > > message: "Registering is currently not allowed", > > > }) > > > } > > > break > > > } > > > case "/sign-up/email": { > > > if (!allowRegistrations) { > > > throw new APIError("BAD_REQUEST", { > > > message: "Registering is currently not allowed", > > > }) > > > } > > > break > > > } > > > } > > > }), > > > ``` > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > This middleware works now for email users. But for google accounts, i cannot seem to get any information about the user out of the ctx. Wether on /sign-in/social or /callback/:id. > > > Does anyone know if its possible to retreive / decrypt the user info from a google callback url? > > > > > > ``` > > google: { > > clientId: env.GOOGLE_CLIENT_ID, > > clientSecret: env.GOOGLE_CLIENT_SECRET, > > disableSignUp: true, > > }, > > ``` > > this is good, but as i wanted to control disableSignUp programatically the static prop did not seem to work - in combination with a function, i got a pretty specific and odd nextjs error. if you wanna control the disableSignUp programatically, i think you still can if you set this in the global config ``` socialProviders: { google: { clientId: env.GOOGLE_CLIENT_ID, clientSecret: env.GOOGLE_CLIENT_SECRET, disableImplicitSignUp: true, }, }, ``` and then when using the client you pass a requestSignUp based on your conditions, you should be able to enable the signup or disable when you want ``` await authClient.signIn.social( { provider: "google", callbackURL, requestSignUp: true, <- true to allow signup }, ); ``` ref: https://www.better-auth.com/docs/concepts/oauth#other-provider-configurations
Author
Owner

@dennisjnnh commented on GitHub (Aug 9, 2025):

@jgtavarez Yes, this looks to be it - amazing man, thanks for sharing this.

<!-- gh-comment-id:3170208048 --> @dennisjnnh commented on GitHub (Aug 9, 2025): @jgtavarez Yes, this looks to be it - amazing man, thanks for sharing this.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#8614