Search code examples
symfony4symfony-securityphp-7.2symfony-flex

Symfony Guard login never authenticates


I'm using guard as my authentication layer for my symfony 4 flex app.

whenever I enter in my username and password it automatically redirects me to the login page, no errors just redirects me. In my logs it shows that I couldn't log in but the form isn't showing any of that.

I'm new to symfony in terms of using their full stack, switched from silex to symfony. I've been looking at the documents on symfony and tutorials and nothing is working.

security.yml

security:
    access_control:
        - { path: ^/admin/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/admin, roles: ['ROLE_ADMIN', 'ROLE_SUPERUSER'] }
    role_hierarchy:
        ROLE_ADMIN:       ROLE_USER
        ROLE_SUPER_ADMIN: [ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
    encoders:
        App\Entities\User:
            algorithm: bcrypt
            cost:      15
    providers:
        admin:
            id: App\Security\UserProviders\AdminUserServiceProvider
    firewalls:
        admin:
            anonymous: ~
            provider: admin
            pattern: ^/admin/
            logout:
                path: /admin/logout
                target: /admin/login
                invalidate_session: true
            guard:
                authenticators:
                    - App\Security\Authenticators\AdminAuthenticator
        main:
            anonymous: ~
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false
            form_login: ~

SecurityController.php

class AuthController extends Controller
{
    /**
    * @Route("/admin/login", name="adm_login")
    * @param Request $request
    * @return Response
    */
    public function adminLoginAction(Request $request) : Response
    {
        $utils = $this->get('security.authentication_utils');


        return $this->render('Auth/adminLogin.twig', [
            'PageTitle' => 'Login',
            'error' => $utils->getLastAuthenticationError()
        ]);
    }

    /**
    * @Route("/admin/authenticate", name="security_login_check")
    */
    public function loginCheckAction()
    {

    }

    /**
    * @Route("/admin/logout", name="logout")
    */
    public function logoutAction()
    {

    }
}

adminLogin.twig

{% extends 'Shared/admin-login.twig' %}

{% block headJavascript %}
    <script type="application/javascript" src="{{ absolute_url('/assets/dependencies/jquery-validation/js/jquery.validate.min.js') }}"></script>
{% endblock %}

{% block loginForm %}
    <div class="login-box">
        <div class="login-logo">
            Admin Login
        </div>

        <div class="login-box-body">
            {% if error %}
                <div class="alert alert-danger" role="alert">
                    <span class="fa fa-exclamation-circle"></span>&nbsp;{{ error.messageKey }}
                </div>
            {% endif %}

            <p class="login-box-msg">Sign in to start your session</p>

            <form role="form" id="frmLogin" action="{{ path('security_login_check') }}" method="post">
                <div class="form-group has-feedback">
                    <label for="eml" class="sr-only">Email Address</label>
                    <input type="email" id="eml" name="_username" class="form-control" placeholder="Email Address">
                    <span class="fa fa-envelope form-control-feedback"></span>
                </div>
                <div class="form-group has-feedback">
                    <label for="pwd" class="sr-only">Password</label>
                    <input type="password" id="pwd" name="_password" class="form-control" placeholder="Password">
                    <span class="fa fa-lock form-control-feedback"></span>
                </div>
                <div class="row">
                    <div class="col-xs-8">
                        &nbsp;
                    </div>
                    <div class="col-xs-4">
                        <input type="hidden" name="_csrf_token" value="{{ csrf_token('frmLogin') }}">
                        <button type="submit" class="btn btn-primary btn-block btn-flat">
                            <span class="fa fa-sign-in"></span> Sign In
                        </button>
                    </div>
                </div>
            </form>
            <a href="{{ path('AccountRecovery') }}">I forgot my password</a><br>
        </div>
    </div>
{% endblock %}

{% block bottomJavascript %}
    <script type="application/javascript" src="{{ absolute_url('/assets/dependencies/jquery-validation/js/jquery.validate.min.js') }}"></script>
    <script type="application/javascript" src="{{ absolute_url('/assets/js/lib/auth/admin-login.js') }}"></script>
{% endblock %}

AdminAuthenticator.php

class AdminAuthenticator extends AbstractFormLoginAuthenticator
{
    private $encoderFactory;
    private $csrfTokenManager;
    private $userService;
    private $failMessage = 'Invalid credentials';
    private $loginUrl = '/admin/login';
    private $successUrl = '/admin/';

    public function __construct(UserService $userService, EncoderFactoryInterface $encoderFactory, CsrfTokenManagerInterface $csrfTokenManager)
    {
        $this->userService = $userService;
        $this->encoderFactory = $encoderFactory;
        $this->csrfTokenManager = $csrfTokenManager;
    }

    public function getCredentials(Request $request)
    {
        if ($request->getPathInfo() !== '/admin/authenticate'|| !$request->isMethod('POST'))
        {
            return null;
        }

        $csrfToken = $request->request->filter('_csrf_token', null, FILTER_SANITIZE_STRING);

        if (false === $this->csrfTokenManager->isTokenValid(new CsrfToken('authenticate', $csrfToken))) {
            throw new InvalidCsrfTokenException('Invalid session token.');
        }

        return [
            'username' => $request->request->filter('_username', null, FILTER_SANITIZE_STRING),
            'password' => $request->request->filter('_password', null, FILTER_SANITIZE_STRING),
        ];
    }
    public function getUser($credentials, UserProviderInterface $userProvider) : ?UserInterface
    {
        try
        {
            if (!$userProvider instanceof AdminUserServiceProvider)
            {
                throw new CustomUserMessageAuthenticationException('Invalid Provider');
            }

            return $userProvider->loadUserByUsername($credentials['username']);
        }
        catch (UsernameNotFoundException $e)
        {
            throw new CustomUserMessageAuthenticationException($e->getMessage());
        }
    }

    public function checkCredentials($credentials, UserInterface $user) : bool
    {
        $encoder = $this->encoderFactory->getEncoder($user);

        if ($encoder->isPasswordValid($user->getPassword(), $credentials['password'], $user->getSalt()))
        {
            return true;
        }

        throw new CustomUserMessageAuthenticationException($this->failMessage);
    }
    public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey) : ?Response
    {
        return new RedirectResponse($this->successUrl);
    }

    public function supportsRememberMe() : bool
    {
        return false;
    }

    protected function getLoginUrl() : string
    {
        return $this->loginUrl;
    }

    public function supports(Request $request) : ?bool
    {
        return $request->headers->has('_username') && $request->headers->has('_password');
    }
}

