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