Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Api-Platform User Login via GraphQL

I am creating an API using API-Platform and have set up my user entity etc using the standard symfony security bundle (https://symfony.com/doc/current/security.html#retrieving-the-user-object)

I have the login working with REST at {url}/api/login using JWT but I cannot see any way of sending my login details with GraphQL

The API-platform documentation shows how to set up security and how to setup GraphQL separately but doesn't really show how to combine them.

https://api-platform.com/docs/core/graphql

https://api-platform.com/docs/core/fosuser-bundle

How do I make the login accessible in GraphQL? Currently, I only have the createUser updateUser and deleteUser mutations, I assume I would need an authenticateUser one?

like image 306
GiantJelly Avatar asked Nov 17 '22 22:11

GiantJelly


1 Answers

Yes, you'll need a custom mutation for the login.

Assuming you are using the API Platform standard docs for the API, you are using JWT to authenticate your calls, you need a UserMutationResolver for auth:

<?php

namespace App\Resolver;

use ApiPlatform\Core\GraphQl\Resolver\MutationResolverInterface;
use App\Entity\User;
use Lexik\Bundle\JWTAuthenticationBundle\Services\JWTTokenManagerInterface;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use Doctrine\ORM\EntityManagerInterface;

final class UserMutationResolver implements MutationResolverInterface
{
    public function __construct(
        private UserPasswordEncoderInterface $userPasswordEncoder,
        private JWTTokenManagerInterface $JWTManager,
        private EntityManagerInterface $em,
    )
    {}

    /**
     * @param User|null $item
     *
     * @return User
     */
    public function __invoke($item, array $context)
    {
        // Mutation input arguments are in $context['args']['input'].

        if ($context["info"]->fieldName == 'loginUser') {
            $userRepository = $this->em->getRepository("App:User");
            $user = $userRepository->findOneBy(['email' => $item->getEmail()]);
            if ($this->userPasswordEncoder->isPasswordValid($user, $item->getPlainPassword())) {
                $token = $this->JWTManager->create($item);
                $user->setToken($token);
            }
            return $user;
        }
    }
}

Then you add that custom mutation to the User entity. Be sure to add the names of the auto-generated mutations/queries or they will disappear (item_query, create, update, delete, collection_query). You'll also need to disable some of the stages, since this is a mutation Api Platform will try and save this as a new user, which we don't want, so as you see below, 'write' => false and 'validate' => false

// api/src/Entity/User.php

// imports etc .
// ...

#[ApiResource(
    normalizationContext: ["groups" => "user:read"],
    denormalizationContext: ["groups" => "user:write"],
    attributes: [
        'write' => true,
    ],
    graphql: [
        'item_query',
        'create',
        'update',
        'delete',
        'collection_query',
        'login' => [
            'mutation' => UserMutationResolver::class,
            'write' => false,
            'validate' => false, 
            'args' => [
                'email' => ['type' => 'String!', 'description'=> 'Email of the user ' ],
                'password' => ['type' => 'String!', 'description'=> 'Password of the user ' ]
            ]
        ],
    ],
    iri:"http://schema.org/Person",
)]
#[UniqueEntity(fields: ["email"])]
class User implements UserInterface
{
// ...

This will create a mutation that you can access like this:

mutation {
  loginUser(input: {email:"[email protected]", password:"password"})  {
    user {
      token
    }
  }
}

and the result should look something like this:

{
  "data": {
    "loginUser": {
      "user": {
        "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpYXQiOjE2MTgzNjM1NDQsImV4cCI6MTYxODM2NzE0NCwicm9sZXMiOlsiUk9MRV9VU0VSIl0sInVzZXJuYW1lIjoidGVzdDFAdGVzdC5jb20ifQ.pSoAyNcaPa4MH2cxaAM4LJOGvirHfr94GMf_k20eXlF1LAaJyXRraKyC9hmBeKSUeAdIgowlfGFAHt96Z4EruBlkn2mbs3mj3uBWr2zqfNTVyQcicJDkJCO5EpbpexyLO5igD9qZU__4ctPvZcfWY-dJswSfiCTP1Uz0BiGFsGqb72chd8Rhn5Btls-D6b9Uuzl9ZZeLj2pIuBA-yi_CMm3CzopKIJ1NySMT8HyvafHcTdfpzFWFPoUqxkVAzt4U6tqBpEnTqmwRW_3kTisJhIY9xH2uXKghz2VWM6mvTL1PahZgbwLqsVb_sBOOEtiASpGf8WNc1uXtKNhBCb_YJw"
      }
    }
  }
}
like image 91
Brettins Avatar answered Dec 23 '22 11:12

Brettins