Building a Secure Email Migration Tool: OAuth, Encryption, and Privacy by Design
Source: Dev.to
When building tools that handle user credentials and email data, security isn’t optional—it’s fundamental. Below are the key architectural decisions that made a POP3‑to‑Gmail migration service trustworthy.
OAuth for Gmail Access
Decision: Use Google OAuth instead of asking for the user’s Gmail password.
Benefits
- The service never sees the user’s Google password.
- Users authenticate directly with Google.
- Minimal permission scope (
gmail.insertonly). - Automatic token refresh.
- Users can revoke access anytime via Google Account settings.
// Request minimal scope
const SCOPES = ['https://www.googleapis.com/auth/gmail.insert'];
// Users authenticate with Google, receive OAuth code
// Exchange code for tokens server‑side
// Store refresh token encrypted
// Use for automated background sync
Key insight: gmail.insert allows adding messages but cannot read, modify, or delete existing email—the principle of least privilege in action.
Encrypting POP3 Credentials
Challenge: Background sync needs POP3 passwords, but plaintext passwords must never hit the server.
Solution: Encrypt passwords in the browser before transmission.
Architecture
- Generate a one‑time public/private key pair server‑side.
- Publish the public key at
/pop3-migrator.pub.asc. - The client fetches the public key, encrypts the POP3 password locally, and sends the ciphertext to the server.
- The server decrypts only when it needs to open a POP3 connection.
Why it matters
- Network sniffing can’t reveal passwords (they’re already encrypted).
- A database breach won’t expose plaintext passwords.
- Exposure window is minimized.
Privacy by Deletion
Challenge: Storing email metadata creates privacy risks and compliance burdens.
Solution: Delete messages from the POP3 server after a successful sync to Gmail.
Benefits
- No need to store what emails users have.
- No message metadata in the database.
- Reduces GDPR/privacy compliance surface area.
- Users’ email lives only in Gmail (single source of truth).
Trade‑off: Users must accept that messages cannot be re‑synced.
Implementation note: Deleting from the source also prevents duplicate imports if other migration tools are used in parallel.
Failure Notifications
Challenge: Silent failures lead to terrible UX and leave users uncertain about migration status.
Solution: Send email notifications when messages fail to import.
Typical failure scenarios
- Oversized messages (> 10 MB)
- Malformed messages
- Network failures
- API quota exceeded
Data Storage Policy
Principle: Only store what’s necessary for the service to function.
| What we store | What we don’t store |
|---|---|
| OAuth refresh tokens (encrypted) | Email content |
| POP3 credentials (encrypted) | Email metadata (subjects, senders, dates) |
| Sync state (which messages already synced) | User’s email list |
| Configuration (server, port, username) | Read/unread status |
Implementation: Use POP3 message UIDs (unique identifiers) to track synced messages without storing the actual message data.
SSRF Prevention
Challenge: Users provide hostnames; malicious actors could target internal services.
Solution: Validate and block private IP ranges.
Blocked ranges
127.0.0.1,::1(localhost)10.0.0.0/8172.16.0.0/12192.168.0.0/16- Link‑local addresses
- DNS rebinding attempts
Why it matters: Prevents attackers from using the service to probe internal networks or attack localhost services.
Security Decisions Summary
| Decision | Rationale |
|---|---|
| Stateful service with encrypted state storage | Needed for continuous sync; stateless would lose progress. |
| Client‑side encryption for POP3 credentials | Password never travels in plaintext. |
| OAuth over password storage | Higher user trust; direct Google authentication. |
| Encrypt early, decrypt late | Minimizes the window where sensitive data is accessible. |
| Privacy by deletion | Not storing data is safer than securing stored data. |
| Fail loudly in development, notify users in production | Improves UX and debugging. |
| Minimal scope creep | Request only the permissions actually needed. |
Pre‑Launch Checklist
- OAuth flow works end‑to‑end.
- Credentials are encrypted before leaving the browser (verify in the network tab).
- SSRF prevention blocks private IPs.
- Oversized messages are handled gracefully.
- Email notifications are sent on failures.
- Users can revoke access via Google Account settings.
Conclusion
Building secure migration tools requires careful thinking about:
- Authentication: Prefer OAuth over password storage.
- Encryption: Perform client‑side encryption before transmission.
- Privacy: Delete data you don’t need.
- Transparency: Notify users of failures.
- Least privilege: Request only the permissions you actually need.
The result is a tool users can trust with their email history. If you’re building similar tools, I hope these patterns help. What security considerations have you faced in your projects?