fix(email): test saved OAuth accounts through shared transports (#5653)

* fix(email): use XOAUTH2 in test-connection for Google OAuth accounts

The test-connection endpoint was password-only and had no awareness of
OAuth accounts. For Google-connected accounts this caused two failures:
- IMAP: "Need IMAP host, username, and password" because imap_pass is
  empty (no password is stored for OAuth accounts)
- SMTP: 535 BadCredentials because smtp.login() was called with an
  empty password instead of an XOAUTH2 token

Fix: include oauth_provider and token fields in saved_body when hydrating
from the DB, then use conn.authenticate("XOAUTH2") / smtp.auth("XOAUTH2")
for Google accounts in both the IMAP and SMTP test paths, mirroring what
_send_smtp_message already does for real sends.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* test(email): add OAuth2 tests for test-connection endpoint

Covers the XOAUTH2 changes made to routes/email_routes.py:
- Google OAuth accounts must not be rejected with 'Need IMAP host,
  username, and password' (no stored password for OAuth accounts)
- IMAP and SMTP test paths must use conn.authenticate('XOAUTH2')
  for Google accounts
- Password accounts must still use conn.login() / smtp.login()

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(email): bind OAuth account tests to Google transport

---------

Co-authored-by: TNTBA <trynottobreakanything@gmail.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
RaresKeY
2026-07-22 14:54:38 +02:00
committed by GitHub
parent dcc7e52a86
commit 65987fc772
3 changed files with 570 additions and 15 deletions
+14 -3
View File
@@ -1035,13 +1035,23 @@ def _coerce_imap_timeout_seconds(raw: str | None) -> int:
_IMAP_TIMEOUT_SECONDS = _coerce_imap_timeout_seconds(os.environ.get("ODYSSEUS_IMAP_TIMEOUT_SECONDS"))
def _open_imap_connection(host: str, port: int, *, starttls: bool, timeout: int = _IMAP_TIMEOUT_SECONDS):
def _open_imap_connection(
host: str,
port: int,
*,
starttls: bool,
timeout: int = _IMAP_TIMEOUT_SECONDS,
ssl_context=None,
):
"""Open an IMAP connection using the configured security mode."""
port = int(port or 993)
if starttls:
conn = imaplib.IMAP4(host, port, timeout=timeout)
try:
conn.starttls()
if ssl_context:
conn.starttls(ssl_context=ssl_context)
else:
conn.starttls()
except Exception:
# Don't leak the open plain socket if the STARTTLS upgrade is
# rejected; close it before propagating. (#3174)
@@ -1051,7 +1061,8 @@ def _open_imap_connection(host: str, port: int, *, starttls: bool, timeout: int
pass
raise
elif port == 993:
conn = imaplib.IMAP4_SSL(host, port, timeout=timeout)
kwargs = {"ssl_context": ssl_context} if ssl_context else {}
conn = imaplib.IMAP4_SSL(host, port, timeout=timeout, **kwargs)
else:
conn = imaplib.IMAP4(host, port, timeout=timeout)
try: