phpauthenticationsymfonygraphqlsymfony4

The App\Security\LoginFormAuthenticator::getUser() method must return a UserInterface. You returned Softonic\GraphQL\Response


I'm trying to customize the login with a graphql query to load the User data and it shows me that error.

enter image description here

I use php bin / console make: auth and it works correctly when I query the $ user variable from MySQL but when I load the $ user variable from GRAPHQL it shows me that error.

This is the code:

public function getUser($credentials, UserProviderInterface $userProvider)
{

    if ($credentials["csrf_token"] != "") {
        $client = new GH();
        $headers = ['Content-Type' => 'application/x-www-form-urlencoded'];
        $result = $client->request('POST', 'https://graphql.clientecliente.com/get-token', [
            'json' => [
                'usuario' => $credentials["email"],
                'clave' => $credentials["password"]
            ]
        ]);
        $data = $result->getBody()->getContents();
        $objetodata=(json_decode($data));
        $token = $objetodata->token;
    }
    //Conexion servidor GraphQL
    $servidor = $this->params->get('enlace_graphql');
    //Codigo debe salir desde la base de datos
    $codigoAuth = $token;

    $options = [
        'headers' => [
            'Authorization' => 'Bearer ' . $codigoAuth,
        ],
    ];
    $client = \Softonic\GraphQL\ClientBuilder::build($servidor, $options);

$gpl1 = <<<'QUERY'
query ($limit: Int!, $offset: Int!, $CODIGO_USUARIO: String) {
    obt_usuarios(limit: $limit, offset: $offset, CODIGO_USUARIO: $CODIGO_USUARIO) {
      totalCount
      OBT_USUARIOS {
        CODIGO_USUARIO
        CLAVE_USUARIO
        CODIGO_EMPRESA
        CODIGO_CLIENTE
        CODIGO_PASAJERO
        ES_ADMINISTRADOR
      }
    }
  }
QUERY;

$variables1 = [
    'limit' => 10,
    'offset' => 0,
    'CODIGO_USUARIO' => $credentials["email"]
];

    //$user = $this->entityManager->getRepository(Usuario::class)->findOneBy(['email' => $credentials['email']]);
    $user = $client->query($gpl1,$variables1);

    if (!$user) {
        // fail authentication with a custom error
        throw new CustomUserMessageAuthenticationException('El usuario no existe');
    }

    return $user;
}

Maybe, Anything idea?


Update. Now create the custom user provider. I use this command: php bin/console make:user;

<?php
//src/Security/UserProvider.php
namespace App\Security;

use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;

//Consulta API
use GuzzleHttp\Client as GH;
use GuzzleHttp\Pool;
//Consulta GraphQL
use Softonic\GraphQL;
use Softonic\GraphQL\Client;
use Softonic\GraphQL\ClientBuilder;
use function GuzzleHttp\json_decode;

class UserProvider implements UserProviderInterface
{
    /**
     * Symfony calls this method if you use features like switch_user
     * or remember_me.
     *
     * If you're not using these features, you do not need to implement
     * this method.
     *
     * @return UserInterface
     *
     * @throws UsernameNotFoundException if the user is not found
     */
    public function loadUserByUsername($username)
    {
        $client = new GH();
            $headers = ['Content-Type' => 'application/x-www-form-urlencoded'];
            $result = $client->request('POST', 'https://graphql.cliente.com.ec/get-token', [
                'json' => [
                    'usuario' => 'test',
                    'clave' => 'test1234',

                ]
            ]);
            $data = $result->getBody()->getContents();
            $objetodata=(json_decode($data));
            //var_dump($objetodata->token);
            //exit;
            $token = $objetodata->token;

            $servidor = "https://graphql.cliente.com/graphql";         
            //Codigo debe salir desde la base de datos
            //$codigoAuth = $token;
            $codigoAuth= "12345678912345678mlvIjoiT0JUX0NjIxOTI1ODN9.8dNiZI6iZsYS0plVU0fuqFlhkTDSOt9OFy5B-WZiRmk";
            //var_dump($codigoAuth);
            //exit;
            //echo $codigoAuth; exit; 
        $options = [
            'headers' => [
                'Authorization' => 'Bearer ' . $codigoAuth,
            ],
        ];
        $client = \Softonic\GraphQL\ClientBuilder::build($servidor, $options);

$gpl1 = <<<'QUERY'
query ($limit: Int!, $offset: Int!, $CODIGO_USUARIO: String) {
    obt_usuarios(limit: $limit, offset: $offset, CODIGO_USUARIO: $CODIGO_USUARIO) {
      totalCount
      OBT_USUARIOS {
        CODIGO_USUARIO
        CLAVE_USUARIO
        CODIGO_EMPRESA
        CODIGO_CLIENTE
        CODIGO_PASAJERO
        ES_ADMINISTRADOR
      }
    }
  }
QUERY;

$variables1 = [
    'limit' => 10,
    'offset' => 0,
    'CODIGO_USUARIO' => 'test'
];

$user=$client->query($gpl1,$variables1);
//var_dump($user);exit;

        //$username=$user;
        return $user;      
        //throw new \Exception('TODO: fill in loadUserByUsername() inside '.__FILE__);
    }

