Skip to content

IMAP Login can run before server greeting (breaks email-oauth2-proxy / fast upstream setup) #218

@endolith

Description

@endolith

I haven't been able to get IMAP to work with https://github.com/simonrob/email-oauth2-proxy. Cursor with access to both codebases wrote this summary:

Summary

msgvault add-imap fails against a local plain-IMAP OAuth proxy (e.g. email-oauth2-proxy) with IMAP login: unexpected EOF, while Thunderbird succeeds with the same local host, port, and “no TLS to proxy” settings.

What I’m trying to do

  • Run an OAuth 2.0 email proxy on the same machine that listens on plain IMAP on a high port (e.g. 1993).
  • The proxy opens a separate TLS connection to the real provider (e.g. Office 365 IMAPS on 993), replaces LOGIN with AUTHENTICATE XOAUTH2, etc.
  • Configure msgvault with add-imap --no-tls to that loopback host and port so traffic to the proxy is unencrypted, matching the proxy’s documented model.
  • Expectation: connection test succeeds like any other IMAP client (e.g. Thunderbird).

Actual behavior

  • msgvault add-imap ... --no-tls reports: connection test failed: IMAP login: unexpected EOF when using a host name that reaches the proxy (e.g. localhost).

Environment (generic)

  • OS: Windows 10.
  • msgvault: add-imap with --no-tls, provider-agnostic mailbox.
  • Proxy: email-oauth2-proxy-style setup: local plain IMAP → remote outlook.office365.com:993 with TLS. (running near commit 779e70e5, need to update it)
  • Proxy config: local_address = localhost for the IMAP server section; remote server_address / server_port as required by the provider.

Evidence

1. msgvault does not wait for the IMAP greeting before Login

In internal/imap/client.go, after DialInsecure / DialTLS / DialStartTLS, the code calls Login(...).Wait() immediately with no WaitGreeting() (or equivalent).

2. go-imap v2 behavior

In github.com/emersion/go-imap/v2 (imapclient):

  • DialInsecure returns New(conn, options) as soon as TCP is connected; it does not block until the server greeting is received.
  • Login() sends the LOGIN command via beginCommand without an explicit WaitGreeting() first.

So the client may emit LOGIN on the wire before the server (here, the proxy) has finished bringing up the upstream session (TCP + TLS + provider greeting).

3. Proxy debug log (ordering)

With proxy --debug, a failing attempt showed (paraphrased):

  1. Inbound from msgvault: censored T1 LOGIN …
  2. Outbound toward provider: T1 AUTHENTICATE XOAUTH2 (+ payload)
  3. Then server-side: [ Client connected ] and Starting TLS handshake
  4. Then ConnectionAbortedError / WinError 10053 during TLS handshake on the upstream connection, followed disconnect → client sees EOF during login.

That ordering is consistent with commands being driven to the upstream socket before its TLS handshake has completed, which can abort the connection on Windows (10053).

4. Why Thunderbird works but msgvault doesn’t

  • Thunderbird (and typical GUI clients) follow the usual IMAP sequence: read the * OK greeting, then send commands like LOGIN. That gives the proxy time to finish the outbound TLS session to the provider before authentication traffic is forwarded.
  • msgvault + go-imap v2 can send LOGIN immediately after TCP connect, which does not guarantee the greeting has been processed or that the proxy’s upstream leg is ready, triggering the failure mode above.

This is an interoperability / spec-ordering issue: RFC 3501/9051 expect the client to wait for the greeting before commands; the library allows sending LOGIN without an explicit wait in the high-level path msgvault uses.

Suggested fix (implementation hint)

After successful Dial* and before Login, call (*imapclient.Client).WaitGreeting() and return a clear error if it fails. That matches IMAP ordering and aligns behavior with Thunderbird.

Optional follow-up: document that loopback host choice (localhost vs 127.0.0.1) depends on how local_address is set on the proxy (separate from this root cause).

Severity / impact

  • Medium for users relying on a local OAuth IMAP proxy (common for Microsoft/Google with legacy-style LOGIN).
  • Low for direct TLS to normal IMAP servers where the greeting arrives and is processed before any visible issue (many servers tolerate minimal slack, or timing hides the race).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions