Skip to content

Invalid (or unclear to me) handling of data returned from MessageHandler::__invoke() #2550

Description

@Perf

Hello,
it seems handling of data returned from __invoke() method of class that implements MessageHandlerInterface is not correct.

Sending request to /users/password-reset-confirmation.

Expected behavior:
Api platform should return a response object that was returned from __invoke().
In this particular case I'm trying to return JWTAuthenticationSuccessResponse which extends JsonResponse.

Current behavior:

Uncaught PHP Exception Symfony\Component\HttpKernel\Exception\ControllerDoesNotReturnResponseException: "The controller must return a "Symfony\Component\HttpFoundation\Response" object but it returned an object of type App\Dto\User\UserPasswordResetConfirmation." at /srv/vendor/api-platform/core/src/Action/PlaceholderAction.php line 32

DTO class UserPasswordResetConfirmation.php:

namespace App\Dto\User;

use App\Validator as AppAssert;
use Symfony\Component\Validator\Constraints as Assert;

final class UserPasswordResetConfirmation
{
    /**
     * @var string
     * @Assert\NotBlank()
     * @AppAssert\IsHash()
     */
    public $passwordResetToken;
    /**
     * @var string
     * @Assert\NotBlank()
     * @AppAssert\PlainPassword()
     */
    public $plainPassword;
}

API Resource config user_password_reset_confirmation.yaml:

App\Dto\User\UserPasswordResetConfirmation:
    attributes:
        messenger: true
        output: false
    itemOperations: {}
    collectionOperations:
        post:
            access_control: 'is_anonymous()'
            path: '/users/password-reset-confirmation'
            status: 200
            swagger_context:
                tags: ['User']
                summary: 'Confirms and sets new password'
                responses:
                    200:
                        description: 'Successfully set new password, returning a new JWT token'
                        schema:
                            type: 'object'
                            properties:
                                token:
                                    type: 'string'
                                    description: 'JWT authentication token'
                    400:
                        description: 'Invalid input'
                    404:
                        description: 'Resource not found'

Message handler class UserPasswordResetConfirmationHandler.php:

namespace App\Handler;

use App\Dto\User\UserPasswordResetConfirmation;
use App\Event\UserEvent;
use App\Service\User\UserService;
use App\Traits\EventDispatcherable;
use Lexik\Bundle\JWTAuthenticationBundle\Security\Http\Authentication\AuthenticationSuccessHandler;
use Symfony\Component\Messenger\Handler\MessageHandlerInterface;

class UserPasswordResetConfirmationHandler implements MessageHandlerInterface
{
    use EventDispatcherable;

    /**
     * @var UserService
     */
    private $userService;
    /**
     * @var AuthenticationSuccessHandler
     */
    private $authenticationHandler;

    public function __construct(UserService $userService, AuthenticationSuccessHandler $authenticationHandler)
    {
        $this->userService = $userService;
        $this->authenticationHandler = $authenticationHandler;
    }

    public function __invoke(UserPasswordResetConfirmation $dto)
    {
        $user = $this->userService->confirmPasswordReset($dto);
        $this->dispatch(UserEvent::PASSWORD_RESET_CONFIRMED, new UserEvent($user));

        // creating a new JWT token
        $response = $this->authenticationHandler->handleAuthenticationSuccess($user);

        return $response;
    }
}

Debugging showed that event is populated with response object vendor/api-platform/core/src/EventListener/WriteListener.php at lines 60-66:

$persistResult = $this->dataPersister->persist($controllerResult);
//
$event->setControllerResult($persistResult ?? $controllerResult);

but later on somewhere controller result is overwritten with DTO object UserPasswordResetConfirmation, than becomes a cause of an exception.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions