I can't get the user from JWT - symfony

I have a project with Symfony 5.1 using Lexik JWT v2.8.0, gesdinet jwt refresh token v0.9.1 and my own entity user. I can log in with JWT and get the token, save it in a HttpOnly cookie and use it with the protected APIs successfully.
My web app has some API Rest but it has also pages to browse. So my intention is do it all from API, but also login in the web browser when user obtains a token.
But as api login is stateless, after the success login the web profiler still shows logged in as anon. and I can't get te user from the token. I made a service to get the user from the token, to call it in the controller and send some logged in user data to the front end.
I've got a service from this question. The service I have implemented to get the user is:
use Lexik\Bundle\JWTAuthenticationBundle\Services\JWTTokenManagerInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use UserBundle\Domain\Entity\User;
class UserService
{
private TokenStorageInterface $tokenStorage;
private JWTTokenManagerInterface $jwtManager;
public function __construct( TokenStorageInterface $storage, JWTTokenManagerInterface $jwtManager )
{
$this->tokenStorage = $storage;
$this->jwtManager = $jwtManager;
}
public function getCurrentUser() : ?User
{
$decodedJwtToken = $this->jwtManager->decode($this->tokenStorage->getToken());
if ($decodedJwtToken instanceof TokenInterface) {
$user = $decodedJwtToken->getUser();
return $user;
} else {
return null;
}
}
}
And it is declared as a service:
get.token.user.service:
class: UserBundle\Domain\Service\UserService
arguments: [ '#security.token_storage' ]
But all I get from $this->tokenStorage->getToken() is a web token with "anon." user, so it is not a JWT and jwtManager can't decode it.
UserService.php on line 25:
Symfony\Component\Security\Core\Authentication\Token\AnonymousToken {#16 ▼
-secret: "RXVaVx3"
-user: "anon."
-roleNames: []
-authenticated: true
-attributes: []
}
I also tried to get the jwt from the cookie in the controller and send it to the service as an argument, but I get an error from decode that I'm passing a string and it expects a TokenInterface object, so it did not worked neither.
How can I get the user from the token in the service? is there a best practice to login in the web through api than get the user from the jwt and send it to the render?
Edit: add the code to use lexik jwt and to save token in cookie:
# /config/packages/lexik_jwt_authentication.yaml
lexik_jwt_authentication:
secret_key: '%env(resolve:JWT_SECRET_KEY)%'
public_key: '%env(resolve:JWT_PUBLIC_KEY)%'
pass_phrase: '%env(JWT_PASSPHRASE)%'
token_ttl: 3600
user_identity_field: email
token_extractors:
cookie:
enabled: true
name: BEARER
the security file code
# /config/packages/security.yaml
security:
encoders:
UserBundle\Domain\Entity\User:
algorithm: auto
providers:
app_user_provider:
entity:
class: UserBundle\Domain\Entity\User
property: email
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
login:
pattern: ^/api/login
stateless: true
anonymous: true
json_login:
provider: app_user_provider
check_path: /api/login_check
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
refresh:
pattern: ^/api/token/refresh
stateless: true
anonymous: true
register:
pattern: ^/api/register
stateless: true
anonymous: true
api:
pattern: ^/api
stateless: true
anonymous: true
provider: app_user_provider
guard:
authenticators:
- lexik_jwt_authentication.jwt_token_authenticator
main:
anonymous: true
lazy: true
provider: app_user_provider
access_control:
- { path: ^/api/user, roles: IS_AUTHENTICATED_ANONYMOUSLY, methods: [GET] }
- { path: ^/api/linkpage, roles: IS_AUTHENTICATED_ANONYMOUSLY, methods: [GET] }
- { path: ^/api/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/api/token/refresh, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/api/register, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/api, roles: IS_AUTHENTICATED_FULLY }
Listener to save jwt in a cookie and avoid the token to be in the response:
use Lexik\Bundle\JWTAuthenticationBundle\Event\AuthenticationSuccessEvent;
use Symfony\Component\HttpFoundation\Cookie;
class AuthenticationSuccessListener
{
private bool $secure = false;
private int $tokenTtl;
public function __construct(int $tokenTtl)
{
$this->tokenTtl = $tokenTtl;
}
public function onAuthenticationSuccess( AuthenticationSuccessEvent $event)
{
$response = $event->getResponse();
$data = $event->getData();
$token = $data['token'];
unset($data['token']); // remove token from response. It works.
unset($data['refresh_token']); // remove token from refresh token, even though I still get this token in the response
$event->setData($data);
$response->headers->setCookie(
new Cookie(
'BEARER',
$token,
(new \DateTime())->add(new \DateInterval('PT'. $this->tokenTtl . 'S')),
'/',
null,
$this->secure
)
);
}
}
#services.yaml
app.listener.authenticationsuccesslistener:
class: UserBundle\Application\Listeners\AuthenticationSuccessListener
arguments: ['%lexik_jwt_authentication.token_ttl%']
tags:
- { name: kernel.event_listener, event: lexik_jwt_authentication.on_authentication_success, method: onAuthenticationSuccess }
I still coded 2 more listeners for the refresh token, but I don't think they are needed.
The rest of the authentication is the default authentication code from lexik jwt bundle.
In the BEARER cookie is stored the jwt with the correct format, something like this:
eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpYXQiOjE1OTgzNTE0ODYsImV4cCI6MTU5ODM1NTA4Niwicm9sZXMiOlsiUk9MRV9BRE1JTiIsIlJPTEVfVVNFUiJdLCJlbWFpbCI6ImFkbWluQGVtYWlsLmNvbSJ9.p5WYCxEE-VSxp09Eo7CxXoxi6zy1ZcnLJiBe1YGsrk3iFm7T-6JAWbvyb9ZW_-1jtpYcWQlFOjOf7uET4wRHvlvygnPOeZck7tZM8TUlSqMXIllMeVARz8mEUvXVwhDWEk5T7Ibw9c3VgfvyiLUgSb_cmSyK3DwtgPCd9vOYarkag9XKxkxNI9M1OHGL61v1NoQHdkXloC72xdUUMcj5Y8yCJWZFdOp8-Xtfq8ZChAHzwCjXUhC3VnBqZcMT0tAvtilwTGDYDykppNKK1vbNoyOex47wQH_ILEFuX5Eh1p2xfbc0lWm3Ip21z3EQ2M_eOQgZvHR65T3b2dv9g5GPiFp3CNo8AuW8m6rXjWK6NZXJO8qYodxI5cUYYyFooCfVXXU8JXzGQfCZIdOPw-iBGzQEfFuLL50_sAOVcxjklAYCFZYRHwFKWmwl1BwJF4mAw4jnNIAdMmc66Z17ul2Jep9apOO90C1dZzGuVKxWqglc9GZo7-teHt0dMsg0ADrvaOKNJUwDBGJ4lZpWx6_stcl7DkCdc5k1kePGdLa7YXRO3umPwa_FVYVgnT_Z9x7RtfnGioa2TZJCIdbJnuj0L90vkgFBjHqFdVydDaaBu3Y0mKoQ2v3Sf1so4-uwJm8z1vQVZleMQgFibMiyyk3YyDidhPSxxyp4u-4xPNOSDNo
If I am not logged in and don't have jwt or I delete the BEARER cookie I can't access the protected APIs ({"code": 401, "message": "JWT Token not found"}), when I have the jwt assigned I can request APIs correctly.

