diff --git a/apps/api/src/services/EmailVerificationService.ts b/apps/api/src/services/EmailVerificationService.ts index 1b2af63..8fdc512 100644 --- a/apps/api/src/services/EmailVerificationService.ts +++ b/apps/api/src/services/EmailVerificationService.ts @@ -8,6 +8,35 @@ const DISPOSABLE_DOMAINS_URL = const DISPOSABLE_DOMAINS_CACHE_KEY = 'email:disposable_domains'; 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 { private static disposableDomainsSet: Set | null = null; @@ -16,6 +45,7 @@ export class EmailVerificationService { * - Checks if domain exists (DNS A/AAAA records) * - Checks for MX records * - Detects disposable email addresses + * - Detects forwarding/alias email addresses * - Suggests corrections for common typos */ static async verifyEmail(email: string): Promise { @@ -23,6 +53,7 @@ export class EmailVerificationService { email, valid: true, isDisposable: false, + isAlias: false, isTypo: false, isPlusAddressed: false, domainExists: false, @@ -43,6 +74,9 @@ export class EmailVerificationService { // Check if email is from a disposable domain using GitHub list 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 result.isPlusAddressed = emailParts[0]!.includes('+'); @@ -146,4 +180,11 @@ export class EmailVerificationService { const disposableDomains = await this.getDisposableDomains(); 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()); + } } diff --git a/apps/landing/src/components/tools/EmailVerificationResult.tsx b/apps/landing/src/components/tools/EmailVerificationResult.tsx index 137fcfd..a6fe5ec 100644 --- a/apps/landing/src/components/tools/EmailVerificationResult.tsx +++ b/apps/landing/src/components/tools/EmailVerificationResult.tsx @@ -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'; interface EmailVerificationResultProps { @@ -83,6 +94,22 @@ export function EmailVerificationResult({result}: EmailVerificationResultProps) )} + {/* Alias/Forwarding Email */} +
+
+ +
+

Forwarding Service

+

Email alias/forwarding detected

+
+
+ {result.isAlias ? ( + + ) : ( + + )} +
+ {/* Typo Detection */}
diff --git a/apps/landing/src/lib/emailVerification.ts b/apps/landing/src/lib/emailVerification.ts index 491519f..0f80b80 100644 --- a/apps/landing/src/lib/emailVerification.ts +++ b/apps/landing/src/lib/emailVerification.ts @@ -2,6 +2,7 @@ export interface EmailVerificationResult { email: string; valid: boolean; isDisposable: boolean; + isAlias: boolean; isTypo: boolean; isPlusAddressed: boolean; domainExists: boolean; diff --git a/apps/wiki/openapi.json b/apps/wiki/openapi.json index 695cc09..d415055 100644 --- a/apps/wiki/openapi.json +++ b/apps/wiki/openapi.json @@ -664,6 +664,10 @@ "type": "boolean", "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": { "type": "boolean", "description": "Whether a potential typo was detected in the email address" @@ -698,6 +702,7 @@ "email", "valid", "isDisposable", + "isAlias", "isTypo", "isPlusAddressed", "domainExists", @@ -716,6 +721,7 @@ "email": "user@gmail.com", "valid": true, "isDisposable": false, + "isAlias": false, "isTypo": false, "isPlusAddressed": false, "domainExists": true, @@ -732,6 +738,7 @@ "email": "user@gmial.com", "valid": false, "isDisposable": false, + "isAlias": false, "isTypo": true, "isPlusAddressed": false, "domainExists": false, @@ -749,6 +756,7 @@ "email": "user@tempmail.com", "valid": true, "isDisposable": true, + "isAlias": false, "isTypo": false, "isPlusAddressed": false, "domainExists": true, diff --git a/packages/types/src/api/verification.ts b/packages/types/src/api/verification.ts index cbccdb7..f2037e7 100644 --- a/packages/types/src/api/verification.ts +++ b/packages/types/src/api/verification.ts @@ -9,6 +9,7 @@ export interface EmailVerificationResult { email: string; valid: boolean; isDisposable: boolean; + isAlias: boolean; isTypo: boolean; isPlusAddressed: boolean; domainExists: boolean;