@@ -788,7 +788,11 @@ test("POST /signup with redirect returns custom redirect URL", async () => {
788788} ) ;
789789
790790test ( "POST /signup rejects duplicate email" , async ( ) => {
791- const { url, helpers } = await serve ( ) ;
791+ const { url, helpers } = await serve ( {
792+ bindings : {
793+ enableSignups : true ,
794+ } ,
795+ } ) ;
792796 const email = "existing@example.com" ;
793797
794798 await helpers . createUser ( { email } ) ;
@@ -871,6 +875,31 @@ test("POST /signup without email verification creates session and redirects to c
871875 expect ( cookieString ) . toContain ( "last_login_provider=credentials" ) ;
872876} ) ;
873877
878+ test ( "POST /signup rejects when signups are disabled and not first user" , async ( ) => {
879+ const { url, helpers } = await serve ( {
880+ bindings : {
881+ enableSignups : false ,
882+ } ,
883+ } ) ;
884+
885+ await helpers . createUser ( { email : "existing@example.com" } ) ;
886+
887+ const res = await fetch ( `${ url } /api/auth/signup` , {
888+ method : "POST" ,
889+ headers : {
890+ "Content-Type" : "application/json" ,
891+ } ,
892+ body : JSON . stringify ( {
893+ email : "blocked@example.com" ,
894+ password : "password123" ,
895+ } ) ,
896+ } ) ;
897+
898+ expect ( res . status ) . toBe ( 403 ) ;
899+ const data = await res . json ( ) ;
900+ expect ( data . error ) . toBe ( "Signups are disabled" ) ;
901+ } ) ;
902+
874903test ( "POST /resend-email-verification regenerates token" , async ( ) => {
875904 const { url, helpers, bindings } = await serve ( ) ;
876905 const { user } = await helpers . createUser ( {
@@ -1105,6 +1134,7 @@ test("POST /signup second user with autoJoinOrganizations gets site_role member"
11051134 bindings : {
11061135 sendEmail : undefined ,
11071136 autoJoinOrganizations : true ,
1137+ enableSignups : true ,
11081138 } ,
11091139 } ) ;
11101140
@@ -1197,6 +1227,7 @@ test("GET /callback/github second user with autoJoinOrganizations gets site_role
11971227 GITHUB_CLIENT_ID : "test-client-id" ,
11981228 GITHUB_CLIENT_SECRET : "test-client-secret" ,
11991229 autoJoinOrganizations : true ,
1230+ enableSignups : true ,
12001231 } ,
12011232 } ) ;
12021233
@@ -1252,6 +1283,65 @@ test("GET /callback/github second user with autoJoinOrganizations gets site_role
12521283 expect ( secondUser ?. site_role ) . toBe ( "member" ) ;
12531284} ) ;
12541285
1286+ test ( "GET /callback/github blocks new user when signups are disabled" , async ( ) => {
1287+ const { url, bindings, helpers } = await serve ( {
1288+ bindings : {
1289+ GITHUB_CLIENT_ID : "test-client-id" ,
1290+ GITHUB_CLIENT_SECRET : "test-client-secret" ,
1291+ autoJoinOrganizations : true ,
1292+ enableSignups : false ,
1293+ } ,
1294+ } ) ;
1295+
1296+ const { user : firstUser } = await helpers . createUser ( {
1297+ email : "admin@example.com" ,
1298+ site_role : "admin" ,
1299+ } ) ;
1300+ const db = await bindings . database ( ) ;
1301+ await db . insertOrganizationWithMembership ( {
1302+ name : "default" ,
1303+ kind : "organization" ,
1304+ created_by : firstUser . id ,
1305+ } ) ;
1306+
1307+ mswServer . use (
1308+ http . post ( "https://github.com/login/oauth/access_token" , ( ) => {
1309+ return HttpResponse . json ( mockOAuthTokenResponse ) ;
1310+ } ) ,
1311+ http . get ( "https://api.github.com/user" , ( ) => {
1312+ return HttpResponse . json ( {
1313+ ...mockGitHubProfile ,
1314+ id : 999998 ,
1315+ email : "blockedoauth@example.com" ,
1316+ } ) ;
1317+ } )
1318+ ) ;
1319+
1320+ const { encode } = await import ( "next-auth/jwt" ) ;
1321+ const state = await encode ( {
1322+ secret : bindings . AUTH_SECRET ,
1323+ salt : "oauth-state" ,
1324+ token : {
1325+ provider : "github" ,
1326+ nonce : "test-nonce" ,
1327+ callbackUrl : `${ url } /api/auth/callback/github` ,
1328+ } ,
1329+ } ) ;
1330+
1331+ const res = await fetch (
1332+ `${ url } /api/auth/callback/github?code=test-code&state=${ state } ` ,
1333+ {
1334+ redirect : "manual" ,
1335+ }
1336+ ) ;
1337+
1338+ expect ( res . status ) . toBe ( 302 ) ;
1339+ expect ( res . headers . get ( "location" ) ) . toBe ( "/login?error=signups_disabled" ) ;
1340+
1341+ const blockedUser = await db . selectUserByEmail ( "blockedoauth@example.com" ) ;
1342+ expect ( blockedUser ) . toBeUndefined ( ) ;
1343+ } ) ;
1344+
12551345// Suspended user tests
12561346
12571347const oauthProviderConfigs = [
0 commit comments