The problem was on the security.yaml configuration, specifically in the firewalls.main section, where I had to add the jwt_token_authenticator as the guard. Finally, my security.yaml is:
security:
encoders:
UserBundle\Domain\Entity\User:
algorithm: auto
providers:
app_user_provider:
entity:
class: UserBundle\Domain\Entity\User
property: email
jwt:
lexik_jwt: ~
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
login:
pattern: ^/api/login
stateless: true
anonymous: true
json_login:
provider: app_user_provider
check_path: /api/login_check
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
require_previous_session: false
refresh:
pattern: ^/api/token/refresh
stateless: true
anonymous: true
register:
pattern: ^/api/register
stateless: true
anonymous: true
api:
pattern: ^/api
stateless: true
anonymous: true
provider: jwt
guard:
authenticators:
- lexik_jwt_authentication.jwt_token_authenticator
main:
anonymous: true
lazy: true
provider: jwt
guard:
authenticators:
- lexik_jwt_authentication.jwt_token_authenticator
logout:
path: app_logout
target: /
access_control:
- { path: ^/api/login_check, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/api/token/refresh, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/api/register, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/logout, roles: IS_AUTHENTICATED_FULLY }
- { path: ^/api, roles: IS_AUTHENTICATED_FULLY }
Then I had some problems with the logout, as the logout didn't erase the jwt cookies and the logout kept me logged in. So I made a Listener to the LogoutEvent and made the listener delete the cookies.
use Symfony\Component\Security\Http\Event\LogoutEvent;
class LogoutListener
{
public function onSymfonyComponentSecurityHttpEventLogoutEvent(LogoutEvent $event)
{
$response = $event->getResponse();
$response->headers->clearCookie('BEARER');
$response->headers->clearCookie('REFRESH_TOKEN');
$event->setResponse($response);
}
}
and declared it as a service:
app.listener.logout:
class: UserBundle\Application\Listeners\LogoutListener
tags:
- name: 'kernel.event_listener'
event: 'Symfony\Component\Security\Http\Event\LogoutEvent'
dispatcher: security.event_dispatcher.main
And now the login is working on the web without having to decode the jwt with the service I was using and pass the user to the front. Though the service that decoded the jwt, now is working fine.
I've lost almost a week with this issue, but finally I've found a solution and it's working fine. So I'll post it here to save time if anybody has some similar issue.

