feat: Add forwarding domains as verification check
This commit is contained in:
@@ -8,6 +8,35 @@ const DISPOSABLE_DOMAINS_URL =
|
|||||||
const DISPOSABLE_DOMAINS_CACHE_KEY = 'email:disposable_domains';
|
const DISPOSABLE_DOMAINS_CACHE_KEY = 'email:disposable_domains';
|
||||||
const CACHE_TTL_SECONDS = 24 * 60 * 60; // 24 hours (list updates daily)
|
const CACHE_TTL_SECONDS = 24 * 60 * 60; // 24 hours (list updates daily)
|
||||||
|
|
||||||
|
// Known email forwarding/alias services
|
||||||
|
const FORWARDING_DOMAINS = new Set([
|
||||||
|
'privaterelay.appleid.com', // Apple Sign In
|
||||||
|
'mozmail.com', // Firefox Relay
|
||||||
|
'simplelogin.com', // SimpleLogin
|
||||||
|
'simplelogin.fr',
|
||||||
|
'simplelogin.co',
|
||||||
|
'simplelogin.io',
|
||||||
|
'aleeas.com',
|
||||||
|
'slmail.me',
|
||||||
|
'dralias.com',
|
||||||
|
'8shield.net',
|
||||||
|
'anonaddy.com', // Addy.io
|
||||||
|
'anonaddy.me',
|
||||||
|
'addy.io',
|
||||||
|
'duck.com', // DuckDuckGo
|
||||||
|
'33mail.com', // 33mail
|
||||||
|
'33m.co',
|
||||||
|
'passmail.com', // Proton Pass
|
||||||
|
'passmail.net',
|
||||||
|
'passinbox.com',
|
||||||
|
'passfwd.com',
|
||||||
|
'y.yo.fr',
|
||||||
|
'opayq.com', // IronVest (formerly Blur)
|
||||||
|
'cloak.id', // Cloaked
|
||||||
|
'erine.email', // Erine
|
||||||
|
'use.startmail.com', // StartMail
|
||||||
|
]);
|
||||||
|
|
||||||
export class EmailVerificationService {
|
export class EmailVerificationService {
|
||||||
private static disposableDomainsSet: Set<string> | null = null;
|
private static disposableDomainsSet: Set<string> | null = null;
|
||||||
|
|
||||||
@@ -16,6 +45,7 @@ export class EmailVerificationService {
|
|||||||
* - Checks if domain exists (DNS A/AAAA records)
|
* - Checks if domain exists (DNS A/AAAA records)
|
||||||
* - Checks for MX records
|
* - Checks for MX records
|
||||||
* - Detects disposable email addresses
|
* - Detects disposable email addresses
|
||||||
|
* - Detects forwarding/alias email addresses
|
||||||
* - Suggests corrections for common typos
|
* - Suggests corrections for common typos
|
||||||
*/
|
*/
|
||||||
static async verifyEmail(email: string): Promise<EmailVerificationResult> {
|
static async verifyEmail(email: string): Promise<EmailVerificationResult> {
|
||||||
@@ -23,6 +53,7 @@ export class EmailVerificationService {
|
|||||||
email,
|
email,
|
||||||
valid: true,
|
valid: true,
|
||||||
isDisposable: false,
|
isDisposable: false,
|
||||||
|
isAlias: false,
|
||||||
isTypo: false,
|
isTypo: false,
|
||||||
isPlusAddressed: false,
|
isPlusAddressed: false,
|
||||||
domainExists: false,
|
domainExists: false,
|
||||||
@@ -43,6 +74,9 @@ export class EmailVerificationService {
|
|||||||
// Check if email is from a disposable domain using GitHub list
|
// Check if email is from a disposable domain using GitHub list
|
||||||
result.isDisposable = await this.isDisposableDomain(domain);
|
result.isDisposable = await this.isDisposableDomain(domain);
|
||||||
|
|
||||||
|
// Check if email is from a known forwarding/alias service
|
||||||
|
result.isAlias = this.isForwardingDomain(domain);
|
||||||
|
|
||||||
// Check for plus addressing
|
// Check for plus addressing
|
||||||
result.isPlusAddressed = emailParts[0]!.includes('+');
|
result.isPlusAddressed = emailParts[0]!.includes('+');
|
||||||
|
|
||||||
@@ -146,4 +180,11 @@ export class EmailVerificationService {
|
|||||||
const disposableDomains = await this.getDisposableDomains();
|
const disposableDomains = await this.getDisposableDomains();
|
||||||
return disposableDomains.has(domain.toLowerCase());
|
return disposableDomains.has(domain.toLowerCase());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a domain is a known forwarding/alias service
|
||||||
|
*/
|
||||||
|
private static isForwardingDomain(domain: string): boolean {
|
||||||
|
return FORWARDING_DOMAINS.has(domain.toLowerCase());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,15 @@
|
|||||||
import {AlertCircle, AlertTriangle, CheckCircle, Info, Mail, Server, Shield, Trash2, XCircle} from 'lucide-react';
|
import {
|
||||||
|
AlertCircle,
|
||||||
|
AlertTriangle,
|
||||||
|
CheckCircle,
|
||||||
|
Info,
|
||||||
|
Mail,
|
||||||
|
Server,
|
||||||
|
Shield,
|
||||||
|
Trash2,
|
||||||
|
XCircle,
|
||||||
|
Forward,
|
||||||
|
} from 'lucide-react';
|
||||||
import type {EmailVerificationResult as VerificationResult} from '../../lib/emailVerification';
|
import type {EmailVerificationResult as VerificationResult} from '../../lib/emailVerification';
|
||||||
|
|
||||||
interface EmailVerificationResultProps {
|
interface EmailVerificationResultProps {
|
||||||
@@ -83,6 +94,22 @@ export function EmailVerificationResult({result}: EmailVerificationResultProps)
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Alias/Forwarding Email */}
|
||||||
|
<div className="flex items-center justify-between px-6 py-4">
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<Forward className="h-5 w-5 text-neutral-600" />
|
||||||
|
<div>
|
||||||
|
<p className="font-medium text-neutral-900">Forwarding Service</p>
|
||||||
|
<p className="text-sm text-neutral-600">Email alias/forwarding detected</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{result.isAlias ? (
|
||||||
|
<Info className="h-5 w-5 text-blue-600" />
|
||||||
|
) : (
|
||||||
|
<CheckCircle className="h-5 w-5 text-green-600" />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Typo Detection */}
|
{/* Typo Detection */}
|
||||||
<div className="flex items-center justify-between px-6 py-4">
|
<div className="flex items-center justify-between px-6 py-4">
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ export interface EmailVerificationResult {
|
|||||||
email: string;
|
email: string;
|
||||||
valid: boolean;
|
valid: boolean;
|
||||||
isDisposable: boolean;
|
isDisposable: boolean;
|
||||||
|
isAlias: boolean;
|
||||||
isTypo: boolean;
|
isTypo: boolean;
|
||||||
isPlusAddressed: boolean;
|
isPlusAddressed: boolean;
|
||||||
domainExists: boolean;
|
domainExists: boolean;
|
||||||
|
|||||||
@@ -664,6 +664,10 @@
|
|||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"description": "Whether the email is from a disposable/temporary email domain"
|
"description": "Whether the email is from a disposable/temporary email domain"
|
||||||
},
|
},
|
||||||
|
"isAlias": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Whether the email is from a forwarding/alias service"
|
||||||
|
},
|
||||||
"isTypo": {
|
"isTypo": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"description": "Whether a potential typo was detected in the email address"
|
"description": "Whether a potential typo was detected in the email address"
|
||||||
@@ -698,6 +702,7 @@
|
|||||||
"email",
|
"email",
|
||||||
"valid",
|
"valid",
|
||||||
"isDisposable",
|
"isDisposable",
|
||||||
|
"isAlias",
|
||||||
"isTypo",
|
"isTypo",
|
||||||
"isPlusAddressed",
|
"isPlusAddressed",
|
||||||
"domainExists",
|
"domainExists",
|
||||||
@@ -716,6 +721,7 @@
|
|||||||
"email": "[email protected]",
|
"email": "[email protected]",
|
||||||
"valid": true,
|
"valid": true,
|
||||||
"isDisposable": false,
|
"isDisposable": false,
|
||||||
|
"isAlias": false,
|
||||||
"isTypo": false,
|
"isTypo": false,
|
||||||
"isPlusAddressed": false,
|
"isPlusAddressed": false,
|
||||||
"domainExists": true,
|
"domainExists": true,
|
||||||
@@ -732,6 +738,7 @@
|
|||||||
"email": "[email protected]",
|
"email": "[email protected]",
|
||||||
"valid": false,
|
"valid": false,
|
||||||
"isDisposable": false,
|
"isDisposable": false,
|
||||||
|
"isAlias": false,
|
||||||
"isTypo": true,
|
"isTypo": true,
|
||||||
"isPlusAddressed": false,
|
"isPlusAddressed": false,
|
||||||
"domainExists": false,
|
"domainExists": false,
|
||||||
@@ -749,6 +756,7 @@
|
|||||||
"email": "[email protected]",
|
"email": "[email protected]",
|
||||||
"valid": true,
|
"valid": true,
|
||||||
"isDisposable": true,
|
"isDisposable": true,
|
||||||
|
"isAlias": false,
|
||||||
"isTypo": false,
|
"isTypo": false,
|
||||||
"isPlusAddressed": false,
|
"isPlusAddressed": false,
|
||||||
"domainExists": true,
|
"domainExists": true,
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ export interface EmailVerificationResult {
|
|||||||
email: string;
|
email: string;
|
||||||
valid: boolean;
|
valid: boolean;
|
||||||
isDisposable: boolean;
|
isDisposable: boolean;
|
||||||
|
isAlias: boolean;
|
||||||
isTypo: boolean;
|
isTypo: boolean;
|
||||||
isPlusAddressed: boolean;
|
isPlusAddressed: boolean;
|
||||||
domainExists: boolean;
|
domainExists: boolean;
|
||||||
|
|||||||
Reference in New Issue
Block a user