feat: block free email provider signups

Mirrors the existing client-side check in the v3 signup form
(company-email-validator via Vuelidate) so non-business emails can no
longer slip in via Google OAuth, direct API calls, or any other path
that bypassed the form.

The deny list ships as config/deny_listed_email_domains.yml — picked up
automatically by ValidEmail2 — and is trimmed to the 6.5k consumer-only
domains the gem's disposable list doesn't already cover.
This commit is contained in:
Shivam Mishra
2026-05-19 13:31:55 +05:30
parent 0cb5eb8a5f
commit 685a1dda48
5 changed files with 6546 additions and 2 deletions
@@ -17,6 +17,8 @@ class Account::SignUpEmailValidationService
raise InvalidEmail.new({ valid: true, disposable: true }) if address.disposable?
raise InvalidEmail.new({ free_email_provider: true }) if address.deny_listed?
true
end
File diff suppressed because it is too large Load Diff
+1
View File
@@ -60,6 +60,7 @@ en:
signup:
disposable_email: We do not allow disposable emails
blocked_domain: This domain is not allowed. If you believe this is a mistake, please contact support.
free_email_provider: Please sign up with your work email address.
invalid_email: You have entered an invalid email
email_already_exists: 'You have already signed up for an account with %{email}'
invalid_params: 'Invalid, please check the signup paramters and try again'
+2
View File
@@ -7,6 +7,8 @@ module CustomExceptions::Account
I18n.t 'errors.signup.blocked_domain'
elsif @data[:disposable]
I18n.t 'errors.signup.disposable_email'
elsif @data[:free_email_provider]
I18n.t 'errors.signup.free_email_provider'
elsif !@data[:valid]
I18n.t 'errors.signup.invalid_email'
end
@@ -5,7 +5,8 @@ require 'rails_helper'
RSpec.describe Account::SignUpEmailValidationService, type: :service do
let(:service) { described_class.new(email) }
let(:blocked_domains) { "gmail.com\noutlook.com" }
let(:valid_email_address) { instance_double(ValidEmail2::Address, valid?: true, disposable?: false) }
let(:valid_email_address) { instance_double(ValidEmail2::Address, valid?: true, disposable?: false, deny_listed?: false) }
let(:free_provider_email_address) { instance_double(ValidEmail2::Address, valid?: true, disposable?: false, deny_listed?: true) }
let(:disposable_email_address) { instance_double(ValidEmail2::Address, valid?: true, disposable?: true) }
let(:invalid_email_address) { instance_double(ValidEmail2::Address, valid?: false) }
@@ -62,9 +63,21 @@ RSpec.describe Account::SignUpEmailValidationService, type: :service do
end
end
context 'when email is valid business email' do
context 'when email is from a free provider' do
let(:email) { 'test@example.com' }
it 'raises InvalidEmail with free email provider message' do
allow(ValidEmail2::Address).to receive(:new).with(email).and_return(free_provider_email_address)
expect { service.perform }.to raise_error do |error|
expect(error.class.name).to eq('CustomExceptions::Account::InvalidEmail')
expect(error.message).to eq(I18n.t('errors.signup.free_email_provider'))
end
end
end
context 'when email is valid business email' do
let(:email) { 'test@chatwoot.com' }
it 'returns true' do
allow(ValidEmail2::Address).to receive(:new).with(email).and_return(valid_email_address)
expect(service.perform).to be(true)