fix(mqtt-transport): warn when a configured password is dropped for want of a username

Driver.Mqtt.Transport-002 (Low, correctness). Credentials were attached only
when Username was non-empty, so a config carrying a Password and no Username
connected anonymously with the secret silently discarded — the deployment reads
as authenticated and is not, which is the direction an operator cannot see from
the config.

Warned rather than sent. MQTT 3.1.1 may not carry a password without a
username, so the password-only CONNECT is not available on every protocol
version this factory serves, and synthesizing one under V500 only would change
the wire behaviour of deployments that connect happily today. The configured
values stay authoritative; the drop just stops being silent. The secret is
never formatted into the message.

Tests: the warning fires and does NOT contain the password, plus the falsifying
control that an ordinary anonymous config (neither credential set) stays silent
— without it the fix could warn on every connect and still pass.
This commit is contained in:
Joseph Doherty
2026-08-10 07:58:19 -04:00
parent 94b8633e68
commit 5d4dd17d7f
2 changed files with 49 additions and 0 deletions
@@ -98,6 +98,18 @@ public static class MqttClientOptionsFactory
{
builder = builder.WithCredentials(options.Username, options.Password ?? string.Empty);
}
else if (!string.IsNullOrEmpty(options.Password))
{
// A password with no username is dropped, and the drop is the direction an operator cannot
// see: the config reads as authenticated and the CONNECT goes out anonymous. MQTT 3.1.1 may
// not carry a password without a username, so synthesizing one is not available on every
// protocol version this factory serves — and inventing a password-only CONNECT under 5.0
// only would change the wire behaviour of deployments that connect happily today. Say so
// once, here, and leave the configured values authoritative. Never format the secret.
logger?.LogWarning(
"MQTT: a password is configured with no username, so it is NOT being sent — this connection "
+ "will be made anonymously. Set the username, or clear the password to make that intentional.");
}
builder = builder.WithTlsOptions(tls => ConfigureTls(tls, options, logger));