Adding Gmail OAuth2: One Flow, Two Providers
When we shipped Microsoft OAuth2 in v1.2.0, the goal was clear: let users sign in with their existing Microsoft account instead of generating an app password. It worked well for Outlook, Hotmail, and M365 accounts. The natural next step was Gmail — the most popular email provider in the world.
Why OAuth2 Matters
Google has been tightening access to IMAP for years. If you have 2FA enabled (and you should), you need an App Password to use third-party email clients. App passwords work, but they're obscure, easy to forget, and feel like a workaround. OAuth2 lets users click "Sign in with Google," authenticate in their browser, and come back to the app — no passwords copied, no settings pages searched.
The Implementation
MailVault already had a working OAuth2 PKCE flow for Microsoft, written in Rust. The architecture has three parts: a local TCP callback server on localhost:19876 that receives the redirect after browser login, a token exchange function that swaps the authorization code for access/refresh tokens, and XOAUTH2 SASL authentication for IMAP and SMTP.
Adding Google meant making all of this provider-agnostic. Instead of hardcoding Microsoft's endpoints, we refactored the Rust OAuth2 module to accept provider configuration at runtime — authorization URL, token URL, scopes, and client ID all come from the frontend when initiating the flow.
For the client ID, we followed the same approach as Microsoft: use a well-known public client ID. Thunderbird, the open-source email client by Mozilla, registers public OAuth2 clients with both Google and Microsoft. MailVault uses Thunderbird's Google client ID (406964657835-aq8lmia8j95dhl1a2bvharmfk3t1hgqj.apps.googleusercontent.com) with the https://mail.google.com/ scope, which grants full IMAP and SMTP access.
What Changed in the Code
The Rust side needed a new oauth2_start command that accepts provider-specific parameters (auth URL, token URL, scopes, client ID) instead of assuming Microsoft. The callback server and PKCE logic stayed the same — OAuth2 is OAuth2, regardless of provider.
On the frontend, the AccountModal component now shows a "Sign in with Google" button for Gmail accounts (alongside the existing "Use App Password instead" fallback). The button triggers the same OAuth2 flow, just with Google's endpoints and scopes instead of Microsoft's. After authentication, the access token is used with XOAUTH2 SASL for both IMAP (imap.gmail.com) and SMTP (smtp.gmail.com).
Token refresh works identically to Microsoft: when an access token expires (typically after one hour), MailVault automatically refreshes it using the stored refresh token. The user never has to re-authenticate unless they revoke access from their Google account settings.
The Result
Gmail users can now click one button, sign in through Google's standard login page, and start using MailVault immediately. No app passwords, no IMAP settings to find, no "less secure apps" toggles. It works with 2FA, Google Workspace, and personal Gmail accounts.
The same OAuth2 infrastructure is now ready for any future provider that supports standard OAuth2 — the Rust module is fully provider-agnostic.