diff --git a/packages/stack-server/prisma/migrations/20240611063825_sign_up_enable/migration.sql b/packages/stack-server/prisma/migrations/20240611063825_sign_up_enable/migration.sql
new file mode 100644
index 00000000..c46624bf
--- /dev/null
+++ b/packages/stack-server/prisma/migrations/20240611063825_sign_up_enable/migration.sql
@@ -0,0 +1,2 @@
+-- AlterTable
+ALTER TABLE "ProjectConfig" ADD COLUMN "signUpEnabled" BOOLEAN NOT NULL DEFAULT true;
diff --git a/packages/stack-server/prisma/schema.prisma b/packages/stack-server/prisma/schema.prisma
index ae6b0f9d..a64941c1 100644
--- a/packages/stack-server/prisma/schema.prisma
+++ b/packages/stack-server/prisma/schema.prisma
@@ -42,6 +42,7 @@ model ProjectConfig {
allowLocalhost Boolean
credentialEnabled Boolean
magicLinkEnabled Boolean
+ signUpEnabled Boolean @default(true)
createTeamOnSignUp Boolean
projects Project[]
diff --git a/packages/stack-server/prisma/seed.ts b/packages/stack-server/prisma/seed.ts
index 06c2d5c7..636e3110 100644
--- a/packages/stack-server/prisma/seed.ts
+++ b/packages/stack-server/prisma/seed.ts
@@ -58,6 +58,7 @@ async function seed() {
},
credentialEnabled: true,
magicLinkEnabled: true,
+ signUpEnabled: true,
createTeamOnSignUp: false,
},
},
diff --git a/packages/stack-server/src/app/(main)/(protected)/(outside-dashbaord)/new-project/page-client.tsx b/packages/stack-server/src/app/(main)/(protected)/(outside-dashbaord)/new-project/page-client.tsx
index cd2ea584..a91cfd94 100644
--- a/packages/stack-server/src/app/(main)/(protected)/(outside-dashbaord)/new-project/page-client.tsx
+++ b/packages/stack-server/src/app/(main)/(protected)/(outside-dashbaord)/new-project/page-client.tsx
@@ -40,6 +40,7 @@ export default function PageClient () {
id: "id",
credentialEnabled: form.watch("signInMethods").includes("credential"),
magicLinkEnabled: form.watch("signInMethods").includes("magicLink"),
+ signUpEnabled:true,
oauthProviders: (["google", "facebook", "github", "microsoft"] as const).map(provider => ({
id: provider,
enabled: form.watch("signInMethods").includes(provider),
@@ -56,6 +57,7 @@ export default function PageClient () {
config: {
credentialEnabled: values.signInMethods.includes("credential"),
magicLinkEnabled: values.signInMethods.includes("magicLink"),
+ signUpEnabled:true,
oauthProviders: (["google", "facebook", "github", "microsoft"] as const).map(provider => ({
id: provider,
enabled: values.signInMethods.includes(provider),
diff --git a/packages/stack-server/src/app/(main)/(protected)/projects/[projectId]/auth-methods/page-client.tsx b/packages/stack-server/src/app/(main)/(protected)/projects/[projectId]/auth-methods/page-client.tsx
index 45b65efd..0f065a0b 100644
--- a/packages/stack-server/src/app/(main)/(protected)/projects/[projectId]/auth-methods/page-client.tsx
+++ b/packages/stack-server/src/app/(main)/(protected)/projects/[projectId]/auth-methods/page-client.tsx
@@ -55,8 +55,22 @@ export default function PageClient() {
});
}}
/>;
- })}
+ })}
-
+
+
+ {
+ await project.update({
+ config: {
+ signUpEnabled: checked,
+ },
+ });
+ }}
+ />
+
+
);
}
diff --git a/packages/stack-server/src/app/api/v1/auth/callback/[provider]/route.tsx b/packages/stack-server/src/app/api/v1/auth/callback/[provider]/route.tsx
index fce07963..11edd271 100644
--- a/packages/stack-server/src/app/api/v1/auth/callback/[provider]/route.tsx
+++ b/packages/stack-server/src/app/api/v1/auth/callback/[provider]/route.tsx
@@ -99,6 +99,10 @@ export const GET = deprecatedSmartRouteHandler(async (req: NextRequest, options:
throw new StatusError(StatusError.NotFound, "Provider not found or not enabled");
}
+ if (!project.evaluatedConfig.signUpEnabled) {
+ throw new StatusError(StatusError.Forbidden, "new signup is not enabled");
+ }
+
const userInfo = await getProvider(provider).getCallback({
codeVerifier: innerCodeVerifier,
state: innerState,
diff --git a/packages/stack-server/src/app/api/v1/auth/send-magic-link/route.tsx b/packages/stack-server/src/app/api/v1/auth/send-magic-link/route.tsx
index ae5f6b8b..b15255fc 100644
--- a/packages/stack-server/src/app/api/v1/auth/send-magic-link/route.tsx
+++ b/packages/stack-server/src/app/api/v1/auth/send-magic-link/route.tsx
@@ -47,6 +47,10 @@ export const POST = deprecatedSmartRouteHandler(async (req: NextRequest) => {
throw new StatusError(StatusError.Forbidden, "Magic link is not enabled for this project");
}
+ if (!project.evaluatedConfig.signUpEnabled) {
+ throw new StatusError(StatusError.Forbidden, "new signup is not enabled");
+ }
+
const users = await prismaClient.projectUser.findMany({
where: {
projectId,
diff --git a/packages/stack-server/src/app/api/v1/auth/signup/route.tsx b/packages/stack-server/src/app/api/v1/auth/signup/route.tsx
index b0608777..96fbef5e 100644
--- a/packages/stack-server/src/app/api/v1/auth/signup/route.tsx
+++ b/packages/stack-server/src/app/api/v1/auth/signup/route.tsx
@@ -58,6 +58,10 @@ export const POST = deprecatedSmartRouteHandler(async (req: NextRequest) => {
throw new StatusError(StatusError.Forbidden, "Password authentication is not enabled");
}
+ if (!project.evaluatedConfig.signUpEnabled) {
+ throw new StatusError(StatusError.Forbidden, "new signup is not enabled");
+ }
+
if (
!validateUrl(
emailVerificationRedirectUrl,
diff --git a/packages/stack-server/src/app/api/v1/projects/[projectId]/route.tsx b/packages/stack-server/src/app/api/v1/projects/[projectId]/route.tsx
index d15ff470..ff75d380 100644
--- a/packages/stack-server/src/app/api/v1/projects/[projectId]/route.tsx
+++ b/packages/stack-server/src/app/api/v1/projects/[projectId]/route.tsx
@@ -56,6 +56,7 @@ const handler = deprecatedSmartRouteHandler(async (req: NextRequest, options: {
id: project.id,
credentialEnabled: project.evaluatedConfig.credentialEnabled,
magicLinkEnabled: project.evaluatedConfig.magicLinkEnabled,
+ signUpEnabled: project.evaluatedConfig.signUpEnabled,
oauthProviders: project.evaluatedConfig.oauthProviders.map(
(provider) => ({
id: provider.id,
diff --git a/packages/stack-server/src/lib/projects.tsx b/packages/stack-server/src/lib/projects.tsx
index b13c4c4f..61fbe2c2 100644
--- a/packages/stack-server/src/lib/projects.tsx
+++ b/packages/stack-server/src/lib/projects.tsx
@@ -173,6 +173,7 @@ export async function createProject(
allowLocalhost: projectOptions.config?.allowLocalhost ?? true,
credentialEnabled: !!projectOptions.config?.credentialEnabled,
magicLinkEnabled: !!projectOptions.config?.magicLinkEnabled,
+ signUpEnabled: !!projectOptions.config?.signUpEnabled ?? true,
createTeamOnSignUp: !!projectOptions.config?.createTeamOnSignUp,
emailServiceConfig: {
create: {
@@ -469,6 +470,7 @@ export async function updateProject(
data: {
credentialEnabled: options.config?.credentialEnabled,
magicLinkEnabled: options.config?.magicLinkEnabled,
+ signUpEnabled:options.config?.signUpEnabled,
allowLocalhost: options.config?.allowLocalhost,
createTeamOnSignUp: options.config?.createTeamOnSignUp,
},
@@ -531,6 +533,7 @@ export function projectJsonFromDbType(project: ProjectDB): ProjectJson {
allowLocalhost: project.config.allowLocalhost,
credentialEnabled: project.config.credentialEnabled,
magicLinkEnabled: project.config.magicLinkEnabled,
+ signUpEnabled: project.config.signUpEnabled,
createTeamOnSignUp: project.config.createTeamOnSignUp,
domains: project.config.domains.map((domain) => ({
domain: domain.domain,
@@ -592,6 +595,7 @@ const nonRequiredSchemas = {
).optional().default(undefined),
credentialEnabled: yup.boolean().optional(),
magicLinkEnabled: yup.boolean().optional(),
+ signUpEnabled: yup.boolean().optional(),
allowLocalhost: yup.boolean().optional(),
createTeamOnSignUp: yup.boolean().optional(),
emailConfig: yup.object({
@@ -628,6 +632,7 @@ export const projectSchemaToUpdateOptions = (
allowLocalhost: update.config.allowLocalhost,
credentialEnabled: update.config.credentialEnabled,
magicLinkEnabled: update.config.magicLinkEnabled,
+ signUpEnabled:update.config.signUpEnabled,
createTeamOnSignUp: update.config.createTeamOnSignUp,
oauthProviders: update.config.oauthProviders && update.config.oauthProviders.map((provider) => {
if (sharedProviders.includes(provider.type as SharedProvider)) {
diff --git a/packages/stack-shared/src/interface/adminInterface.ts b/packages/stack-shared/src/interface/adminInterface.ts
index b53cb9b2..ba4996f3 100644
--- a/packages/stack-shared/src/interface/adminInterface.ts
+++ b/packages/stack-shared/src/interface/adminInterface.ts
@@ -41,6 +41,7 @@ export type ProjectUpdateOptions = {
credentialEnabled?: boolean,
magicLinkEnabled?: boolean,
allowLocalhost?: boolean,
+ signUpEnabled?: boolean,
createTeamOnSignUp?: boolean,
emailConfig?: EmailConfigJson,
},
diff --git a/packages/stack-shared/src/interface/clientInterface.ts b/packages/stack-shared/src/interface/clientInterface.ts
index 85dab47b..42edec7a 100644
--- a/packages/stack-shared/src/interface/clientInterface.ts
+++ b/packages/stack-shared/src/interface/clientInterface.ts
@@ -41,6 +41,7 @@ export type ClientProjectJson = {
id: string,
credentialEnabled: boolean,
magicLinkEnabled: boolean,
+ signUpEnabled: boolean,
oauthProviders: {
id: string,
enabled: boolean,
@@ -95,6 +96,7 @@ export type ProjectJson = {
allowLocalhost: boolean,
credentialEnabled: boolean,
magicLinkEnabled: boolean,
+ signUpEnabled:boolean,
oauthProviders: OAuthProviderConfigJson[],
emailConfig?: EmailConfigJson,
domains: DomainConfigJson[],
diff --git a/packages/stack/src/lib/stack-app.ts b/packages/stack/src/lib/stack-app.ts
index 1781675e..b459a5c7 100644
--- a/packages/stack/src/lib/stack-app.ts
+++ b/packages/stack/src/lib/stack-app.ts
@@ -830,6 +830,7 @@ class _StackClientAppImpl