feat: Ability to disable signups and disable email verification for self-hosters

This commit is contained in:
Dries Augustyns
2026-01-12 15:20:14 +01:00
parent 4db1ccc3fc
commit 93165644af
6 changed files with 80 additions and 26 deletions
+34 -22
View File
@@ -7,6 +7,7 @@ import * as React from 'react';
import {
DASHBOARD_URI,
DISABLE_SIGNUPS,
EMAIL_VERIFICATION_RATE_LIMIT,
EMAIL_VERIFICATION_RATE_WINDOW,
GITHUB_OAUTH_ENABLED,
@@ -15,6 +16,7 @@ import {
PASSWORD_RESET_RATE_LIMIT,
PLUNK_ENABLED,
TOKEN_EXPIRY_SECONDS,
VERIFY_EMAIL_ON_SIGNUP,
} from '../app/constants.js';
import {prisma} from '../database/prisma.js';
import {redis, REDIS_ONE_MINUTE} from '../database/redis.js';
@@ -63,33 +65,43 @@ export class Auth {
@Post('signup')
@CatchAsync
public async signup(req: Request, res: Response, _next: NextFunction) {
const {email, password} = AuthenticationSchemas.login.parse(req.body);
// Verify email is valid and not disposable/plus-addressed
const verification = await EmailVerificationService.verifyEmail(email);
if (
verification.isDisposable ||
verification.isPlusAddressed ||
!verification.domainExists ||
!verification.hasMxRecords
) {
// Build list of reasons for notification
const reasons: string[] = [];
if (verification.isDisposable) reasons.push('disposable email');
if (verification.isPlusAddressed) reasons.push('plus addressing');
if (!verification.domainExists) reasons.push('domain does not exist');
if (!verification.hasMxRecords) reasons.push('no MX records');
// Send notification about failed signup attempt
await NtfyService.notifyFailedSignupAttempt(email, reasons);
// Check if signups are disabled
if (DISABLE_SIGNUPS) {
return res.json({
success: false,
data: 'This email address cannot be used for signup',
data: 'New user signups are currently disabled',
});
}
const {email, password} = AuthenticationSchemas.login.parse(req.body);
// Verify email is valid and not disposable/plus-addressed (if verification enabled)
if (VERIFY_EMAIL_ON_SIGNUP) {
const verification = await EmailVerificationService.verifyEmail(email);
if (
verification.isDisposable ||
verification.isPlusAddressed ||
!verification.domainExists ||
!verification.hasMxRecords
) {
// Build list of reasons for notification
const reasons: string[] = [];
if (verification.isDisposable) reasons.push('disposable email');
if (verification.isPlusAddressed) reasons.push('plus addressing');
if (!verification.domainExists) reasons.push('domain does not exist');
if (!verification.hasMxRecords) reasons.push('no MX records');
// Send notification about failed signup attempt
await NtfyService.notifyFailedSignupAttempt(email, reasons);
return res.json({
success: false,
data: 'This email address cannot be used for signup',
});
}
}
const user = await UserService.email(email);
if (user) {
+7
View File
@@ -4,11 +4,13 @@ import type {NextFunction, Request, Response} from 'express';
import {
API_URI,
DASHBOARD_URI,
DISABLE_SIGNUPS,
GITHUB_OAUTH_CLIENT,
GITHUB_OAUTH_ENABLED,
GITHUB_OAUTH_SECRET,
} from '../../app/constants.js';
import {prisma} from '../../database/prisma.js';
import {BadRequest} from '../../exceptions/index.js';
import {jwt} from '../../middleware/auth.js';
import {NtfyService} from '../../services/NtfyService.js';
import {UserService} from '../../services/UserService.js';
@@ -81,6 +83,11 @@ export class Github {
let isNewUser = false;
if (!user) {
// Check if signups are disabled
if (DISABLE_SIGNUPS) {
throw new BadRequest('New user signups are currently disabled');
}
user = await prisma.user.create({
data: {
email,
+7
View File
@@ -4,11 +4,13 @@ import type {NextFunction, Request, Response} from 'express';
import {
API_URI,
DASHBOARD_URI,
DISABLE_SIGNUPS,
GOOGLE_OAUTH_CLIENT,
GOOGLE_OAUTH_ENABLED,
GOOGLE_OAUTH_SECRET,
} from '../../app/constants.js';
import {prisma} from '../../database/prisma.js';
import {BadRequest} from '../../exceptions/index.js';
import {jwt} from '../../middleware/auth.js';
import {NtfyService} from '../../services/NtfyService.js';
import {UserService} from '../../services/UserService.js';
@@ -71,6 +73,11 @@ export class Google {
let isNewUser = false;
if (!user) {
// Check if signups are disabled
if (DISABLE_SIGNUPS) {
throw new BadRequest('New user signups are currently disabled');
}
user = await prisma.user.create({
data: {
email,