Symfony simple Login form with the new authentication method not working

Viewed 2916

I am having trouble setting up a new symfony application and I am sure its something about the new Authenticator-based Security system.

  1. I installed a fresh symfony application, version 5.3.6.
  2. Installed the security bundle composer require symfony/security-bundle(https://symfony.com/doc/current/security.html). Followed all the steps there.
  3. After that I wanted to build a simple login form following this guide: https://symfony.com/doc/current/security/form_login_setup.html. I executed the command php bin/console make:auth which generated all the files and updated my security.yml, as per usual. Here I notice that the command did not generate the Guard authenticator(since I understand its depracaded), but the new one(https://symfony.com/doc/current/security/authenticator_manager.html).
  4. After this I go to my /login page, type the credentials and submit the form. The page reloads and nothing. No error message and I am still not authenticated. I did not do any additional steps, since it should work as it is? Well at least the old Guard authentication worked like that. However this new authentication system seems to not work. Am I missing something?

MY FILES:

LoginFormAuthenticator.php

class LoginFormAuthenticator extends AbstractLoginFormAuthenticator{

use TargetPathTrait;

public const LOGIN_ROUTE = 'app_login';

private UrlGeneratorInterface $urlGenerator;

public function __construct(UrlGeneratorInterface $urlGenerator)
{
    $this->urlGenerator = $urlGenerator;
}

public function authenticate(Request $request): PassportInterface
{
    $email = $request->request->get('email', '');

    $request->getSession()->set(Security::LAST_USERNAME, $email);

    return new Passport(
        new UserBadge($email),
        new PasswordCredentials($request->request->get('password', '')),
        [
            new CsrfTokenBadge('authenticate', $request->get('_csrf_token')),
        ]
    );
}

public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
{
    if ($targetPath = $this->getTargetPath($request->getSession(), $firewallName)) {
        return new RedirectResponse($targetPath);
    }

    // For example:
    return new RedirectResponse($this->urlGenerator->generate('dashboard'));
}

protected function getLoginUrl(Request $request): string
{
    return $this->urlGenerator->generate(self::LOGIN_ROUTE);
}

}

SecurityController.php

class SecurityController extends AbstractController{

/**
 * @Route("/login", name="app_login")
 */
public function login(AuthenticationUtils $authenticationUtils): Response
{
    // if ($this->getUser()) {
    //     return $this->redirectToRoute('target_path');
    // }

    // get the login error if there is one
    $error = $authenticationUtils->getLastAuthenticationError();
    // last username entered by the user
    $lastUsername = $authenticationUtils->getLastUsername();

    return $this->render('security/login.html.twig', ['last_username' => $lastUsername, 'error' => $error]);
}

/**
 * @Route("/logout", name="app_logout")
 */
public function logout()
{
    throw new \LogicException('This method can be blank - it will be intercepted by the logout key on your firewall.');
}

}

security.yml

security:
# https://symfony.com/doc/current/security/experimental_authenticators.html
enable_authenticator_manager: true
# https://symfony.com/doc/current/security.html#c-hashing-passwords
password_hashers:
    Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto'
    App\Entity\User:
        algorithm: auto

# https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
providers:
    # used to reload user from session & other features (e.g. switch_user)
    app_user_provider:
        entity:
            class: App\Entity\User
            property: email
firewalls:
    dev:
        pattern: ^/(_(profiler|wdt)|css|images|js)/
        security: false
    main:
        lazy: true
        provider: app_user_provider
        custom_authenticator: App\Security\LoginFormAuthenticator
        logout:
            path: app_logout
            # where to redirect after logout
            # target: app_any_route

        # 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: ^/admin, roles: ROLE_ADMIN }
    # - { path: ^/profile, roles: ROLE_USER }
1 Answers

The problem was found in the AbstractLoginFormAuthenticator class supports() method. The method checks if the getLoginUrl() method returns full URI, while the getPathInfo() gives the extra path information after the URI. I had to override the supports() method and thange getPathInfo() with getRequestUri() and it worked.

public function supports(Request $request): bool
{
    return $request->isMethod('POST') && $this->getLoginUrl($request) === $request->getRequestUri();
}
Related