diff --git a/apps/api/src/controllers/Auth.ts b/apps/api/src/controllers/Auth.ts index 1c130b6..2acf986 100644 --- a/apps/api/src/controllers/Auth.ts +++ b/apps/api/src/controllers/Auth.ts @@ -21,6 +21,7 @@ import {redis, REDIS_ONE_MINUTE} from '../database/redis.js'; import {BadRequest, NotAuthenticated, RateLimitError} from '../exceptions/index.js'; import {jwt, parseJwt} from '../middleware/auth.js'; import {AuthService} from '../services/AuthService.js'; +import {EmailVerificationService} from '../services/EmailVerificationService.js'; import {NtfyService} from '../services/NtfyService.js'; import {UserService} from '../services/UserService.js'; import {Keys} from '../services/keys.js'; @@ -64,6 +65,31 @@ export class Auth { 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); + + return res.json({ + success: false, + data: 'This email address cannot be used for signup', + }); + } + const user = await UserService.email(email); if (user) { diff --git a/apps/api/src/services/NtfyService.ts b/apps/api/src/services/NtfyService.ts index ae971c3..84e5f34 100644 --- a/apps/api/src/services/NtfyService.ts +++ b/apps/api/src/services/NtfyService.ts @@ -318,6 +318,19 @@ export class NtfyService { }); } + /** + * Notify about failed signup attempt with invalid email - LOW priority + */ + public static async notifyFailedSignupAttempt(email: string, reasons: string[]): Promise { + const reasonText = reasons.join(', '); + await this.send({ + title: 'Failed Signup - Invalid Email', + message: `Signup attempt blocked for email: ${email}\nReasons: ${reasonText}`, + priority: NtfyPriority.LOW, + tags: [NtfyTag.WARNING, NtfyTag.SHIELD], + }); + } + /** * Notify about new user account created via OAuth - LOW priority */