    /**
     * Refreshes the user after being reloaded from the session.
     *
     * When a user is logged in, at the beginning of each request, the
     * User object is loaded from the session and then this method is
     * called. Your job is to make sure the user's data is still fresh by,
     * for example, re-querying for fresh User data.
     *
     * If your firewall is "stateless: true" (for a pure API), this
     * method is not called.
     *
     * @return UserInterface
     */
    public function refreshUser(UserInterface $user)
    {
        if (!$user instanceof User) {
            throw new UnsupportedUserException(sprintf('Invalid user class "%s".', get_class($user)));
        }

        // Return a User object after making sure its data is "fresh".
        // Or throw a UsernameNotFoundException if the user no longer exists.
        throw new \Exception('TODO: fill in refreshUser() inside '.__FILE__);
        return $this->loadUserByUsername($user->getUsername());

    }

    /**
     * Tells Symfony to use this provider for this User class.
     */
    public function supportsClass($class)
    {
        return User::class === $class;
    }
}

config/packages/security.yaml:

providers:
    app_user_provider:
        id: App\Security\UserProvider



firewalls:
    secured_area:
        anonymous: true
        form_login:
            login_path:  loginanterior
            check_path:  login_check
            default_target_path: index
            provider: app_user_provider
            remember_me: true

    logout:
        path:   logout
        target: index

    remember_me:
        secret:   '%kernel.secret%'
        lifetime: 604800 # 1 week in seconds
        path:     /

src/Security/User.php

<?php

namespace App\Security;

use Symfony\Component\Security\Core\User\UserInterface;

class User implements UserInterface
{
    private $email;

    private $roles = [];

    /**
     * @var string The hashed password
     */
    private $password;

    public function getEmail(): ?string
    {
        return $this->email;
    }

    public function setEmail(string $email): self
    {
        $this->email = $email;

        return $this;
    }

    /**
     * A visual identifier that represents this user.
     *
     * @see UserInterface
     */
    public function getUsername(): string
    {
        return (string) $this->email;
    }

    /**
     * @see UserInterface
     */
    public function getRoles(): array
    {
        $roles = $this->roles;
        // guarantee every user at least has ROLE_USER
        $roles[] = 'ROLE_USER';

        return array_unique($roles);
    }

    public function setRoles(array $roles): self
    {
        $this->roles = $roles;

        return $this;
    }

    /**
     * @see UserInterface
     */
    public function getPassword(): string
    {
        return (string) $this->password;
    }

    public function setPassword(string $password): self
    {
        $this->password = $password;

        return $this;
    }

    /**
     * @see UserInterface
     */
    public function getSalt()
    {
        // not needed when using the "bcrypt" algorithm in security.yaml
    }

    /**
     * @see UserInterface
     */
    public function eraseCredentials()
    {
        // If you store any temporary, sensitive data on the user, clear it here
        // $this->plainPassword = null;
    }
}

However, after this it does not show me any errors or anything. Just go back to the login again


Solution

  • You have to map your Softonic\GraphQL\Response to your User model. This should be done in your custom UserProvider so Authenticator doesn't know where the user actually comes from. You can also look at existing user providers code for inspiration.