Related

jwt lexik JWT Authetication not found

I have been looking for a solution for a while now.
All authentication system is in place, I get my token but when I use it to retrieve the data I get a 404 jwt not found.
I have an emergency on this project, a help will help me a lot.
this is my security.yaml:
app_user_provider:
entity:
class: App\Entity\User
property: email
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
login:
pattern: ^/api/login
stateless: true
json_login:
check_path: /api/login_check # or api_login_check as defined in config/routes.yaml
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
api:
pattern: ^/api
stateless: true
guard:
authenticators:
- lexik_jwt_authentication.jwt_token_authenticator
main:
lazy: true
provider: app_user_provider
# activate different ways to authenticate
# https://symfony.com/doc/current/security.html#the-firewall
# https://symfony.com/doc/current/security/impersonating_user.html
# switch_user: true
# Easy way to control access for large sections of your site
# Note: Only the *first* access control that matches will be used
access_control:
# - { path: ^/admin, roles: ROLE_ADMIN }
# - { path: ^/profile, roles: ROLE_USER }
- { path: ^/api/login, roles: PUBLIC_ACCESS }
- { path: ^/api, roles: IS_AUTHENTICATED_FULLY }
Pour la declaration du controller:
api_login_check:
path: /api/login_check
You have to do the following;
go to /config/packages/lexik_jwt_authentication.yaml
lexik_jwt_authentication:
secret_key: '%env(resolve:JWT_SECRET_KEY)%'
public_key: '%env(resolve:JWT_PUBLIC_KEY)%'
pass_phrase: '%env(JWT_PASSPHRASE)%'
token_extractors:
authorization_header:
enabled: true
prefix: Bearer
name: Authorization
cookie: // if you are using cookie
enabled: true
name: cookie_name // set the cookie name
If you are using httpClient; then you can Authorization: 'Bearer ' .$token in your header as follows;
$headers = [
'Authorization' => 'Bearer '.$token,
'Content-Type' => 'application/json',
];
NOTE: make sure you have jwt directory, where you have private and public keys.

I get an invalid token?

