Use Laravel DNS validation to make sure you're getting real emails
What validation do you run on emails that users enter in your Laravel application? For example, for validating a user registration, you might not have strayed too much from the default:
'email' => 'required|email|unique:users|max:255'
According to the Laravel docs, the default email
validator checks a string against the RFC spec. This just validates that the string is a valid email address.
RFC validation is not enough
Sadly, the RFC spec is quite loose in what it defines as an email. For example, this is a valid email according to the spec: john@gmail
. Yep, you don't even need a domain (though it was pointed to me that internal company domains could very well have that structure). Of course, for almost all use cases, we'd expect that our users would have their emails hosted on some external domain.
Even if you checked for the presence of a domain, this still leaves your users open to misspelling them. The most common one I see is something like john@gmail.con
.
In my product, Thankbox, I was tired of dealing with these cases and wanted to increase my confidence in the emails that users were supplying. I wanted to verify that the address was real.
Yes, I know that for user registrations you can send people a verification email, but I've got a lot of functionality where people leave their email without actually needing to be registered into the system. Therefore I needed another approach.
Enter DNS Validation
I read up and found that a good solution is to verify the DNS records of the email address provided by the user. This would take care of both the john@gmail
and the john@gmail.con
cases as they'd both fail the check.
I was happy to discover that Laravel offers a DNS validator and all you need to do is change your email validation from
'email' => 'email'
to
'email' => 'email:rfc,dns'
That way you apply both the RFC and DNS validators to your address. The DNS one is open source and can be found here.
Unit testing Faker gotcha
I happily swapped all of my email validators to use this new approach thinking "easy, problem solved".
Then my tests started randomly failing ❌
I couldn't really understand. I was using $faker->email
in all of my factories and tests. That should give a valid email right? Right...
$faker->email gives you a valid DNS email but only sometimes
Most of the time, faker will spit out an email like cordie65@yahoo.com
but it can also give you one like grayson.wost@wuckert.info
. The second one will fail DNS validation because the domain does not exist.
This took me a while to find but it made me realize that I cannot rely on $faker->email
for my unit tests.
$faker->freeEmail to the rescue
Reading up a bit on the faker docs I discovered that it also provides a $faker->freeEmail
function. Looking a bit more into it, I found out that it only generates an email from 3 safe domains:
protected static $freeEmailDomain = array('gmail.com', 'yahoo.com', 'hotmail.com');
This seemed like a perfect solution to me!
I quickly replaced all of my $faker->email
usages with $faker->freeEmail
and now all of my tests were reliably green ✅