DirectoryTree / LdapRecord

A fully-featured LDAP framework.
https://ldaprecord.com
MIT License
507 stars 45 forks source link

Authenticate using user and pass provided by the user - Laravel Jetstream/Fortify #213

Closed fcno closed 3 years ago

fcno commented 3 years ago

php artisan --version: Laravel Framework 8.8.0 php -v: PHP 7.4.9 (cli) (built: Aug 4 2020 11:52:41) ( ZTS Visual C++ 2017 x64 ) Laravel Ldaprecord 1.7.2

Hi. I am studying LdapRecord to use it in an application. Currently, still testing it, I can authenticate normally in Active Directory following the documentation provided. However, I need this authentication to take place without the need for the following variables that will be null: LDAP_USERNAME and LDAP_PASSWORD. Instead, I intend to use the credentials provided by the user in the attempt to authenticate. I am using Laravel Jetstream with Fortify as suggested in its documentation, but I'm stuck and I'm not sure how to do this.

stevebauman commented 3 years ago

Hi @Babiute,

This is not supported. You must configure a username and password to connect to your LDAP server for authentication synchronization to take place, or allow anonymous binds to your AD server. This is because LdapRecord first locates the user in your directory (by pre-binding using your configured credentials), and then attempts to authenticate them.

Otherwise, you would have to have users bind by their full distinguished name, since you could not first locate them in your directory by their credentials (username / email address).

You can simply create a read-only Active Directory account to pre-bind to your LDAP server.

fcno commented 3 years ago

Hi Steve, thanks for the instant response, even though we are on a Saturday. I know you marked the topic closed, but I would like to explore a point in your answer. If I'm doing something wrong, please let me know so I can ask the question in the right place (another issue for example). You said you would have to have users bind by their full distinguished name. I think I could offer this username through some concatenation to the given username cn = user, dc = local, dc = com. Would that make any sense or would it still not be possible? In AdLdap2 I managed to do this by concatenating user @ sufix. Anyway, I thank you very much for the excellent work done both in the documentation and in the functionality. And that your son named ldaprecord have a long life and the recognition he deserves just like you.

stevebauman commented 3 years ago

Hi @Babiute,

Forgive me for closing this issue so soon without giving you a chance to respond.

Also, no problem! I'm always happy to help, this is right place to ask any questions 👍 😄

I think I could offer this username through some concatenation to the given username cn = user, dc = local, dc = com. Would that make any sense or would it still not be possible?

Yes this is possible, but only if your users reside in the same organizational unit.

For this to work, you will have to attempt to bind your LDAP connection during Fortify's authentication attempt by dynamically assembling the users distinguished name, then attempting an LDAP bind request.

Here's an example that should get you started:

Fortify::authenticateUsing(function ($request) {
    // Retrieve the LdapRecord authentication provider.
    $provider = Auth::getProvider();

    $provider->getLdapUserAuthenticator()->authenticateUsing(function () {
        // Since we are pre-binding to our LDAP connection using the
        // authenticating user, we can simply return `true` here,
        // since we know the users credentials are valid.
        return true;
    });

    $repository = $provider->getLdapUserRepository();

    // Retrieve the connection from the configured model.
    $connection = $repository->createModel()->getConnection();

    // Create the users distinguished name dynamically.
    $dn = sprintf('cn=%s,dc=local,dc=com', $request->username);

    // Bind to our LDAP connection.
    $connection->auth()->bind($dn, $request->password);

    // Attempt authentication (import / sync the user).
    $validated = Auth::validate([
        'samaccountname' => $request->username,
        'password' => $request->password
    ]);

    return $validated ? Auth::getLastAttempted() : null;
});

However, keep in mind that various LdapRecord-Laravel features will not work with this setup, such as (but not limited to) Eloquent Model Binding and Importing LDAP users. These features require a configured username and password to be able to connect to your LDAP server and perform these tasks.

I hope this helps! Let me know if you need any further assistance, or if you have any questions ❤️

fcno commented 3 years ago

Thank you very much @stevebauman. You don't just provide a "starting point", but the implementation is almost complete. As you pointed out, I end up losing a lot of functionality if I adopt this alternative. I will follow up with your initial recommendation and use an AD user with permission to read on the LDAP server. Anyway, thank you very much for your attention. You can close the topic.