fix(auth): avoid AD lockout on stale credentials#18
Open
edospadoni wants to merge 2 commits into
Open
Conversation
When the middleware rejects credentials (401/403) the LDAP bind has already failed upstream: skip the legacy /webrest fallback instead of repeating the same bind, which doubled the AD bad password count. Answer 410 Gone on explicitly rejected credentials and provision extProvLogoutStatusCode=410 in the account XML, so the app logs the user out and asks for new credentials instead of retrying forever with a stale password until the AD account gets locked.
There was a problem hiding this comment.
Pull request overview
This PR adjusts the authentication/provisioning flow in app/index.php to prevent duplicate upstream authentication attempts (which can trigger AD lockouts on stale credentials) and to instruct the mobile app to log out when credentials are explicitly rejected.
Changes:
- Track upstream HTTP status codes for middleware/login and
/user/mecalls to detect explicit credential rejection (401/403). - Skip legacy fallback when the middleware already rejected credentials (avoids duplicate LDAP binds per request).
- Return
410 Goneon explicit credential rejection and include<extProvLogoutStatusCode>410</extProvLogoutStatusCode>in successful provisioning XML so the app logs out instead of retrying indefinitely.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
When the upstream explicitly rejects the credentials the legacy fallback is skipped, so the log must not claim that both middleware and legacy modes were attempted.
Stell0
approved these changes
Jun 18, 2026
Stell0
approved these changes
Jun 18, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
On installations where the NethVoice user provider is a Microsoft Active Directory, users get locked out of their AD account after changing their password (or after the password expires) while the mobile app still holds the old one.
Two compounding causes, both in this proxy:
Duplicate LDAP binds per request.
getAuthContext()always cascaded from the middleware login (POST /api/login) to the legacy one (POST /webrest/authentication/login). Both endpoints end up performing the same LDAP bind against the user provider, so every provisioning request with stale credentials produced 2 failed binds on AD instead of 1.The app never logs out. On any non-2xx provisioning response, Acrobits shows a generic error and keeps the stored account untouched, retrying at every app open and at every
extProvIntervalcycle. With a stale password this means failed binds repeating forever, until the AD lockout threshold (badPwdCount) is reached and the account is locked (Security event 4625, SubStatus0xc000006a).Fix
Skip the legacy fallback when the middleware explicitly rejects the credentials (401/403). The middleware has already performed the LDAP bind at that point; repeating it on
/webrestis a guaranteed identical failure. The fallback still applies when the middleware is missing (404) or unreachable (network error / 5xx), so legacy-only installations are unaffected.Tell the app to log out when credentials are rejected. The provisioning account XML now includes
<extProvLogoutStatusCode>410</extProvLogoutStatusCode>, and theloginprovisioning answers410 Gone(instead of a generic 404) when the upstream explicitly rejected the credentials — wrong password, or QR/persistent token revoked (both/user/meendpoints answering 401/403). On 410 the app logs the user out and shows the login screen, so the user can re-enter the new password or scan a new QR code. All other failures (license validation, missing mobile extension, transient errors) keep answering 404 and leave the app account untouched.Verified end-to-end against a Microsoft AD: after a password change, the next re-provisioning gets a single failed bind, the app receives 410 and logs the user out; logging back in with the new password restores normal operation.
Note: apps already failing before this fix never received the
extProvLogoutStatusCodeparameter (it is delivered by a successful provisioning), so those users still need a one-time manual logout/login.