Error Log:

[2018-05-15 23:04:38] request.INFO: Matched route "security_login_check". {"route":"security_login_check","route_parameters":{"_controller":"App\\Controller\\AuthController::loginCheckAction","_route":"security_login_check"},"request_uri":"http://dev.local.com/admin/authenticate","method":"POST"} []
[2018-05-15 23:04:38] security.DEBUG: Checking for guard authentication credentials. {"firewall_key":"admin","authenticators":1} []
[2018-05-15 23:04:38] security.DEBUG: Calling getCredentials() on guard configurator. {"firewall_key":"admin","authenticator":"App\\Security\\Authenticators\\AdminAuthenticator"} []
[2018-05-15 23:04:38] security.INFO: Populated the TokenStorage with an anonymous Token. [] []
[2018-05-15 23:04:38] security.DEBUG: Access denied, the user is not fully authenticated; redirecting to authentication entry point. {"exception":"[object] (Symfony\\Component\\Security\\Core\\Exception\\AccessDeniedException(code: 403): Access Denied. at /var/www/Websites/dev.local.com/vendor/symfony/security/Http/Firewall/AccessListener.php:68)"} []
[2018-05-15 23:04:38] security.DEBUG: Calling Authentication entry point. [] []
[2018-05-15 23:04:38] request.INFO: Matched route "adm_login". {"route":"adm_login","route_parameters":{"_controller":"App\\Controller\\AuthController::adminLoginAction","_route":"adm_login"},"request_uri":"http://dev.local.com/site-admin/login","method":"GET"} []
[2018-05-15 23:04:38] security.DEBUG: Checking for guard authentication credentials. {"firewall_key":"admin","authenticators":1} []
[2018-05-15 23:04:38] security.DEBUG: Calling getCredentials() on guard configurator. {"firewall_key":"admin","authenticator":"App\\Security\\Authenticators\\AdminAuthenticator"} []
[2018-05-15 23:04:38] security.INFO: Populated the TokenStorage with an anonymous Token. [] []

Solution

  • You have made a few mistake here

    • The check of the request url and the method should be in the supports method.
    • You are looking for headers instead of request vars in your supports method.

    Replace it by the following.

    public function supports(Request $request) : ?bool
    {
        return $request->request->has('_username') && $request->request->has('_password');
    }