I work under a Docker project. Symfony 5.
the registration goes well, I have the user registered in the database
the connection goes well, I get the token
there is a record line in the auth table with the token during the connection
on the other hand the access to a protected route it indicates me that the token is not valid
GET http://localhost:8003/api/users/13
Authorization Bearer ey..............................................................
{
"code": 401,
"message": "Invalid JWT Token"
}
security.yml
security:
encoders:
App\Entity\User:
algorithm: bcrypt
providers:
app_user_provider:
entity:
class: App\Entity\User
property: email
firewalls:
refresh:
pattern: ^/api/token/refresh
stateless: true
anonymous: true
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
doc:
pattern: ^/doc
security: false
login:
pattern: ^/api/login
stateless: true
anonymous: true
json_login:
username_path: email
check_path: /api/login/token
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
user_register:
pattern: ^/api/user/register
stateless: true
anonymous: true
main:
pattern: ^/api
stateless: true
anonymous: false
provider: app_user_provider
guard:
authenticators:
- lexik_jwt_authentication.jwt_token_authenticator
access_control:
- { path: ^/api/token/refresh, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/api/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/api/user/register, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/api/, roles: IS_AUTHENTICATED_FULLY }
lexik_jwt_authentication.yaml
lexik_jwt_authentication:
private_key_path: "%kernel.project_dir%/%env(JWT_PRIVATE_KEY_PATH)%"
public_key_path: "%kernel.project_dir%/%env(JWT_PUBLIC_KEY_PATH)%"
pass_phrase: "%env(JWT_PASSPHRASE)%"
token_ttl: 3600 # token TTL in seconds, defaults to 1 hour
user_identity_field: email
clock_skew: 0
encoder:
service: lexik_jwt_authentication.encoder.lcobucci
signature_algorithm: RS256
token_extractors:
authorization_header:
enabled: true
prefix: Bearer
name: Authorization
cookie:
enabled: false
name: BEARER
query_parameter:
enabled: false
name: bearer
I have created the 2 certificates with the right passphrase :
.env
...
JWT_SECRET_KEY=%kernel.project_dir%/config/jwt/private.pem
JWT_PUBLIC_KEY=%kernel.project_dir%/config/jwt/public.pem
JWT_PASSPHRASE=f8bfe4494b7cf3032d642a3e72dcac53
/config/jwt/private.pem
/config/jwt/public.pem
\vendor\Lexik\Bundle\JWTAuthenticationBundle\Security\Guard\JWTTokenAuthenticator.php
public function getCredentials(Request $request)
{
$tokenExtractor = $this->getTokenExtractor();
if (!$tokenExtractor instanceof TokenExtractorInterface) {
throw new \RuntimeException(sprintf('Method "%s::getTokenExtractor()" must return an instance of "%s".', __CLASS__, TokenExtractorInterface::class));
}
if (false === ($jsonWebToken = $tokenExtractor->extract($request))) {
return;
}
$preAuthToken = new PreAuthenticationJWTUserToken($jsonWebToken);
try {
dump($preAuthToken); // --------------
dump($this->jwtManager->decode($preAuthToken)); // ERROR HERE
// --------------
if (!$payload = $this->jwtManager->decode($preAuthToken)) {
throw new InvalidTokenException('Invalid JWT Token');
}
$preAuthToken->setPayload($payload);
} catch (JWTDecodeFailureException $e) {
if (JWTDecodeFailureException::EXPIRED_TOKEN === $e->getReason()) {
$expiredTokenException = new ExpiredTokenException();
$expiredTokenException->setToken($preAuthToken);
throw $expiredTokenException;
}
throw new InvalidTokenException('Invalid JWT Token', 0, $e);
}
return $preAuthToken;
}
dump($preAuthToken);
JWTTokenAuthenticator.php on line 106:
Lexik\Bundle\JWTAuthenticationBundle\Security\Authentication\Token\PreAuthenticationJWTUserToken {#598
-rawToken: "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyaWQiOjIyLCJ1c2VybmFtZSI6InRvdG8xMUB0b3RvLmZyIiwiaWF0IjoxNjIyMDI3NzQ4fQ.COr_fuXAH8iq3Ecr8mJVIVVdLI6H5zv7419gvQwLy6Q"
-payload: null
-credentials: null
-guardProviderKey: null
-user: null
-roleNames: []
-authenticated: false
-attributes: []
}
it's the right token passed in the request
an error occurs on this line: $this->jwtManager->decode($preAuthToken)
and triggers: new InvalidTokenException('Invalid JWT Token', 0, $e);
Which version are you using of LexikJWTAuthentication ? I guess the latest with symfony 5, you should use param public_key instead of public_key_path : https://github.com/lexik/LexikJWTAuthenticationBundle/blob/master/Resources/doc/1-configuration-reference.md

How to create JWT token in Symfony5?

