Ldaprecord-laravel: Allow usage without admin user

Created on 16 Mar 2020  路  11Comments  路  Source: DirectoryTree/LdapRecord-Laravel

Is your feature request related to a problem? Please describe.
The primary problem is that all use cases don't support the use of an admin user. In our case, we don't have, and can not get, an admin user to verify against. We'd thus like to use the to-be authenticated user instead.

Describe the solution you'd like
Support of the $bindAsUser parameter as a configurable option - meaning that the library will, in all instances, disregard any specified LDAP_USERNAME/LDAP_PASSWORD, and instead bind as the user we're attempting to log in through.

Describe alternatives you've considered
An alternative would be to override the implemented attemptLogin() method in the LoginController and utilize the underlying LdapRecord library to manually authenticate with the $bindAsUser parameter.

Additional context
No context necessary - but huge kudos for an amazing library! :)

question

Most helpful comment

@stevebauman Perhaps that's the issue and why this can't be done? I'm looking at my specific use case, where AD Username formats such as the ones you specified work just fine. I know too little of other LDAP implementations to propose alternatives. I'll write my own implementation for our use case. Thanks for the back and forth, and thorough explanations! :)

All 11 comments

Hi @CmdrSharp,

The primary problem is that all use cases don't support the use of an admin user.

You don't need an 'admin' user, you simply need any user to bind to your domain to be able to retrieve the information from user accounts that are authenticating.

This account simply needs access to be able to read attributes on your LDAP user objects that will be authenticating in your Laravel application. In my production applications, I do not have a Domain Administrator user configured - I have a user with read-only permissions.

Support of the $bindAsUser parameter as a configurable option - meaning that the library will, in all instances, disregard any specified LDAP_USERNAME/LDAP_PASSWORD, and instead bind as the user we're attempting to log in through.

This is not possible. A user must be configured to bind to your domain to be able to retrieve the _authenticating_ users attributes. For example, their email address, username, and most importantly, their distinguished name.

There is no way around this. Every feature in LdapRecord-Laravel relies on a configured account to be able to bind to your domain. You would not be able to import users, use the WindowsAuthenticate middleware for single-sign-on, Eloquent model binding, or most importantly, the plain LDAP authentication driver.

Think of this exactly how you would configuring your Laravel applications database connection. You must have your database credentials inside your .env file so Laravel can connect at any time your application requires access.

I hope this answers your questions, and thanks for your kind words :smile:

Hi! Thanks for the elaborate (and quick) reply. I'm not sure I follow though - why would we not be able to use the user we're attempting to authenticate as, as the user we're doing the lookup with?
The difference is that we don't configure that user in static configuration.

What am I missing? When I attempt using ldap_bind directly, that very concept works fine (it would error if the user/password was incorrect, or work if the user authenticated correctly).

@stevebauman To add to my last reply: Isn't this exactly why $bindAsUser is a parameter to the attempt method in https://github.com/DirectoryTree/LdapRecord/blob/master/src/Auth/Guard.php ?

I'm not sure I follow though - why would we not be able to use the user we're attempting to authenticate as, as the user we're doing the lookup with?

How would you lookup a user without binding to your directory with a user first? How would that work?

The only way you could do this is through anonymous authentication, and in which case your domain supports that, you can set both the username and password configuration options to null.

Besides that, you can't connect to your domain, start running search operations, and then bind as your user. A connection is established with a username and password to your LDAP server, and then search operations are allowed.

What am I missing? When I attempt using ldap_bind directly, that very concept works fine (it would error if the user/password was incorrect, or work if the user authenticated correctly).

The ldap_bind method requires you to enter in the users Full Distinguished Name (cn=sbauman,dc=local,dc=com), or their userPrincipalName ([email protected]) which is an Active Directory only feature. Are you going to be logging in users with their full distinguished names?

LdapRecord-Laravel supports using any LDAP attribute as their username - this is accomplished by first binding to your LDAP server with the configured credentials you have in your .env file, which is used to perform an ldap_search for the user who is logging into your Laravel application. Once a user is located, their distinguished name becomes available (as it is returned from the search result), and then passed into the ldap_bind attempt.

To add to my last reply: Isn't this exactly why $bindAsUser is a parameter to the attempt method in https://github.com/DirectoryTree/LdapRecord/blob/master/src/Auth/Guard.php ?

This is in the core LdapRecord library, and can be used if required, but passing in that parameter will not automatically rebind to your directory as that user on subsequent HTTP requests to your Laravel application. Your user will be bound once for one single request, and then the connection will be shut down when the request completes. Your LDAP server will be inaccessible for any further requests in your application.

