Repository navigation
A client gets stuck forever on a failing authentication method #212
Description
Disclaimer: Content below is AI-generated under human supervision and scrutiny.
When a WinRMClient is given more than one authentication method, for example Kerberos then NTLM, or NTLM then Basic, it moves to the next method when one fails. But it never goes back to an earlier one. If the method it ended up on keeps failing, the client fails every request for the rest of its life, even after the original method would work again. Only creating a new client fixes it.
A single temporary failure is enough to get there, and that happens with valid credentials too:
- A Windows host that is restarting, or that briefly can't reach its domain controller, rejects logins for a while;
- A connection that drops in the middle of authentication gets rejected;
- An unreachable Kerberos server (KDC) makes Kerberos fail.
The method the client gets stuck on can fail for good, for example:
- NTLM, when the domain blocks it through policy;
- Basic, when it's disabled on the host (the Windows default).
How to Reproduce (3.1.00, Windows Server 2025 with Basic Authentication Disabled)
The client uses the password array you pass in rather than a copy. So a temporary rejection can be simulated by starting with a wrong password and correcting the array afterwards:
char[] password = "Passw0rd!".toCharArray();
password[password.length - 1] = '?'; // wrong password, same length
try (WinRMClient client = WinRMClient.builder(host).http()
.credentials("Administrator", password) // local account
.authentication(AuthScheme.NTLM, AuthScheme.BASIC)
.build()) {
client.wql("SELECT Name FROM Win32_ComputerSystem").execute(); // rejected, as expected
password[password.length - 1] = '!'; // password is correct again
client.wql("SELECT Name FROM Win32_ComputerSystem").execute(); // rejected, still uses Basic
client.wql("SELECT Name FROM Win32_ComputerSystem").execute(); // rejected, still uses Basic and will keep doing so for good
}After the first rejection, the client stays on Basic and never tries NTLM again. Since Basic is disabled, every subsequent request fails, while a new client with the same credentials connects immediately over NTLM.
The same happens with Kerberos then NTLM. After one Kerberos failure, for example with the KDC briefly unreachable, the client stays on NTLM, even through reconnections and after the KDC is back, which is a problem on hosts with NTLM disabled.
Possible Fix
Once every method has failed, start over from the first one on the next request. In FallbackAuthScheme.advance() (src/main/java/org/metricshub/winrm/light/FallbackAuthScheme.java):
public boolean advance() {
if (active != null && startIndex + 1 < candidates.size()) {
active.reset();
active = null;
startIndex++;
return true;
}
+ active = null;
+ startIndex = 0;
return false;
}The same rewind is needed in FallbackAuthScheme.authenticate(). When a method fails before the server is even contacted, for example Kerberos unable to get a ticket, the failure is raised there and advance() is never called:
for (int i = startIndex; i < candidates.size(); i++) {
...
}
+ startIndex = 0;
throw new IllegalStateException(
"All requested authentication schemes failed" +
(lastFailure == null ? "" : " (last: " + lastFailure.getMessage() + ")"),
lastFailure
);With both changes, a request on which every method failed makes the next request start over from the first method, whatever the reason each method failed.
EDIT: The suggested changes seemed to work for me. It will probably be fine fine, though you should check for possible regressions this might cause.
Activity
Start the authentication fallback over once every scheme has failed
iskandergaba commentedon Oct 27, 2026
That was pretty fast! Thanks for your quick reaction @bertysentry!
I do not mean to push or anything, but any idea on when will this fix be officially released?
bertysentry commentedon Oct 27, 2026
@iskandergaba 3.1.01 release in progress! Should be available in Maven Central in 30 minutes!