Introduction
Recently, I’ve been investigating Symfony as a solution for a student petitions web application that I’m working on at Taft College. Symfony is a PHP framework that greatly simplifies the creation of web applications. It’s essentially at Model View Controller (MVC) framework.
This blog outlines the enabling of Active Directory integration with Symfony. My reason for documenting it here, was because when I tried searching online for information/examples on how to achieve this, there were references on how to install various components but there were no real step-by-step instructions on how to verify everything it actually working. Hopefully this will help someone else out who is struggling to set this up properly.
If you don’t need to manage users and simply just need to authenticate against an Active Directory server, then look at my new article Symfony LDAP Component AD Authentication instead.
Installing Symfony
This blog presumes you have some Linux OS and are using LAMP (Linux Apache MySql/MariaDB PHP) and each of those is configured correctly. You will need to install Symfony and create your Symfony project that you’ll be using to setup the Active Directory integeration. The Symfony Book Installing and Configuring Symfony gives instructions on how to install Symfony (binary) and Composer (you’ll need this too); I recommend to fully go over most of the Symfony Book, as it will help you get familiar with Symfony. Once you’ve created your Symfony project, you can proceed with the next steps.
Install FOSUserBundle
The Friends Of Symfony User Bundle (FOSUserBundle), is a bundle that persists and fetches users in a database. To install it, use composer with the following command within the root of your Symfony web application. For example, let’s presume your apache DocumentRoot is ‘/var/www/html’ and you’ve created your Symfony project application as ‘test’ in the DocumentRoot, then you would run this command under ‘/var/www/html/test’. Run the following command to install FOSUserBundle:
composer require friendsofsymfony/user-bundle "~2.0@dev"
Install PHP LDAP
In this example, I’m using Centos 6. So if you have a different Linux distro, you may have to install with different commands (i.e. rpm, apt-get). Run the following command to install ext-ldap:
yum install php-ldap
There is no post install configuration required.
Install FR3DLdapBundle
The FR3DLdapBundle provide LDAP authentication. To install it, run the following composer command from your Symfony project root:
composer require fr3d/ldap-bundle "3.0.*@dev"
Configuration
You will need to edit a number of files described here to setup the user, security and Active Directory configuration. First, open the file ‘app/AppKernel.php’ and add the FOSUserBundle and FR3DLdapBundle bundles to the registerBundles function:
<?php // app/AppKernel.php public function registerBundles() { $bundles = [ // ... new FOS\UserBundle\FOSUserBundle(), new FR3D\LdapBundle\FR3DLdapBundle(), // ... ]; }
Create a file called ‘FOSUser.php’ under ‘src/AppBundle/Entity’. Note: create the Entity directory if it doesn’t exist. Add the following contents to FOSUser.php:
<?php // src/AppBundle/Entity/FOSUser.php namespace AppBundle\Entity; use FOS\UserBundle\Model\User as BaseUser; use FR3D\LdapBundle\Model\LdapUserInterface; use Doctrine\ORM\Mapping as ORM; /** * @ORM\Entity * @ORM\Table(name="fos_user") */ class FOSUser extends BaseUser implements LdapUserInterface{ /** * @ORM\Id * @ORM\Column(type="integer") * @ORM\GeneratedValue(strategy="AUTO") */ protected $id; /** * @ORM\Column(type="string") */ protected $dn; public function __construct() { parent::__construct(); if (empty($this->roles)) { $this->roles[] = 'ROLE_USER'; } } /** * {@inheritDoc} */ public function setDn($dn) { $this->dn = $dn; } /** * {@inheritDoc} */ public function getDn() { return $this->dn; } }
Open the file ‘app/config/routing.yml’ and make the following change:
# app/config/routing.yml ... fos_user: resource: "@FOSUserBundle/Resources/config/routing/all.xml"
The above setting enables Symfony routing based on configuration from the file vendor/friendsofsymfony/user-bundle/Resources/config/routing/all.xml which enables the routes such as “/login” and “/logout”, providing standard login/logout capability. The Bundle can be overriden but that is not discussed here.
Open the file ‘app/config/security.yml’ and make the following changes. NOTE: This is only my example; Read the Symfony Book > Security section for other examples on how you might configure security for your own scenario. Symfony is highly configurable, and this example is only used as a guideline on helping you to understand how security works in Symfony.
# 'app/config/security.yml' security: # Preserve plain text password in token for refresh the user. # Analyze the security considerations before turn off this setting. erase_credentials: false encoders: FOS\UserBundle\Model\UserInterface: bcrypt role_hierarchy: ROLE_ADMIN: ROLE_USER ROLE_SUPER_ADMIN: ROLE_ADMIN providers: chain_provider: chain: providers: [fos_userbundle, fr3d_ldapbundle] fr3d_ldapbundle: id: fr3d_ldap.security.user.provider fos_userbundle: id: fos_user.user_provider.username firewalls: # disables authentication for assets and the profiler, adapt it according to your needs dev: pattern: ^/(_(profiler|wdt)|css|images|js)/ fr3d_ldap: ~ security: false form_login: always_use_default_target_path: true default_target_path: /profile logout: true anonymous: true main: pattern: ^/ fr3d_ldap: ~ form_login: always_use_default_target_path: true default_target_path: /profile logout: true anonymous: true access_control: - { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY } - { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY } - { path: ^/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY } - { path: ^/admin/, role: ROLE_ADMIN }
Open the file ‘app/config/config.yml’ and add the following lines at the bottom of the file:
# app/config/config.yml ... # Friends of Symfony user fos_user: db_driver: orm firewall_name: main user_class: AppBundle\Entity\FOSUser from_email: address: someone@somewhere.com sender_name: someone fr3d_ldap: driver: host: ldap.forumsys.com user: baseDn: dc=example, dc=com attributes: - { ldap_attr: uid, user_method: setUsername } - { ldap_attr: mail, user_method: setEmail } filter: (&(ObjectClass=person))
In the above configuration, I’m using the online test LDAP server ‘ldap.forumsys.com’, which is available for anyone to use to test LDAP authentication. Information on this LDAP test server is found at the following web site: http://www.forumsys.com/tutorials/integration-how-to/ldap/online-ldap-test-server/
Update Schema
Run the following command from the project root directory:
php bin/console doctrine:schema:update --force
This updates the database schema.
Testing Operation
This section describes the steps needed to verify that the above configuration is actually working and doing what it is supposed to. The key to troubleshooting is checking the ‘var/logs/dev.log’ entries. Since the login page configured is ‘^/login$’, you need to point your browser to your apache Directory path. I’m presuming here that you’ve configured this as your Symfony root/web path (the web folder under your Symfony project root directory). Then in your browser enter the following URL:
http://host.name/app_dev.php/login
Where ‘host.name’ is either an IP address or hostname where your apache web server is running. Notice the path contains ‘app_dev.php’. This is a file located in the web folder. If you are not running your browser on the same host that the apache web server is running on, then you’ll need to open this file and add the IP address of your host machine to the ‘REMOTE_ADDR’ array. NOTE: Only add trusted hosts to the ‘REMOTE_ADDR’ array. The following line shows an example where my host machine has an IP address of ‘192.168.40.208’:
// web/app_dev.php ... if (isset($_SERVER['HTTP_CLIENT_IP']) || isset($_SERVER['HTTP_X_FORWARDED_FOR']) || !(in_array(@$_SERVER['REMOTE_ADDR'], ['192.168.40.208', '127.0.0.1', 'fe80::1', '::1']) || php_sapi_name() === 'cli-server') )
After you browse to the login page, you should should see labels and text fields for entering username and password and a submit button. Enter a username of “riemann” and password of “password”, then click “security.login.submit”, then you should the profile information for the user. Click the “layout.logout” link to logout.
If the login/logout works correctly, then the normal login URL to use is:
http://host.name/login
And to logout, simply use:
http://host.name/logout
Getting AD Authentication to Work
Once you have verified the above (verifies LDAP authentication works), then you need to change your “config.yml” to use your Active Directory server. Edit your “config.yml” as follows (the following data is obsfucated):
# FILE: app/config/config.yml ... fr3d_ldap: driver: host: my_ad_server.hostname.com username: ad_admin_user_account password: admin_password accountDomainName: DOM.hostname.com accountDomainNameShort: DOM # i.e. prefix you use to login. user: usernameAttribute: sAMAccountName baseDn: cn=Users,dc=hostname,dc=com # Use a LDAP browser to verify this. filter: (&(ObjectClass=Person)) attributes: - { ldap_attr: samaccountname, user_method: setUsername } - { ldap_attr: mail, user_method: setEmail }
You should now be able to login to your Symfony system using your Active Directory accounts. Always start in the dev environment (append ‘app_dev.php’ to URL) to check first that things are working normally, and check the var/logs/dev.log for error messages.
Change this: “fr3d/ldap-bundle”: “3.0.*@dev” to “fr3d/ldap-bundle”: “2.0.*@dev”. If you are still having problems, please post a question on Stackoverflow here (http://stackoverflow.com/questions/tagged/fr3dldapbundle). Hopefully you still don’t have that namespace problem…
Small error in the article: In the config.yml the FOS section should be:
# Friends of Symfony user
fos_user:
db_driver: orm
firewall_name: main
user_class: AppBundle\Entity\FOSUser
not:
# Friends of Symfony user
fos_user:
db_driver: orm
firewall_name: main
user_class: AppBundle\Entity\User
Since that’s the name of your entity class. Once I’d changed that, everything worked fine 🙂
Thank you for the Feedback! I changed my post to reflect those changes.
Hello and thanks for this article.
Have you try to connect with an user, move this user in another OU in your AD (this will change DN) and retry to connect ?
It seems ldap_bind reject connection because the DN register in database don’t match with the DN of the user in AD.
So I don’t know how to patch it.
You might have to change the basedn value temporarily and get the user to login, and then change the basedn back to the normal value. Once they are able to login, the user record is written to the fos_user table and they are able to login afterwards.
Hello, first of all I would like to thank you for your help and your sharing. Rare are the tutorials regarding the LDAP on symfony 3, really thank you. After following the tutorial, I encounter an error when I enter the identifiers. I am returned “The authentication request could not be executed due to a system problem.” My files security.yml, config.yml and my entity User are the same as you except that my entity is called User and not FOSUser and my Bundle is called UserBundle. Did you know where the problem came from? If you want I can show you my files and my “var log”.
Glad I could help. This is not the place to post a lot of code and logs. Can you go to stackoverflow and the FR3DLdapBundle tags and post there? I will look when I can. Here is the link: http://stackoverflow.com/questions/tagged/fr3dldapbundle
Thank you very much !!!! 😀 you’re my hero ! 😀
Also, if I don’t respond right away, let me know here that you’ve posted and maybe your Stackoverflow account name. I don’t check the FR3DLdapBundle tags very often.
Thanks to you I was able to get out and contact the server. However when I wanted to adapt the code to my LDAP server I encountered some difficulties. At first I could not contact him and although it is settling now, “I believe”, the problem is that he tells me that my IDs are invalid when I’m sure. I posted as you said a question on the site stackoverflow whose title is: “Why can not I log in with my LDAP credentials on Symfony3?” And my account name: Jérôme CHRISTOPHE.
I’ve responded to your question on Stackoverflow Jérôme.
Thanks for this article, it helped me a lot and it works for my Symfony3 project: when a user acces to my intranet, a login screen is shown, where the user enter an username and a password and the authentication is done as expected.
So, I have a question:
Is it possible to prevent the login screen (user and password) from being displayed and automatically authenticated with windows credentials? (The current user)
In case the authentication was not satisfactory, then display the Login screen.
(Note: the server is a Windows 2012 R2 server with wampserver)
Hi there Fran. Thanks for reviewing my article. Can you clarify what you mean by “automatically authenticated” ? How would the user enter their credentials? The way it works is if you enter your username/password, and as long as you don’t close the browser, your authentication is maintained when you’ve clicked the “Remember me” checkbox. If they close the browser, then they will need to re-authenticate. You can use the browser to “store” passwords too.
Hi, Alvin. Thanks for your answer.
What I need is that the browser does not ask for user credentials and password, but rather uses Windows authentication (the user who has been identified in Windows).
Following your article, the LDAP ID works perfectly, inserting in the browser the same user and password that is entered to access Windows, and I would like to know if it is possible to avoid this “double” identification.
Thank you.
Hi there Fran. You are talking about Windows Integrated Authentication. Try looking that up on Google. Apache does support it using the mod_authnz_ldap module. This is beyond the scope of this article. I found this link which should help you: http://www.held-im-ruhestand.de/software/apache-ldap-active-directory-authentication.html I have not tried this at all, but I think it might work for you.
Thanks Alvin!!
You really make it appear really easy with your presentation but I find this
matter to be actually something that I think I might never understand.
It seems too complex and extremely huge for me.
I’m looking ahead in your subsequent post,
I will attempt to get the grasp of it!
Stackoverflow is a place to post any questions. If you follow the article and do some experimentation, I’m sure you can get it working. I may write a future article.
J’ai besoin d’aide! j’ai fini l’installation du FOSUserBundle et FR3DldapBundle et ca fonctionne bien mais je veux que l’authetification n’enregistre pas automatiquement les utilisateur du ldap dans ma base de donne
Hi there Ce-noel. This won’t “register” users in LDAP. You’ve misunderstood how this all works. If you need to register users, you’ll need to use something else.
yes I do not know yet how this FR3ldapBundle really works but what happens is that after having an authentication the user who has just authenticated is automatically added to my database or I do not want it to do this automatic recording action. if you can give me an idea Alvin Bunk it’s cool
If you don’t want to manage users then you don’t need the FR2LdapBundle and you don’t need the FOSUserBundle. Take a look at my new article: https://alvinbunk.wordpress.com/2017/09/07/symfony-ldap-component-ad-authentication/
It make authentication easier.
Merci cette aide Alvin Bunk
J’ai utiliser FR3DldapBundle et l”authentifier avec ldap se fait avec rest api mais ca ne fonctionne pas! quelqu’un peut m’aider!
Quelqu’un peu m’aidé comment faire une auth API d’une application Symfony 3 avec une combinaison de 3 paquets: FR3DLdapBundle, LexikJwtAuthBundle et fosUserBundle.
I don’t speak French. Can you post your question on Stackoverfow?
Someone can help me do an API authentication of a Symfony 3 application with a combination of 3 packages: FR3DLdapBundle, LexikJwtAuthBundle and fosUserBundle.
Hi there Ce-noel. Can you post on Stackoverflow please: https://stackoverflow.com/questions/tagged/symfony
comment tester l’existance d’un utilisateur dans la base de donné et la verifier au près du base LDAP
how to test the existence of a user in the database and to check it at the LDAP database
If the name of your FOSUser Entity table is fos_user, then you would login to MySQL using “mysql -u -p” and then “use ” and then “describe fos_user” and then “SELECT FROM fos_user id,username,enabled,roles;”. You can also use Softerra’s LDAP Browser to browser LDAP.
I would like to do a search of the users with the ldap but I do not know how to proceed. someone can help me?
hi. works fine. thank you very much!
Hi,
thank you for your article!
I just tried with fr3ldap bundle because I need a manual authentication over LDPA server.
$user_manager = $this->get(‘fr3d_ldap.ldap_manager’);
$user = $user_manager->findUserByUsername($_username);
But it doesn’t work.
I opened a post on stack overflow with all the details:
https://stackoverflow.com/q…
Maybe you can help me?
thank you!
Sorry, bad explained before…what I need is to use this bundle with a manual authentication. Is it possible?
Please explain on Stackoverflow what you mean by manual authentication – make a good explanation – I don’t have much time!
Hello. Thanks for the article, that helped me with the setup process. I got everything to work with the online LDAP. However, when I proceeded to do a deployment on a client’s network, I am currently stuck on a weird situation where after the binding process is done correctly and a username is found but when proceeding to insert a record in a database, the username is null and thus a constraint violation is triggered. It would be grateful if you could look at my stack overflow post and help me with this problem.
https://stackoverflow.com/questions/48532329/login-with-fr3d-ldapbundle-fos-userbundle-user-found-but-on-db-insert-usernam
Hi there Rishi. I did have a quick look at your post on Stackoverflow. Is there a place that you can post your code for review? I suspect it is something to do with your FOSUser Entity from seeing the logs.
Hello Alvin. I have just updated my post with User class details at the bottom of the post. Let me know if you need more information.
everything works ok but after the first connection the second is KO because ldap tries to duplicate the user’s record on the local database
Hi there Jalal. What’s going on with the second connection? I don’t understand?
i have this message on the second connection :
An exception occurred while executing ‘INSERT INTO fos_user (username, username_canonical, email, email_canonical, enabled, salt, password, last_login, confirmation_token, password_requested_at, roles, dn, code_utilisateur) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)’ with params [“riemann”, “riemann”, “riemann@ldap.forumsys.com”, “riemann@ldap.forumsys.com”, 1, null, “”, “2018-05-23 21:26:15”, null, null, “a:1:{i:0;s:9:\”ROLE_USER\”;}”, “uid=riemann,dc=example,dc=com”, null]:
SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicata du champ ‘riemann@ldap.forumsys.com’ pour la clef ‘UNIQ_957A6479A0D96FBF’
Jalal, I think it’s because you changed from the LDAP test server on ldap.forumsys.com to your new configuration. Just go into MySQL and delete one of the two entries; or delete them both, and try to re-authenticate. I think that should solve it. Can you confirm?
I deleted the only entry from the fos_user table and when I reconnect it works but only for the first login, when I log out and try to log back in again it gives me the same error as if it is trying to insert at the places to update the user
You should post your config and question on stackoverflow not here!
Thank you very much 😀 😀
What part of this tutorial is responsible for linking LDAP to FOS user bundle? That is, what configuration setting is responsible for causing the user authentication success to populate the fos user table?
That is really how the FR3DLdapBundle works. I didn’t develop that bundle. Do you have some concern or issue in configuration?