Issue Description
When an existing account authenticates over IMAP using a syntactically valid but invalid app-password token, Stalwart returns:
- NO [AUTHENTICATIONFAILED] Authentication failed
The response is untagged, and the LOGIN command receives no tagged completion. A client waiting for its command tag therefore keeps waiting instead of reporting the authentication failure.
An ordinary incorrect account password correctly receives a tagged NO response.
Environment:
- Stalwart v0.16.24, official Linux binary
- Internal directory
- IMAP over implicit TLS on port 993
- Affected webmail client: Roundcube 1.7.4
Root cause identified in the v0.16.24 source:
-
validate_credential() in crates/common/src/auth/authentication.rs adds the numeric credential ID as Key::Id to its authentication errors: “Invalid credential secret”, “Credential not found for account” and “Credential has expired”.
-
authenticate() in crates/imap/src/op/authenticate.rs calls err.id(tag.clone()), adding the IMAP command tag under the same Key::Id.
-
In crates/trc/src/event/mod.rs, id() delegates to ctx(), which appends rather than replaces. value() returns the first matching key, and value_as_str() attempts to read that value as a string.
-
SerializeResponse for trc::Error in crates/imap-proto/src/protocol/mod.rs uses value_as_str(Key::Id) for the response tag. It finds the numeric credential ID first, which is not a string, so it emits “*” instead of the command tag.
The ordinary wrong-password path does not add that numeric Key::Id, so its response is tagged correctly.
The IMAP command tag needs to remain distinguishable from the credential ID used as diagnostic metadata.
Expected Behavior
A rejected LOGIN command should promptly receive a tagged completion, for example:
a1 NO [AUTHENTICATIONFAILED] Authentication failed
The client can then report invalid credentials immediately.
RFC 9051, section 2.2.2, specifies that a command completion response is tagged with the same tag as the client command:
Actual Behavior
Stalwart promptly sends an untagged authentication failure but never sends the tagged completion. The client stays blocked until the server’s unauthenticated-session timeout closes the connection: Imap.timeoutAnonymous, 60000 ms by default. In our reproduction on v0.16.24 the untagged NO arrived after 0.06 s and the connection was closed with “* BYE Connection timed out.” after 60.06 s, with no tagged completion in between.
In Roundcube this appears as a wait of about 60 seconds followed by a generic IMAP connection error instead of an immediate login failure. Each affected request occupies a PHP worker while it waits.
Reproduction Steps
-
Connect to Stalwart over IMAP with implicit TLS on port 993.
-
Use the username of an existing account and a deliberately invalid synthetic app-password token: “app_” followed by 36 lowercase “a” characters.
-
Send:
a1 LOGIN “[email protected]” “app_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa” -
Observe:
- NO [AUTHENTICATIONFAILED] Authentication failed
No “a1 NO” completion follows; the server closes the connection only after Imap.timeoutAnonymous.
-
Open a fresh connection and compare with an ordinary incorrect password:
a1 LOGIN “[email protected]” “wrong-password-xyz” -
This time, observe the correctly tagged response:
a1 NO [AUTHENTICATIONFAILED] Authentication failed
Source inspection shows the same tag collision for a revoked app password, an app password whose secret is incorrect and an expired one.
Relevant Log Output
Direct protocol reproduction on Stalwart v0.16.24, default Imap.timeoutAnonymous.
Connection 1:
C: a1 LOGIN “[email protected]” “app_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa”
S (+0.06 s): * NO [AUTHENTICATIONFAILED] Authentication failed
S (+60.06 s): * BYE Connection timed out.
[connection closed by the server; no “a1” completion was ever sent]
Connection 2:
C: a1 LOGIN “[email protected]” “wrong-password-xyz”
S (+0.32 s): a1 NO [AUTHENTICATIONFAILED] Authentication failed
Stalwart Version
v0.16.x
Installation Method
Binary (Linux)
Database Backend
RocksDB
Blob Storage
RocksDB
Search Engine
Internal
Directory Backend
Internal
Additional Context
Client: Roundcube 1.7.4 webmail on the same host, connecting to 127.0.0.1:993 with implicit TLS; no proxy between the client and Stalwart’s IMAP listener. The transcript above was captured with a plain TLS socket straight to 127.0.0.1:993 on the server, so neither the client nor the network is involved. OS: AlmaLinux 10. Imap.timeoutAnonymous is at its default.
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