Search code examples
ldaplexikjwtauthbundlesymfony-4.4

Using LexikJWTAuthenticationBundle with an LDAP Provider


I have a project that use Symfony API-Platform. I want to use LexikJWTAuthenticationBundle on my project but my users are stored in an Active Directory so I set an LDAP UserProvider. I tried to combine the setting of the two following documentation without success :

Here is what I have done

In security.yml :

security:
    providers:
        my_ldap:
            ldap:
                service:     Symfony\Component\Ldap\Ldap
                base_dn:     OU=organization_unit,DC=domain,DC=local
                search_dn:   "OU=organization_unit,DC=domain,DC=local"
                search_password: '%env(resolve:LDAP_PASSWORD)%'
                default_roles: ROLE_USER
                uid_key: uid
firewalls:
    login:
        pattern:  ^/api/login
        stateless: true
        anonymous: true
        json_login:
            provider: my_ldap
            check_path:               /api/login_check
            success_handler:          lexik_jwt_authentication.handler.authentication_success
            failure_handler:          lexik_jwt_authentication.handler.authentication_failure

    api: 
        pattern:   ^/api/
        stateless: true
        guard:
            provider: my_ldap
            authenticators:
                - lexik_jwt_authentication.jwt_token_authenticator

In services.yml :

Symfony\Component\Ldap\Ldap:
    arguments: ['@Symfony\Component\Ldap\Adapter\ExtLdap\Adapter']
Symfony\Component\Ldap\Adapter\ExtLdap\Adapter:
    arguments:
        -   host: '%env(resolve:LDAP_HOST)%'
            port: 636
            # encryption: tls
            options:
                protocol_version: 3
                referrals: false

But when I try to send the request to my_domain/api/login_check with following content :

{
  "username": "my_user",
  "password": "my_password"
}

I received this error as response:

{"code":401,"message":"Invalid credentials."}

Solution

  • I have resolved my issue by using a custom action that checks if the user exist using LDAP and get its roles. Then, if the freshly retrieved user is authorized to use the application, I create a new token and return it in the response. And finally the token is used to authentify the user for each call he do to the API.

    Here is my security.yml

    providers:
        # provider to retrieve user from LDAP
        my_ldap:
            id: App\Security\LdapService
        # provider to retrieve user from user
        jwt:
            lexik_jwt:
                class: App\Security\User
    firewalls:
        login:
            pattern:  ^/api/login
            stateless: true
            anonymous: true
    api: 
            pattern:   ^/api/
            stateless: true
            guard:
                provider: jwt
                authenticators:
                    - lexik_jwt_authentication.jwt_token_authenticator
    

    The login action in controller :

    /**
     * @Route("/api/login", name="api_login", methods={"POST"})
     */
    public function loginCheck(Request $request, LdapService $ldapService, AuthenticationSuccessHandler $authenticationSuccessHandler, AuthenticationFailureHandler $authenticationFailureHandler){
        $content = json_decode($request->getContent());
        $response= new JsonResponse();
        try{
            $user=$ldapService->authenticate($content->username, $content->password);
            return $authenticationSuccessHandler->handleAuthenticationSuccess($user);
        }catch(AuthenticationException $e){
            return $authenticationFailureHandler->onAuthenticationFailure($request, $e);
        }        
        return $response;    
    }