Symfony how to configure login in popup with AJAX request

127 Views Asked by At

Symfony >= 5.3

  • app isn't API

  • app isn't SPA

  • don't use FOSUserBundle

  • Do I need to set json_login setting in security.yaml?

  • Do I need to set custom_authenticator setting in security.yaml?

  • I tried via form_login and custom_authenticator but I don't need a redirect

  • I tried via json_login(without custom_authenticator) but I am not authenticated

1

There are 1 best solutions below

0
Dmitriy Lysenko On BEST ANSWER

I solved the problem I added class App\Security\ProfileLoginAuthenticator.php

class ProfileLoginAuthenticator extends Symfony\Component\Security\Http\Authenticator\AbstractLoginFormAuthenticator
{
    use Symfony\Component\Security\Http\Util\TargetPathTrait;

    public const LOGIN_ROUTE = 'profile_login';

    public function __construct(private UrlGeneratorInterface $urlGenerator, private TranslatorInterface $translator)
    {
    }

    public function authenticate(Request $request): Passport
    {
        $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->request->get('_csrf_token')),
                new RememberMeBadge(),
            ]
        );
    }

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

        return new JsonResponse([
            'success' => true,
            'targetPath' => $request->headers->get('referer'),
        ]);
    }

    public function onAuthenticationFailure(Request $request, AuthenticationException $exception): Response
    {
        if ($request->hasSession()) {
            $request->getSession()->set(Security::AUTHENTICATION_ERROR, $exception);
        }

        return new JsonResponse([
        'success' => false,
        'message' => $this->translator->trans($exception->getMessageKey(), $exception->getMessageData(), 'security'),
        ]);
    }

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

override methods onAuthenticationSuccess, onAuthenticationFailure, return in them JsonResponse

set this class in security.yaml, in section security->firewalls->custom_authenticator

security:
    enable_authenticator_manager: true
    password_hashers:
        App\Entity\User:
            algorithm: 'auto'
    providers:
        db_provider:
            entity:
                class: App\Entity\User
                property: email
    firewalls:
        ...
        profile:
            pattern: ^/
            provider: db_provider
            form_login:
                login_path: profile_login
                check_path: profile_login
            logout:
                path: profile_logout
                target: profile_login
            entry_point: form_login
            custom_authenticator: App\Security\ProfileLoginAuthenticator

JS code:

        let $loginForm = $('#login-form');
        $loginForm.on('submit', function (e) {
            e.preventDefault();
            e.stopPropagation();
            $.ajax({
                url: $loginForm.attr('action'),
                type: 'POST',
                data: $loginForm.serialize(),
                dataType: 'json',
                success: function (response) {
                    if (response.success) {
                        if (response.hasOwnProperty('targetPath')) {
                            window.location = response.targetPath;
                        }
                        $loginForm.find('.error').html('');
                    } else {
                        if (response.hasOwnProperty('message')) {
                            $loginForm.find('.error').html(response.message);
                        }
                    }
                }
            })
        });