Introduction

I previously wrote a very popular article called Symfony AD Integration which uses FOSUserBundle and FR3DLdapBundle, and I wanted to provide a simpler method that uses the Symfony LDAP Component. The main difference with this installation is that it simply authenticates against the server, no user information is stored or managed. If your application needs some type of user-to-application management, then you will need to look at using FOSUserBundle as well.

Documentation

The Symfony doc Authenticating against an LDAP server provides most of the details on the LDAP component and how it is used. I simply provide details here on my working example that authenticates to a Microsoft Active Directory (LDAP) server.

Login Controller

You’ll need to create a route with the path “/login” for your users to login and authenticate to. Below is example working code:

<?php
//	src/AppBundle/Controller/DefaultController.php

namespace AppBundle\Controller;

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;

class DefaultController extends Controller
{
	...
	/**
     * @Route("/login", name="login")
     */
	public function loginAction(Request $request, AuthenticationUtils $authUtils)
	{
		// get the login error if there is one
		$error = $authUtils->getLastAuthenticationError();
		
		// last username entered by the user
		$lastUsername = $authUtils->getLastUsername();
		
		return $this->render('security/login.html.twig', array(
			'last_username' => $lastUsername,
			'error'         => $error,
		));
	}
	...

Login Form Twig Code

Below is the Twig template code for the login form:

{# app/Resources/views/security/login.html.twig #}

{% extends 'base.html.twig' %}

{% block body %}
    {% if error %}
        <div>{{ error.messageKey|trans(error.messageData, 'security') }}</div>
    {% endif %}
    
    <form action="{{ path('login') }}" method="post">
        <label for="username">Username:</label>
        <input type="text" id="username" name="_username" value="{{ last_username }}" />
    
        <label for="password">Password:</label>
        <input type="password" id="password" name="_password" />
    
        {#
            If you want to control the URL the user
            is redirected to on success (more details below)
            <input type="hidden" name="_target_path" value="/account" />
        #}
    
        <button type="submit">login</button>
    </form>
{% endblock %}

{% block stylesheets %}
{% endblock %}

The above twig code just provides a username and password input fields, the form gets posted back to itself and if there are errors, they are shown in the error division tag.

Services YAML File

Below is the services.yml file. The main difference between this and the Symfony docs, is that for the dn_string, I only had to specify “MYDOMAIN\{username}”. The variable {username} is retrieved from the login form automatically. The prefix “MYDOMAIN\” is what you would normally put in for a domain name when you log in to Active Directory with a Windows computer. In this case “MYDOMAIN” is an example, and you should use what you would normally enter in your environment.

# app/config/services.yml
services:
    ...
    Symfony\Component\Ldap\Ldap:
        arguments: ['@Symfony\Component\Ldap\Adapter\ExtLdap\Adapter']
    Symfony\Component\Ldap\Adapter\ExtLdap\Adapter:
        arguments:
            -   host: 192.168.100.138
                port: 389
                #encryption: tls
                options:
                    protocol_version: 3
                    referrals: false

Notice that “encryption” is commented out. This was a major problem for me; so I suggest you comment it out first.

Security YAML File

The import parts of the security.yml file are the base_dn should include your domain’s “Users” CN (Common Name). You might want to download a copy of Softerra’s LDAP Browser – it’s free, and use this to figure out what you need to enter for your base_dn and search_dn. The search_dn will be an Active Directory account that is allowed to search throughout the directory. Then the search_password is the user account’s password. If you use the Softerra LDAP Browser, you would use the same account information to connect to and browse Active Directory.

#	app/config/security.yml
security:
    providers:
        my_ldap:
            ldap:
                service: Symfony\Component\Ldap\Ldap
                base_dn: 'CN=Users,DC=somedomain,DC=com'
                search_dn: 'CN=LDAP_ADMIN,CN=Users,DC=somedomain,DC=com'
                search_password: 'mypass'
                default_roles: ROLE_USER

    firewalls:
        # disables authentication for assets and the profiler, adapt it according to your needs
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false

        main:
            anonymous: ~
            
            form_login_ldap:
            #http_basic_ldap:
                login_path: login
                check_path: login
                service: Symfony\Component\Ldap\Ldap
                dn_string: 'MYDOMAIN\{username}'

    access_control:
      - { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
      #- { path: ^/, roles: IS_AUTHENTICATED_ANONYMOUSLY }
      - { path: ^/user_agent, roles: ROLE_USER }

In the above config, I set the path “/user_agent” to only allow authenticated users (ROLE_USER) to browse the path. So once you enter that path in your browser URL, you will be prompted to login. Then enter your user name (without the domain) and password, and you should get authenticated.

Notice in the security.yml file that I commented out “http_basic_ldap”. If you are having issues logging in, I suggest uncommenting this line and then commenting “form_login_ldap”. Then try logging in with only HTTP Basic authentication. What I found is the debug logs show a lot of useful information in this case. Logs are stored in the “var/logs” folder.