I have been assigned the task to improve the authentication system on a symfony-based website. The details don't matter, what's important is that the new Guard component (introduced in Symfony 2.8) is very well fitted to the task.
I have the following problem: the guard DO manage to authenticate the user & redirect to the homepage, but the access to the homepage is refused (AuthenticationExpiredException is thrown) and thus the user is redirected back to the login. From the user POV, it seems that the login always fail.
The problem appear even with a very simple project :
/* Not writing getters, setters and annotations here for readability */
class CustomUser implements UserInterface, \Serializable {
private $id;
protected $username;
protected $salt;
protected $password;
public function __construct() {
$this->salt = md5(uniqid(null, true));
}
public function serialize() {
return serialize(array($this->id,));
}
public function unserialize($serialized) {
list($this->id,) = unserialize($serialized);
}
}
The Guard class:
class CustomGuardAuthenticator extends AbstractGuardAuthenticator {
private $container;
private $router;
public function __construct(ContainerInterface $container) {
$this->container = $container;
$this->router = $container->get('router');
}
public function start(Request $request, AuthenticationException $authException = null) {
$response = new RedirectResponse($this->router->generate('login'));
return $response;
}
public function getCredentials(Request $request) {
if ($request->getPathInfo() != '/login_check' || !$request->isMethod('POST')) {
return null;
}
return array(
'username' => $request->request->get('_username'),
'password' => $request->request->get('_password'),
);
}
public function getUser($credentials, UserProviderInterface $userProvider) {
$user = $userProvider->loadUserByUsername($credentials['username']); /* No problem here, user correctly fetched */
return $user;
}
public function checkCredentials($credentials, UserInterface $user) {
$encoder = $this->container->get('security.password_encoder');
if (!$encoder->isPasswordValid($user, $credentials['password'])) { /* No problem here either */
throw new BadCredentialsException();
}
return true;
}
public function onAuthenticationFailure(Request $request, AuthenticationException $exception) {
$response = new RedirectResponse($this->router->generate('login'));
return $response;
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey) {
/* Debugging show that we DO reach that */
return new RedirectResponse($this->router->generate('homepage'));
}
public function supportsRememberMe() {
return false;
}
}
security.yml:
security:
encoders:
AppBundle\Entity\CustomUser:
algorithm: sha1
encode_as_base64: false
iterations: 1
providers:
main:
entity: {class: AppBundle\Entity\CustomUser, property: username}
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
pattern: ^/
anonymous: true
guard:
authenticators:
- security.custom.guard
access_control:
- { path: ^/security/*, roles: IS_AUTHENTICATED_ANONYMOUSLY}
- { path: ^/, roles: [ROLE_USER] }
I'd like to avoid having to use one of those 3 workarounds. But most of all I'd like understand what is the problem, and not feel like the two last days were wasted!
Any help would be appreciated :)