UXerUIer
UXerUIer

Reputation: 2338

Null returned for HttpFoundation/response

I'm getting the error in Symfony 4:

Return value of App\Controller\RegistrationCreatorController::register() must be an instance of Symfony\Component\HttpFoundation\Response, null returned

Within the RegistrationCreatorController I have this:

namespace App\Controller;

use App\Entity\User;
use App\Entity\Creator;
use App\Entity\CreatorApplication;
use App\Form\CreatorRegistrationForm;
use App\Security\UserAuthenticationAuthenticator;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use Symfony\Component\Security\Guard\GuardAuthenticatorHandler;

class RegistrationCreatorController extends AbstractController
{
        public function register(
                Request $request,
                UserPasswordEncoderInterface $passwordEncoder,
                GuardAuthenticatorHandler $guardHandler,               
                UserAuthenticationAuthenticator $authenticator
        ): Response
    {
        $user        = new User();
        $creator     = new Creator();
        $application = new CreatorApplication();
        $form        = $this
        ->createForm(
            CreatorRegistrationForm::class,
            [$user, $creator, $application]
        );

        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {
            $user
            ->setEmail(
                $form->get('email')->getData()
            )
            ->setPassword(
                $passwordEncoder->encodePassword(
                    $user,
                    $form->get('password')->getData()
                )
            )
            ->setFirstName(
                $form->get('firstname')->getData()
            )
            ->setLastName(
                $form->get('lastname')->getData()
            )
            ->setBirthday(
                $form->get('birthday')->getData()
            );

            $application->createApplication(
                $form->get('question-1')->getData(),
                $form->get('question-2')->getData(),
                $form->get('question-3')->getData(),
                $form->get('question-4')->getData()
            );

            $creator
            ->associateUser($user)
            ->associateApplication($application);

            $entityManager = $this->getDoctrine()->getManager();
            $entityManager->persist($user);
            $entityManager->persist($creator);
            $entityManager->persist($application);
            $entityManager->flush(); //this is what inserts to db!

            // do anything else you need here, like send an

            return $guardHandler->authenticateUserAndHandleSuccess(
                $user,
                $request,
                $authenticator,
                'main' // This is the issue here // 
            );
        }

        return $this->render('registration/creator-register.html.twig', [
            'creatorForm' => $form->createView(),
        ]);
    }
}

The error specifically points to this:

return $guardHandler->authenticateUserAndHandleSuccess(
       $user,
       $request,
       $authenticator,
       'main' // This is the even more specific issue // 
);

I'm not entirely sure what the problem is? When I look into the security.yaml file it points to the userAuthenticationAuthenticator which was auto generated from Symfony 4 when I called the make:registration-form.

UserAuthenticationAuthenticator:

<?php

namespace App\Security;

use App\Entity\User;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use Symfony\Component\Security\Core\Exception\InvalidCsrfTokenException;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Csrf\CsrfToken;
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
use Symfony\Component\Security\Guard\Authenticator\AbstractFormLoginAuthenticator;
use Symfony\Component\Security\Http\Util\TargetPathTrait;

class UserAuthenticationAuthenticator extends AbstractFormLoginAuthenticator
{
    use TargetPathTrait;

    private $entityManager;
    private $urlGenerator;
    private $csrfTokenManager;
    private $passwordEncoder;

    public function __construct(EntityManagerInterface $entityManager, UrlGeneratorInterface $urlGenerator, CsrfTokenManagerInterface $csrfTokenManager, UserPasswordEncoderInterface $passwordEncoder)
    {
        $this->entityManager = $entityManager;
        $this->urlGenerator = $urlGenerator;
        $this->csrfTokenManager = $csrfTokenManager;
        $this->passwordEncoder = $passwordEncoder;
    }

    public function supports(Request $request)
    {
        return 'app_login' === $request->attributes->get('_route')
            && $request->isMethod('POST');
    }

    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;
    }

    public function getUser($credentials, UserProviderInterface $userProvider)
    {
        $token = new CsrfToken('authenticate', $credentials['csrf_token']);
        if (!$this->csrfTokenManager->isTokenValid($token)) {
            throw new InvalidCsrfTokenException();
        }

        $user = $this->entityManager->getRepository(User::class)->findOneBy(['email' => $credentials['email']]);

        if (!$user) {
            // fail authentication with a custom error
            throw new CustomUserMessageAuthenticationException('Email could not be found.');
        }

        return $user;
    }

    public function checkCredentials($credentials, UserInterface $user)
    {
        return $this->passwordEncoder->isPasswordValid($user, $credentials['password']);
    }

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

        // For example : return new RedirectResponse($this->urlGenerator->generate('some_route'));
        //throw new \Exception('TODO: provide a valid redirect inside '.__FILE__);
    }

    protected function getLoginUrl()
    {
        return $this->urlGenerator->generate('app_login');
    }
}
?>

I thought the guard handler would return a response, but I guess not?

Upvotes: 2

Views: 8393

Answers (1)

Jakumi
Jakumi

Reputation: 8374

the doc comment tells, that it returns a response if any

/**
 * Convenience method for authenticating the user and returning the
 * Response *if any* for success.
 */

https://github.com/symfony/symfony/blob/d97f9ab131ae1fbc3c4371f2a38b8c1e41eef499/src/Symfony/Component/Security/Guard/GuardAuthenticatorHandler.php#L84

and it might even throw an Exception...

So no, a Response is not necessarily the result. Especially if you read the AuthenticatorInterface

a pragmatic solution would be to just redirect to the index page (/) in case no Response object is returned:

return $guardhandler->authenticateUserAndHandleSuccess(...) 
    ?: new RedirectResponse('/'); // fallback

Upvotes: 2

Related Questions