I'm trying to create REST API with JWT authentification in Symfony5. At first I've tried to do as written here https://h-benkachoud.medium.com/symfony-rest-api-without-fosrestbundle-using-jwt-authentication-part-2-be394d0924dd . Difficulties appeared when I've tried to make method getTokenUser:
/**
* #param JWTTokenManagerInterface $JWTManager
* #return JsonResponse
* #Route("/api/login_check", name="api_login_check", methods={"POST"})
*/
public function getTokenUser(UserInterface $user,JWTTokenManagerInterface $JWTManager)
{
return new JsonResponse(['token' => $JWTManager->create($user)]);
}
Symfony says that UserInterface is not service so it can't Autowire it.
Ok, then I've tried to find another articles about this problem. But surprisingly they just doesn't say how to write this method. For example, here https://digitalfortress.tech/php/jwt-authentication-with-symfony/ appears like route /api/login_check must work authomatically if it configured in security.yaml and in routes.yaml. But no, it doesn't work.
So how must I write controller?
My security.yaml is:
security:
encoders:
App\Entity\User:
algorithm: bcrypt
# https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
providers:
app_user_provider:
entity:
class: App\Entity\User
property: email
firewalls:
login:
pattern: ^/api/login
stateless: true
anonymous: true
json_login:
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
provider: app_user_provider
guard:
authenticators:
- lexik_jwt_authentication.jwt_token_authenticator
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
anonymous: true
# activate different ways to authenticate
# https://symfony.com/doc/current/security.html#firewalls-authentication
# https://symfony.com/doc/current/security/impersonating_user.html
# switch_user: true
# Easy way to control access for large sections of your site
# Note: Only the *first* access control that matches will be used
access_control:
- { path: ^/register, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/api/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/api, roles: IS_AUTHENTICATED_FULLY }
it's true you do not need this method. You should refer to the documentation of the bundle you are using:
https://github.com/lexik/LexikJWTAuthenticationBundle/blob/2.x/Resources/doc/index.rst
In fact the login URL is handled by the security component of Symfony and rely on the configuration you set under json_login key :) .
Once again, it's detailed in the documentation (section "Usage").

LexikJWTAuthenticationBundle always get null user over token storage

I'm working with symfony at backend (api). The authentication process is handled by FosUserBundle, LexikJWTAuthenticationBundle and LdapTools... all works fine.
The problem is when I'm about to get the Authenticated user in a controller or service.
The user is authenticated by the Authorization header, does not exist 401 Exception
$this->container->get('security.token_storage')->getToken()->getUser()//null
$preAuthToken = $this->container->get('security.token_storage')->getToken();
$tmp = $this->container->get('lexik_jwt_authentication.jwt_manager')->decode($preAuthToken);//i can get the username and roles
But the real proble is with the security system
if ($this->isGranted('ROLE_USER')) {
echo 'never gets here!!';
} else {
echo 'always';
}
The security system always fails because the user returned by getUser() is always null.
My question is: the LexikJWTAuthenticationBundle should not inject or replace the current user, token after a successfull authentication?
or should I do it programatically? I dont want to fall in bad practices..
thanks in advance!
security.yml info
security:
encoders:
FOS\UserBundle\Model\UserInterface: bcrypt
LdapTools\Bundle\LdapToolsBundle\Security\User\LdapUser: plaintext
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: ROLE_ADMIN
providers:
ldap:
id: ldap_tools.security.user.ldap_user_provider
fos_userbundle:
id: fos_user.user_provider.username_email
firewalls:
refresh:
pattern: ^/api/token/refresh
stateless: true
anonymous: true
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
api_login:
pattern: ^/login
stateless: true
provider: fos_userbundle
anonymous: true
form_login:
check_path: /login
require_previous_session: false
username_parameter: username
password_parameter: password
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: AppBundle\Handler\AuthenticationFailureHandler
require_previous_session: false
guard:
authenticators:
- ldap_tools.security.ldap_guard_authenticator
logout: true
api:
pattern: ^/
stateless: true
lexik_jwt: ~
access_control:
- { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/, role: IS_AUTHENTICATED_FULLY }
Auth. failure handler(just in case)
public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
{
$token = $exception->getToken();
if (is_string($exception->getToken()->getUser())) {
$usuario = $this->container->get('fos_user.user_manager')->findUserByUsername($token->getUsername());
if ($usuario) {
$token = new UsernamePasswordToken($usuario, 'yes', 'public', $usuario->getRoles());
} else {
return $this->container->get('lexik_jwt_authentication.handler.authentication_failure')->onAuthenticationFailure($request, $exception);
}
}
return $this->handleAuthenticationFail($request, $token, $exception);
}
public function handleAuthenticationFail(Request $request, TokenInterface $token, AuthenticationException $exception)
{
$username = $token->getUsername();
$password = $request->get('password');
if ($this->ldapManager->authenticate($username, $password)) {
return $this->container->get('lexik_jwt_authentication.handler.authentication_success')->handleAuthenticationSuccess($token->getUser());
}
return $this->container->get('lexik_jwt_authentication.handler.authentication_failure')->onAuthenticationFailure($request, $exception);
}

