Reputation: 43
I am trying to work with Symfony 5 and the security component and I am blocked on a bug. the bug is on authentification success after redirection, $this->getUser()
return null but at the same time, the
development panel show me my role (which is store in database)
my security.yaml:
security:
# https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
providers:
in_database:
entity:
class: App\Entity\User
property: email
role_hierarchy:
ROLE_CLIENT: ROLE_USER
ROLE_ADMIN: ROLE_CLIENT
ROLE_DEV: [ROLE_CLIENT, ROLE_ADMIN]
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
anonymous: true
provider: in_database
form_login:
check_path: app_login
login_path: app_login
default_target_path: /test
username_parameter: email
password_parameter: password
csrf_parameter: _csrf_security_token
csrf_token_id: a_private_string
logout:
path: app_logout
target: app_login
# 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: ^/backoffice, roles: ROLE_ADMIN }
- { path: ^/profile, roles: ROLE_USER }
encoders:
App\Entity\User: plaintext
And I did not modify the User entity.
edit
Following advices, I added those lines in my security.yaml:
guard:
authenticators:
- App\Security\LoginFormAuthentificator
And the LoginFormAuthentificator file (sorry it will be long)
<?php
namespace App\Security;
use App\Entity\User;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;
use Symfony\Component\Security\Core\Exception\InvalidCsrfTokenException;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Csrf\CsrfToken;
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
use Symfony\Component\Security\Guard\Authenticator\AbstractFormLoginAuthenticator;
use Symfony\Component\Security\Http\Util\TargetPathTrait;
class LoginFormAuthentificator extends AbstractFormLoginAuthenticator {
use TargetPathTrait;
private $entityManager;
private $router;
private $csrfTokenManager;
private $passwordEncoder;
public function __construct(EntityManagerInterface $entityManager, RouterInterface $router, CsrfTokenManagerInterface $csrfTokenManager, UserPasswordEncoderInterface $passwordEncoder){
$this->entityManager = $entityManager;
$this->router = $router;
$this->csrfTokenManager = $csrfTokenManager;
$this->passwordEncoder = $passwordEncoder;
}
public function supports(Request $request){
return 'app_login' === $request->attributes->get('_route') && $request->isMethod('POST');
}
public function getCredentials(Request $request){
$credentials = ['email' => $request->request->get('email'), 'password' => $request->request->get('password'), 'csrf_token' => $request->request->get('_csrf_token'),];
$request->getSession()->set(Security::LAST_USERNAME, $credentials['email']);
return $credentials;
}
public function getUser($credentials, UserProviderInterface $userProvider){
$token = new CsrfToken('authenticate', $credentials['csrf_token']);
if(!$this->csrfTokenManager->isTokenValid($token)){
throw new InvalidCsrfTokenException();
}
$user = $this->entityManager->getRepository(User::class)->findOneBy(['email' => $credentials['email']]);
if(!$user){
// fail authentication with a custom error
throw new CustomUserMessageAuthenticationException('Email could not be found.');
}
return $user;
}
public function checkCredentials($credentials, UserInterface $user){
return $this->passwordEncoder->isPasswordValid($user, $credentials['password']);
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey){
if($targetPath = $this->getTargetPath($request->getSession(), $providerKey)){
return new RedirectResponse($targetPath);
}
return new RedirectResponse($this->router->generate('BackOffice_home_index'));
// throw new \Exception('TODO: provide a valid redirect inside '.__FILE__);
}
protected function getLoginUrl(){
return $this->router->generate('app_login');
}
}
Thank you so much for help and time.
Upvotes: 1
Views: 1213
Reputation: 809
After some debugging, I found that you implement Serializable
interface to your User
class. With this approach, methods from this interface should returns correct data, because User
object will be serialized to the session by these methods. In your case methods, serialize
and unserialize
were empty, which causing null value stored in session. So you should remove Serializable
interface and it's methods from your User
class (in this case application store some default fields in session), or implement at least minimal functionality to this methods:
public function serialize(){
return serialize([
$this->id,
$this->email,
$this->password
]);
}
public function unserialize($serialized){
list(
$this->id,
$this->email,
$this->password
) = unserialize($serialized, ['allowed_classes' => false]);
}
also, add return value to getUsername
method implemented from UserInterface
, e.g.
public function getUsername(){
return $this->email;
}
More information here and here
Upvotes: 3