SMTP AUTH fails when the client sends two spaces before the initial response

Issue Description

The Windows Phone 8.1 mail app can’t send mail through Stalwart. It sends the SMTP AUTH command with two spaces between the mechanism name and the initial response (AUTH PLAIN ). Stalwart accepts the mechanism but ignores the initial response, so client and server get out of step and authentication never completes. The phone then asks the user for the password again.

IMAP login from the same phone with the same credentials works, so the account can receive but not send.

The phone connects directly to the submissions listener (port 465, implicit TLS) on the local network. Allowed SASL mechanisms for that listener are [plain, login]; the result is the same with the default [plain, login, oauthbearer, xoauth2].

Expected Behavior

AUTH PLAIN and AUTH LOGIN (two spaces) are handled the same as the single-space forms: the initial response is used.

RFC 4954 specifies a single space, so the client is out of spec, but it can’t be changed on the phone.

Actual Behavior

  1. AUTH PLAIN gets an empty 334 challenge instead of an authentication result.
  2. AUTH LOGIN gets 334 VXNlcm5hbWU6 (“Username:”) instead of 334 UGFzc3dvcmQ6 (“Password:”).

Reproduction Steps

No real credentials are needed; dGVzdA== is “test”.

  1. Connect to the submissions port: openssl s_client -quiet -connect mailexampleorg:465
  2. Send EHLO test
  3. Send AUTH LOGIN dGVzdA== (one space). Reply: 334 UGFzc3dvcmQ6 (“Password:”). Correct.
  4. Reconnect, send EHLO test
  5. Send AUTH LOGIN dGVzdA== (two spaces). Reply: 334 VXNlcm5hbWU6 (“Username:”). The username was ignored.

Relevant Log Output

2026-09-30T08:43:58Z TRACE Raw SMTP output sent (smtp.raw-output) listenerId = "submissions", localPort = 465, remoteIp = 192.168.0.5, remotePort = 49514, size = 216, contents = "``250-mail.example.org`` you had me at EHLO\r\n250-SMTPUTF8\r\n250-SIZE 104857600\r\n250-REQUIRETLS\r\n250-PIPELINING\r\n250-NO-SOLICITING\r\n250-ENHANCEDSTATUSCODES\r\n250-CHUNKING\r\n250-BINARYMIME\r\n250-AUTH PLAIN LOGIN\r\n250 8BITMIME\r\n"
2026-09-30T08:43:58Z TRACE Raw SMTP input received (smtp.raw-input) listenerId = "submissions", localPort = 465, remoteIp = 192.168.0.5, remotePort = 49514, size = 86, contents = "AUTH PLAIN \r\n"
2026-09-30T08:43:58Z TRACE Raw SMTP output sent (smtp.raw-output) listenerId = "submissions", localPort = 465, remoteIp = 192.168.0.5, remotePort = 49514, size = 6, contents = "334 \r\n"
2026-09-30T08:43:58Z TRACE Raw SMTP input received (smtp.raw-input) listenerId = "submissions", localPort = 465, remoteIp = 192.168.0.5, remotePort = 49514, size = 12, contents = "AUTH LOGIN\r\n"
2026-09-30T08:44:03Z TRACE Raw SMTP output sent (smtp.raw-output) listenerId = "submissions", localPort = 465, remoteIp = 192.168.0.5, remotePort = 49514, size = 30, contents = "500 5.5.6 Invalid challenge.\r\n"
2026-09-30T08:44:03Z TRACE Raw SMTP input received (smtp.raw-input) listenerId = "submissions", localPort = 465, remoteIp = 192.168.0.5, remotePort = 49514, size = 38, contents = "AUTH LOGIN \r\n"
2026-09-30T08:44:03Z TRACE Raw SMTP output sent (smtp.raw-output) listenerId = "submissions", localPort = 465, remoteIp = 192.168.0.5, remotePort = 49514, size = 18, contents = "334 VXNlcm5hbWU6\r\n"
2026-09-30T08:44:03Z TRACE Network read error (network.read-error) listenerId = "submissions", localPort = 465, remoteIp = 192.168.0.5, remotePort = 49514, reason = "peer closed connection without sending TLS close_notify: https// docsrs/rustls/latest/rustls/manual/_03_howto/index.html#unexpected-eof"
2026-09-30T08:44:03Z DEBUG SMTP connection ended (smtp.connection-end) listenerId = "submissions", localPort = 465, remoteIp = 192.168.0.5, remotePort = 49514, elapsed = 5453ms

Stalwart Version

v0.16.x

Installation Method

Binary (Linux)

Database Backend

RocksDB

Blob Storage

RocksDB

Search Engine

Internal

Directory Backend

Internal

Additional Context

  • Client: built-in mail app on Windows Phone 8.1 (Nokia Lumia 1020), account type IMAP4, outgoing server on port 465 with SSL.
  • The phone connects to Stalwart directly over the local network; no proxy in between.
  • TLS 1.2 with an ECDSA P-256 Let’s Encrypt certificate (the phone has no cipher suite in common with Stalwart for RSA certificates).
  • The two-space behaviour is also reproducible with openssl s_client from a Linux machine, as in the steps above.

I have reviewed the documentation and FAQ and confirm that my issue is NOT addressed there.

on

I have searched this support forum (open and closed topics) and confirm this is not a duplicate.

on

I understand that topics in this category are triaged by a bot first but a human reply will follow up. If I’d prefer a human-only reply, I’ll add the no-ai tag to my topic.

on

Your diagnosis matches how the SMTP AUTH command is parsed on the server. The AUTH command line is split into mechanism and initial-response by the SMTP protocol parser (the smtp-proto crate), and that parser does not tolerate an extra space between the mechanism name and the initial response: with a single space it correctly captures the base64 blob as the initial response, but with two spaces the initial response ends up empty, so Stalwart falls back to issuing a 334 challenge and waits for a separate line, causing the client and server to go out of step exactly as you observed (empty challenge for AUTH PLAIN, and “Username:” instead of “Password:” for AUTH LOGIN).

This is a parsing gap rather than a configuration issue, there’s no server setting that changes how strictly the initial-response is parsed for AUTH, so there’s no config workaround to offer here (aside from having the client omit the initial response and send credentials on a separate 334-response line, which the Windows Phone client apparently doesn’t do). This looks like a genuine bug in the AUTH line parsing; a human maintainer will need to review the fix.

This is an automated reply from the Stalwart Help Bot. Other community members may follow up if this answer is incomplete or wrong.

Fixed in v0.16.25 (yet to be released).