I want to implement form login with Symfony/Security to my app. I configured everything but it still doesn't work properly.
This is my security.yaml:
security:
providers:
sablon_users:
entity:
class: App\Entity\User
property: email
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
admin_login:
anonymous: true
pattern: ^/admin/auth
form_login:
login_path: security_login
check_path: security_login
csrf_token_generator: security.csrf.token_manager
default_target_path: /admin
admin:
pattern: ^/admin
guard:
authenticators:
- App\Security\LoginAuthenticator
provider: sablon_users
logout:
path: admin_logout
target: security_login
main:
pattern: ^/
anonymous: true
# activate different ways to authenticate
# http_basic: true
# https://symfony.com/doc/current/security.html#a-configuring-how-your-users-will-authenticate
# form_login: true
# https://symfony.com/doc/current/security/form_login_setup.html
# 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/auth/, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/admin, roles: IS_AUTHENTICATED_FULLY }
encoders:
App\Entity\User:
algorithm: bcrypt
cost: 12
my routes.yaml:
admin_logout:
path: /admin/auth/logout
admin:
type: annotation
resource: ../src/Controller/Admin
prefix: /admin
site:
type: annotation
resource: ../src/Controller/Site
LoginAuthenticator.php:
class LoginAuthenticator extends AbstractFormLoginAuthenticator
{
/**
* @var EntityManagerInterface
*/
private $em;
/**
* @var RouterInterface
*/
private $router;
/**
* @var UserPasswordEncoderInterface
*/
private $encoder;
/**
* @var CsrfTokenManagerInterface
*/
private $csrfTokenManager;
/**
* @var TokenStorageInterface
*/
private $tokenStorage;
/**
* LoginAuthenticator constructor.
*
* @param EntityManagerInterface $em
* @param CsrfTokenManagerInterface $csrfTokenManager
* @param RouterInterface $router
* @param TokenStorageInterface $tokenStorage
* @param UserPasswordEncoderInterface $encoder
*/
public function __construct(
EntityManagerInterface $em,
CsrfTokenManagerInterface $csrfTokenManager,
RouterInterface $router,
TokenStorageInterface $tokenStorage,
UserPasswordEncoderInterface $encoder
)
{
$this->em = $em;
$this->csrfTokenManager = $csrfTokenManager;
$this->router = $router;
$this->tokenStorage = $tokenStorage;
$this->encoder = $encoder;
}
/**
* @param Request $request
* @return bool
*/
/**
* @return mixed
*/
public function getCurrentUser()
{
$token = $this->tokenStorage->getToken();
return $token->getUser();
}
/**
* Does the authenticator support the given Request?
*
* If this returns false, the authenticator will be skipped.
*
* @param Request $request
*
* @return bool
*/
public function supports(Request $request)
{
$isLoginSubmitRequest = $request->getPathInfo() === 'admin/auth/login' && $request->isMethod('POST');
if(!$isLoginSubmitRequest){
return false;
}
return true;
}
/**
* @param Request $request
*
* @return mixed Any non-null value
*
* @throws \UnexpectedValueException If null is returned
*/
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;
}
/**
* @param mixed $credentials
* @param UserProviderInterface $userProvider
* @return User|null|object|UserInterface
*/
public function getUser($credentials, UserProviderInterface $userProvider)
{
$token = new CsrfToken('authenticate', $credentials['csrf_token']);
if (!$this->csrfTokenManager->isTokenValid($token)) {
throw new InvalidCsrfTokenException();
}
$user = $this->em->getRepository(User::class)->findOneBy(['email' => $credentials['email']]);
if (!$user) {
// fail authentication with a custom error
throw new Exception('Email could not be found.');
}
return $user;
}
/**
* @param mixed $credentials
* @param UserInterface $user
*
* @return bool
*
* @throws AuthenticationException
*/
public function checkCredentials($credentials, UserInterface $user)
{
$password = $credentials['password'];
return $this->encoder->isPasswordValid($user,$password);
}
/**
*
* @param Request $request
* @param AuthenticationException $exception
*
* @return Response|null
*/
public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
{
$request->getSession()->set(Security::AUTHENTICATION_ERROR, $exception);
return new RedirectResponse($this->router->generate('security_login'));
}
/**
* @param Request $request
* @param TokenInterface $token
* @param string $providerKey The provider (i.e. firewall) key
*
* @return Response|null
*/
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
return new RedirectResponse($this->router->generate('app_homepage'));
}
/**
* @return bool
*/
public function supportsRememberMe()
{
return false;
}
/**
* Return the URL to the login page.
*
* @return string
*/
protected function getLoginUrl()
{
return $this->router->generate('security_login');
}
When i try to login with correct credentials. It loads User and even show the username on the _profiler as authenticated (Token class: UsernamePasswordToken). It shows the corresponding roles for the user.
However when i try to navigate '/admin' area, it redirects me to login page. It doesn't show any error in page or console.
My dev.log shows the flow though:
- [2019-02-13 13:58:01] request.INFO: Matched route "app_homepage". {"route":"app_homepage","route_parameters":{"_controller":"Symfony\Bundle\FrameworkBundle\Controller\RedirectController::urlRedirectAction","path":"/admin/","permanent":true,"scheme":null,"httpPort":8000,"httpsPort":443,"_route":"app_homepage"},"request_uri":"http://localhost:8000/admin","method":"GET"} [] [2019-02-13 13:58:01] security.DEBUG: Checking for guard authentication credentials. {"firewall_key":"admin","authenticators":1} []
- [2019-02-13 13:58:01] security.DEBUG: Checking support on guard authenticator. {"firewall_key":"admin","authenticator":"App\Security\LoginAuthenticator"} []
- [2019-02-13 13:58:01] security.DEBUG: Guard authenticator does not support the request. {"firewall_key":"admin","authenticator":"App\Security\LoginAuthenticator"} []
- [2019-02-13 13:58:01] security.INFO: An AuthenticationException was thrown; redirecting to authentication entry point. {"exception":"[object] (Symfony\Component\Security\Core\Exception\AuthenticationCredentialsNotFoundException(code: 0): A Token was not found in the TokenStorage. at /home/vagrant/Code/project_name/vendor/symfony/security-http/Firewall/AccessListener.php:51)"} []
- [2019-02-13 13:58:01] security.DEBUG: Calling Authentication entry point. [] []