Closed FrancescoD3V closed 2 months ago
Hey @FrancescoD3V,
I remember reading in the documentation that when the users are inserted for the first time in the database, the password field is generated with a "fake" password,
Yea this is correct -- LDAP servers do not return plain text passwords from searches, so it's impossible to store their real password during this process. The only way you can access the user's plain text password to store it in the local database is when they give it to you with an authentication attempt.
To do what you're looking for, you'll probably have to perform the same logical flow as the typical session based guard, but without accessing the session, since your api
route group would not have the session started/booted.
I believe you should be able to do this by creating an LdapRecord DatabaseUserProvider
instance manually. Something like this should get you started:
// Remember to create a credentials array meant to search your LDAP attribute (mail, userPrincipalName, etc.)
$credentials = [
'mail' => $request->get('email'),
'password' => $request->get('password'),
];
/** @var \LdapRecord\Laravel\Auth\DatabaseUserProvider $provider */
$provider = Auth::createUserProvider('ldap');
// Unsaved Eloquent user will be returned, or null if the LDAP user doesn't exist.
/** @var \App\Models\User */
$user = $provider->retrieveByCredentials($credentials);
// This will save the Eloquent user model (and its password) if the credentials are valid
if (! $provider->validateCredentials($user, $credentials)) {
return 'error';
}
$token = Auth::guard('api')->attempt([
'email' => $request->get('email'),
'password' => $request->get('password'),
]);
// ...
Hi Steve, your code works! I can update the hashed password if the credentials are entered.
I add a check to the code to verify that $user is not empty because if you try to pass a user that does not exist in the eloquent database, I get an error like this:
TypeError: LdapRecord\Laravel\Auth\DatabaseUserProvider::validateCredentials(): Argument #1 ($user) must be of type Illuminate\Contracts\Auth\Authenticatable, null given, called in /var/www/app/Http/Controllers/API/Auth/AuthController.php on line 77 in file /var/www/vendor/directorytree/ldaprecord-laravel/src/Auth/DatabaseUserProvider.php on line 174
Result:
$credentials = [
'mail' => $request->get('email'),
'password' => $request->get('password'),
];
/** @var \LdapRecord\Laravel\Auth\DatabaseUserProvider $provider */
$provider = Auth::createUserProvider('ldap');
// Unsaved Eloquent user will be returned, or null if the LDAP user doesn't exist.
/** @var \App\Models\User */
$user = $provider->retrieveByCredentials($credentials);
// This will save the Eloquent user model (and its password) if the credentials are valid
if ($user == null || !$provider->validateCredentials($user, $credentials_with_name_users)) {
return 'error';
}
$token = Auth::guard('api')->attempt([
'email' => $request->get('email'),
'password' => $request->get('password'),
]);
That's great @FrancescoD3V! I'm glad you're up and running and were able to resolve the issue.
Also, thanks for posting the detailed reply, as others may find this useful in the future 🙏
Hi guys, once again I need your amazing support! I have my own laravel application which provides a web interface where you can authenticate with your domain credentials and access your target area.
My application also exposes Rest APIs that allow Android devices to authenticate with the same domain credentials entered from the web.
Everything works great, but I'm having a synchronization issue related to passwords. Let me explain better: My application launches the "php artisan ldap:import --no-interaction" command when the docker container is started to synchronize all the users of the domain. I remember reading in the documentation that when the users are inserted for the first time in the database, the password field is generated with a "fake" password, waiting for the user to log in from the web interface and be able to individually synchronize their password in the database with the ldap server.
What actually happens is what has been described, the password changes when the user logs in from the web.
This synchronization event can only happen if the user uses the web interface since in the Laravel configuration, the session driver uses the ldap provider.
If a user logs in for the first time via Rest API, Laravel will not be able to synchronize the password with the Activedirectory server because in my configuration the APIs use another provider that I created.
I'll share my configuration with you:
//// auth.php
'guards' => [ 'web' => [ 'driver' => 'session', 'provider' => 'ldap', ], 'api' => [ 'driver' => 'jwt', 'provider' => 'users', ], ],
'providers' => [ 'users' => [ 'driver' => 'eloquent', 'model' => App\Models\User::class, ],
//// auth.php
I would like to find a simple way to make my driver can use ldaprecord to authenticate itself or if it is not possible to find an alternative solution.
At the moment the only way is to ask the user to log in at least the first time via the web interface
I share the authentication code via API Rest
//// myAuthController.php public function login(Request $request) { $request->validate([ 'email' => 'required|string', 'password' => 'required|string', ]);
//// myAuthController.php
Environment: