fix(email): add dns validation of email addresses

This commit is contained in:
Hampus Kraft
2026-02-19 19:33:55 +00:00
parent 3cc07f5e9f
commit 17306abec6
11 changed files with 432 additions and 2 deletions

View File

@@ -28,6 +28,7 @@ import {AuthSessionService} from '@fluxer/api/src/auth/services/AuthSessionServi
import {AuthUtilityService} from '@fluxer/api/src/auth/services/AuthUtilityService';
import {createMfaTicket, type UserID} from '@fluxer/api/src/BrandedTypes';
import type {IDiscriminatorService} from '@fluxer/api/src/infrastructure/DiscriminatorService';
import type {IEmailDnsValidationService} from '@fluxer/api/src/infrastructure/IEmailDnsValidationService';
import type {IGatewayService} from '@fluxer/api/src/infrastructure/IGatewayService';
import type {KVAccountDeletionQueueService} from '@fluxer/api/src/infrastructure/KVAccountDeletionQueueService';
import type {KVActivityTracker} from '@fluxer/api/src/infrastructure/KVActivityTracker';
@@ -160,6 +161,7 @@ export class AuthService implements IAuthService {
gatewayService: IGatewayService,
rateLimitService: IRateLimitService,
emailServiceDep: IEmailService,
emailDnsValidationService: IEmailDnsValidationService,
smsService: ISmsService,
snowflakeService: SnowflakeService,
snowflakeReservationService: SnowflakeReservationService,
@@ -182,6 +184,7 @@ export class AuthService implements IAuthService {
this.passwordService = new AuthPasswordService(
repository,
emailServiceDep,
emailDnsValidationService,
rateLimitService,
this.utilityService.generateSecureToken.bind(this.utilityService),
this.utilityService.handleBanStatus.bind(this.utilityService),
@@ -198,6 +201,7 @@ export class AuthService implements IAuthService {
inviteService,
rateLimitService,
emailServiceDep,
emailDnsValidationService,
snowflakeService,
snowflakeReservationService,
discriminatorService,

View File

@@ -20,6 +20,7 @@
import crypto from 'node:crypto';
import {createPasswordResetToken} from '@fluxer/api/src/BrandedTypes';
import {Config} from '@fluxer/api/src/Config';
import type {IEmailDnsValidationService} from '@fluxer/api/src/infrastructure/IEmailDnsValidationService';
import {Logger} from '@fluxer/api/src/Logger';
import type {AuthSession} from '@fluxer/api/src/models/AuthSession';
import type {User} from '@fluxer/api/src/models/User';
@@ -111,6 +112,7 @@ export class AuthPasswordService {
constructor(
private repository: IUserRepository,
private emailService: IEmailService,
private emailDnsValidationService: IEmailDnsValidationService,
private rateLimitService: IRateLimitService,
private generateSecureToken: () => Promise<string>,
private handleBanStatus: (user: User) => Promise<User>,
@@ -236,6 +238,11 @@ export class AuthPasswordService {
});
}
const hasValidDns = await this.emailDnsValidationService.hasValidDnsRecords(data.email);
if (!hasValidDns) {
throw InputValidationError.fromCode('email', ValidationErrorCodes.INVALID_EMAIL_ADDRESS);
}
const user = await this.repository.findByEmail(data.email);
if (!user) {
return;

View File

@@ -22,6 +22,7 @@ import {Config} from '@fluxer/api/src/Config';
import {FIRST_ADMIN_ACL_CONFIG_KEY} from '@fluxer/api/src/constants/InstanceConfig';
import {deleteOneOrMany, executeConditional} from '@fluxer/api/src/database/Cassandra';
import type {IDiscriminatorService} from '@fluxer/api/src/infrastructure/DiscriminatorService';
import type {IEmailDnsValidationService} from '@fluxer/api/src/infrastructure/IEmailDnsValidationService';
import type {KVActivityTracker} from '@fluxer/api/src/infrastructure/KVActivityTracker';
import type {SnowflakeService} from '@fluxer/api/src/infrastructure/SnowflakeService';
import {InstanceConfigRepository} from '@fluxer/api/src/instance/InstanceConfigRepository';
@@ -44,6 +45,7 @@ import {deriveUsernameFromDisplayName} from '@fluxer/api/src/utils/UsernameSugge
import type {ICacheService} from '@fluxer/cache/src/ICacheService';
import {AdminACLs} from '@fluxer/constants/src/AdminACLs';
import {UserFlags} from '@fluxer/constants/src/UserConstants';
import {ValidationErrorCodes} from '@fluxer/constants/src/ValidationErrorCodes';
import type {IEmailService} from '@fluxer/email/src/IEmailService';
import {InputValidationError} from '@fluxer/errors/src/domains/core/InputValidationError';
import {RateLimitError} from '@fluxer/errors/src/domains/core/RateLimitError';
@@ -168,6 +170,7 @@ export class AuthRegistrationService {
private inviteService: InviteService | null,
private rateLimitService: IRateLimitService,
private emailService: IEmailService,
private emailDnsValidationService: IEmailDnsValidationService,
private snowflakeService: SnowflakeService,
private snowflakeReservationService: SnowflakeReservationService,
private discriminatorService: IDiscriminatorService,
@@ -222,6 +225,11 @@ export class AuthRegistrationService {
await this.enforceRegistrationRateLimits({enforceRateLimits, clientIp, emailKey});
if (rawEmail) {
const hasValidDns = await this.emailDnsValidationService.hasValidDnsRecords(rawEmail);
if (!hasValidDns) {
throw InputValidationError.fromCode('email', ValidationErrorCodes.INVALID_EMAIL_ADDRESS);
}
const emailTaken = await this.repository.findByEmail(rawEmail);
if (emailTaken) throw InputValidationError.create('email', 'Email already in use');
}