lexik / LexikJWTAuthenticationBundle

JWT authentication for your Symfony API
MIT License
2.51k stars 611 forks source link

Get entity collections on authentication success? #424

Open vrobinsonscs opened 6 years ago

vrobinsonscs commented 6 years ago

Greetings -

Sincere apologies if this has been asked about, but haven't found any similar discussions, other than the slightly related https://github.com/lexik/LexikJWTAuthenticationBundle/issues/362.

We'd like to return a list of roles after successful login, which relies on a property that is a collection, rather than a simple string or integer. However, grabbing that property in the AuthenticationSuccessListener results in an empty array -- though interestingly, with the correct number of elements.

It looks to me like the Doctrine entities are not fully formed -- no constructor seems to be run, for example. Is there a way to get the objects fully hydrated? Is there a Use statement that might help? If not, then please consider this a feature request.

In the meantime, I have a working solution, FWIW:

Noticing that I can get plain text and integer properties of related objects, I iterated the seemingly empty related objects and populated a new array with the resulting values, which worked.

// OurBundle/EventListener/AuthenticationSuccessListener.php

namespace OurBundle\EventListener;

use Lexik\Bundle\JWTAuthenticationBundle\Event\AuthenticationSuccessEvent;

class AuthenticationSuccessListener
{

    public function onAuthenticationSuccess(AuthenticationSuccessEvent $event)
    {
        $data = $event->getData(); // yields $data['token'] = <the token>
        $person = $event->getUser()->getPerson();
        $person_id = $person->getId();

        // Get App roles via permission assignments
        // Schema: user->person->permissions->orgroles->approles->app

        $approle_objs = $person->getApproles();
        $approles = [];
        foreach ($approle_objs as $approle) {
          $id = $approle->getId();
          $name = $approle->getName();
          $appkey = $approle->getApp()->getAppkey();
          if(isset($approles[$appkey])) {
            $approles[$appkey] += array($id => $name);
          } else {
            $approles[$appkey] = array($id => $name);
          }
        }

        // $data['token'] contains the JWT
        $data['person_id'] = $person_id;
        $data['approles'] = $approles;

        $event->setData($data);
    }
}
chalasr commented 6 years ago

Greetings,

Because json_encode does not know how to encode your objects, you need to define a key for each property you need to expose by calling getters explicitly, and that for all your object hierarchy. I suggest to have a look at the symfony serializer, if your objects are exposing the relevant properties via getters, the following code would normalize them to a clean array:

$approles = $this->serializer->normalize($person->getAppRoles(), \Symfony\Component\Serializer\Encoder\JsonEncoder::FORMAT)
vrobinsonscs commented 6 years ago

Apologies for the tardy acknowledgement, but thank you for the speedy response on your part.

Very intriguing idea. We're using the JSS Serializer, but perhaps there's no reason we couldn't invoke the Symfony serializer for this purpose. We'll give it a try.