Credentials for bound users are not cached anywhere, so users who have successfully bound to your server would not have any type of persistent LDAP connection if your Laravel application required it (such as when using Eloquent Model Binding, or performing LDAP operations inside of your application after a login request).

I understand. Perhaps this is a matter of design where the library simply doesn't fit the use case. Like I said, an ldap_connect followed by ldap_bind would do what I'm proposing. ldap_bind would return false on a failed login, whereas it would successfully bind if the passed credentials are correct.

To answer your question, I can easily pass acme\username as the username or simply have another configurable value (such as LDAP_PREFIX).

I think this describes it best:
https://jotaelesalinas.github.io/laravel-simple-ldap-auth/

That's using the previous Adldap2/Adldap2-Laravel, but it seems to work fine. I guess I figured it would be a neat feature. Corporate AD's don't always grant system users, and I wouldn't want to leave my own user credentials inside a configuration file. Not to mention password rotation.

And to answer your first question:
How would you lookup a user without binding to your directory with a user first? How would that work?
That's not what I'm saying. What I'm saying is: Bind as the user we're trying to log in with. I was simply proposing for this to be a configurable option.

Like I said, an ldap_connect followed by ldap_bind would do what I'm proposing. ldap_bind would return false on a failed login, whereas it would successfully bind if the passed credentials are correct.

This would only work when using the Database Synchronization authentication driver, and not the Plain Authentication Driver, as it requires an LDAP connection to be established on every request to be able to retrieved the signed in user (as it is done in Laravel's database authentication).

Also, all features would break - such as importing / synchronizing users, eloquent model binding, single-sign-on, using alternate usernames, and more. These all require a configured user to bind prior.

To answer your question, I can easily pass acmeusername as the username or simply have another configurable value (such as LDAP_PREFIX).

My mistake, this is also an AD only feature.

Corporate AD's don't always grant system users, and I wouldn't want to leave my own user credentials inside a configuration file. Not to mention password rotation.

I've personally never seen a corporate AD without service accounts, but this is only my own personal experience.

That's not what I'm saying. What I'm saying is: Bind as the user we're trying to log in with. I was simply proposing for this to be a configurable option.

I'd accept a PR if you're able to figure this out!

The tutorial you've mentioned also don't utilize the authentication drivers from Adldap2-Laravel. It's using the core Adldap2 library, binding manually, and then creating a database user.

You can absolutely create your own authentication mechanism like this using the core LdapRecord library - but you won't be able to use any of the features shown in the documentation.

I hope this makes sense!

@stevebauman I understand, I think :) Perhaps it's my own understanding of AD that's lacking. I understand that the tutorial in question does this manually using the underlying Adldap2 library - but since it can, why can't the same thing be done here? What prevents specifying a configuration option that then authenticates as the proposed user, and maintains further operations binded as that very user upon a successful login?
I guess that's the part I'm trying to figure out.

What prevents specifying a configuration option that then authenticates as the proposed user, and maintains further operations binded as that very user upon a successful login?

Can you share a code example of how this would work in a configurable way that has compatibility with other LDAP distros?

The tutorial uses a hard-coded Distinguished Name format for binding users - which assumes that all users who are logging in are contained in the same Organizational Unit. This would already break for me, as my users are stored in separate OU's (such as 'Accounting', 'IT', 'Administration').

If we rely on Active Directory's username formats, such as DOMAIN\username or [email protected], then we've already removed all other LDAP distro compatibility.

This is the issue with the tutorials approach. You will have to somehow determine the authenticating users distinguished name to be able to bind to your LDAP server. Either that, or use AD only features.

If your users are contained in different OU's as mentioned above (or a user is moved in your AD into a different location), then the user will no longer be able to login to your application because their distinguished name has changed.

@stevebauman Perhaps that's the issue and why this can't be done? I'm looking at my specific use case, where AD Username formats such as the ones you specified work just fine. I know too little of other LDAP implementations to propose alternatives. I'll write my own implementation for our use case. Thanks for the back and forth, and thorough explanations! :)

Was this page helpful?
0 / 5 - 0 ratings

Related issues

kruiz122893 picture kruiz122893  路  5Comments

satyakresna picture satyakresna  路  7Comments

kruiz122893 picture kruiz122893  路  5Comments

mgiritli picture mgiritli  路  5Comments

jagDanJu picture jagDanJu  路  8Comments