[Bug] Laravel UI Invalid credentials instead of LDAP error code
Environment:
- LDAP Server Type: Active Directory
- LdapRecord-Laravel Major Version: v3.3.5
- PHP Version: 8.3
Describe the bug: When I check only "User must change password at next logon" for a user in Active Directory, LDAP is returning that the credentials I use are invalid. Once I uncheck it, the user is able to login with the same credentials successfully.
I am using handleLdapBindError to intercept any error codes, and it used to work as expected. It is the default LoginController and the same namespace, and I added $this->listenForLdapBindFailure(); to its constructor just to be sure I didn't break something.
I am using Laravel UI so I'm wondering if they changed something since it used to work.
Here are the LDAP logs showing the flow:
[2025-01-08 10:56:07] local.INFO: LDAP (ldap://roc-dc01.blah.org:389) - Operation: Attempting - Username: CN=Blah Drive,OU=Blah Domain,DC=blah,DC=org
[2025-01-08 10:56:07] local.INFO: LDAP (ldap://roc-dc01.blah.org:389) - Operation: Binding - Username: CN=Blah Drive,OU=Blah Domain,DC=blah,DC=org
[2025-01-08 10:56:07] local.WARNING: LDAP (ldap://roc-dc01.blah.org:389) - Operation: Failed - Username: CN=Blah Drive,OU=Blah Domain,DC=blah,DC=org - Reason: Invalid credentials
[2025-01-08 10:56:07] local.INFO: LDAP (ldap://roc-dc01.blah.org:389) - Operation: Binding - Username: cn=sso_ldap,ou=Management,ou=Servers,dc=blah,dc=org
[2025-01-08 10:56:07] local.INFO: LDAP (ldap://roc-dc01.blah.org:389) - Operation: Bound - Username: cn=sso_ldap,ou=Management,ou=Servers,dc=blah,dc=org
[2025-01-08 10:56:07] local.INFO: User [Blah Drive] has failed LDAP authentication.
Thank you!
Hi @ellisonpatterson,
Can you post your LoginController please?
@stevebauman I removed some of the method logic, but I don't believe any of them would be causing this issue (I haven't changed the LoginController for a long time).
<?php
namespace App\Http\Controllers\Auth;
use Illuminate\Validation\ValidationException;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use LdapRecord\Laravel\Auth\ListensForLdapBindFailure;
use App\Http\Controllers\Controller;
use App\Models\User;
class LoginController extends Controller
{
use AuthenticatesUsers;
use ListensForLdapBindFailure {
handleLdapBindError as baseHandleLdapBindError;
}
protected $maxAttempts = 5;
protected $decayMinutes = 5;
protected $redirectTo = 'dashboard';
public function showLoginForm(Request $request)
{
}
public function logout(Request $request)
{
}
protected function authenticated(Request $request, $user)
{
}
protected function handleLdapBindError($message, $code = null)
{
// User account disabled
if ($code == '533') {
return redirect()->route('login')->with([
'alert' => __('sso.account_disabled'),
'alertType' => 'alert-danger'
]);
}
// User must change password
if ($code == '773') {
return redirect()->route('change-password')->with([
'alert' => __('sso.password_change_required'),
'alertType' => 'alert-warning'
]);
}
// User password has expired
if ($code == '532') {
return redirect()->route('change-password')->with([
'alert' => __('sso.password_expired'),
'alertType' => 'alert-warning'
]);
}
return $this->baseHandleLdapBindError($message, $code);
}
protected function credentials(Request $request)
{
return [
'userprincipalname' => $request->get('email'),
'password' => $request->get('password'),
];
}
}
Thanks @ellisonpatterson. If you dd() the $message, and $code in handleLdapBindError after attempting to login with a user whom has "User must change password at next logon" checked, what do you get?
Thanks @ellisonpatterson. If you
dd()the$message, and$codeinhandleLdapBindErrorafter attempting to login with a user whom has "User must change password at next logon" checked, what do you get?
That's the issue, handleLdapBindError is never called, Laravel is saying the login failed due to the credentials being invalid, and the LDAP logs say that as well.
https://github.com/laravel/ui/blob/0c38f3afa7db2e71d90d17c1e0a8ed572443c8ac/auth-backend/AuthenticatesUsers.php#L32-L60
If I dd() in sendFailedLoginResponse, I see it then. I'm not sure why Active Directory is saying the credentials are invalid if I only check "User must change password at next logon" for that user.
Actually this may be expected behavior?
https://support.oneidentity.com/kb/4259737/error-invalid-credentials-when-ad-user-has-user-must-change-password-at-next-logon
@stevebauman I'm wondering if it's occurring because of this issue I was having a while back, https://github.com/DirectoryTree/LdapRecord-Laravel/issues/398#issuecomment-1090411524
Just started a new project so I was blazing down the same trail currently and came across this same issue.
I remember in another project I had to wrap my Auth::attempt() in a try-catch when V3 came out. From memory and testing it now, its something to do with TLS encryption.
As an example, I've just purposefully misspelled my LDAP servers host record, which should be a timeout/unresolveable issue:
Env File: LDAP_TLS=true
LOG.error: ldap_search(): Search: Can't contact LDAP server { "exception": {} }
Env File: LDAP_TLS=false
LOG.error: Can't contact LDAP server { "code": null }
Brief testing shows me the events are not firing when TLS is enabled:
(This dd only fires if TLS is off in trait ListensForLdapBindFailure)
I'll do a little more debugging tomorrow to try narrow down the offending code if possible, but this might be a starting point for someone in the meantime.