Checking a user's password on Ldap with dynamic dn in symfony 3

With sf3
symfony 3 password can be checked but with a static dn
dn_string: 'uid={username},dc=example,dc=com'
So I created my own UserAuthenticationProvider class
But now I don't know how to declare it in my security.yml ?
security.yml
providers:
your_db_provider:
entity:
class: AcSecurityBundle:User
property: username
fos_userbundle:
id: fos_user.user_provider.username
secured_area:
pattern: ^/
anonymous: true
provider: your_db_provider
form_login:
csrf_token_generator: security.csrf.token_manager
use_referer: true
form_login_ldap:
service: ldap
use_referer: true
logout: true
AcSecurityBundle/AcUserAuthenticationProvider.php
class AcUserAuthenticationProvider extends UserAuthenticationProvider
{
function checkAuthentication(UserInterface $user, UsernamePasswordToken $token)
{
$username = $token->getUsername();
$password = $token->getCredentials();
if ('' === $password) {
throw new BadCredentialsException('The presented password must not be empty.');
}
try {
$query = "(sAMAccountName=$username)";
$search_dn = "CN=app,OU=apps,DC=domain,DC=be";
$search_password = "mdp";
$this->ldap->bind($search_dn, $search_password);
$t = $this->ldap->query("OU=At,OU=user,DC=domain,DC=be", $query);
$entry = $t->execute();
if ($entry->count() > 0) {
$dn = $entry[0]->getDn();
}
$this->ldap->bind($dn, $password);
} catch (ConnectionException $e) {
throw new BadCredentialsException('The presented password is invalid.');
}
}
}
Thank you
Since Symfony 3.1 Symfony uses a new ldap provider
Symfony\Component\Ldap\Ldap
Provider Example:
services:
app.ldap:
class: Symfony\Component\Ldap\Ldap
factory: [ 'Symfony\Component\Ldap\Ldap', 'create']
arguments:
- ext_ldap
- host: srv-host
#debug: true
The string associated to the adapter you wish to use. Only one is
supported for now, which is ext_ldap.
New Ldap provider by Symfony, explained.
Security Provider Example:
providers:
#webservice_provider:
#id: app.webservice_user_provider
company_ldap:
ldap:
service: app.ldap
base_dn: DC=example, DC=local
search_dn: "CN=AdminName,OU=YourAdminGroup,DC=example,DC=LOCAL"
search_password: 'AdminPass'
default_roles: ROLE_USER
Now you are allowed to look in your ldap system. If you set your
form_login_ldap right, your users can authenticate via your form
Security form_login_ldap Example:
form_login_ldap:
provider: company_ldap
service: app.ldap #system/provider your user authenticate to
dn_string: '{username}#example.local'
csrf_token_generator: security.csrf.token_manager
check_path: /login_check #make sure the check is behind the firewall
login_path: /login #make sure login IS_AUTHENTICATED_ANONYMOUSLY
logout:
path: /logout #logout path
target: /login #after logout redirect to login
anonymous: true
Access_control/firewall Example:
access_control:
- { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/admin/, role: ROLE_ADMIN }
- { path: ^/, role: ROLE_USER } #all pages require user to be logged in
Now you have your dynamic ldap login form.
Symfony login form setup.
Hope this helps you. Took me 8 days to figure this out by myself.
You don't need to do any of the above. You can use the DN string as:
dn_string: '{username}'
That's actually the default value according to the docs.This will work because AD supports several bind methods in addition to a full DN (such as the sAMAccountName or UPN).

Resources