Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refresh token with additional fields in payload #65

Closed
nicraMarcin opened this issue May 29, 2017 · 11 comments
Closed

refresh token with additional fields in payload #65

nicraMarcin opened this issue May 29, 2017 · 11 comments

Comments

@nicraMarcin
Copy link

Hello,
I'm new in symfony and and try to set HWTRefreshTokenBundle with JTW Lexik.
In jwt I have own Entity

namespace SharedBundle\Security;

final class User implements \Lexik\Bundle\JWTAuthenticationBundle\Security\User\JWTUserInterface
{
    private $username;
    private $roles;
    private $name;
    
    public function __construct($username, array $roles, $email, $name)
    {
        $this->username = $username;
        $this->roles = $roles;
        $this->email = $email;
        $this->name = $name;
    }
    
    public static function createFromPayload($username, array $payload)
    {
        return new self(
            $username,
            $payload['roles'], // Added by default
            $payload['email'],  // Custom
            $payload['name']
        );
    }

 /**
     * {@inheritdoc}
     */
    public function getUsername()
    {
        return $this->username;
    }
    /**
     * {@inheritdoc}
     */
    public function getName()
    {
        return $this->name;
    }
    /**
     * {@inheritdoc}
     */
    public function getRoles()
    {
        return $this->roles;
    }
 ...

In listener I add custom data:

public function onJWTCreated(JWTCreatedEvent $event) {
    $request = $this->requestStack->getCurrentRequest();

    $payload = $event->getData();
    $user = $event->getUser();
    
    $payload['name'] = $user->getName();
    $payload['email'] = $user->getEmail();
    $payload['ip'] = $request->getClientIp();

    $event->setData($payload);
  }

But when I try to refresh token I get error:

[Mon May 29 22:07:36 2017] 127.0.0.1:56078 [200]: /api/token/refresh
[Mon May 29 22:13:52 2017] PHP Fatal error:  Call to undefined method Symfony\Component\Security\Core\User\User::getName() in .../application/be/src/SharedBundle/EventListener/JWTCreatedListener.php on line 33
Segmentation fault

When I throw email and name from payload token is created but without this data, and return only default role ROLE_USER without ROLE_ADMIN.

I'tried to use refresh_token_entity: SharedBundle\Security\User but without result :/
How can I implemet additional fields in payload?

@thecassion
Copy link

I have the same problem like you. How did you handle it @nicraMarcin ? This issue still exist. Did you use an other bundle or customize this one?

@nicraMarcin
Copy link
Author

@thecassion I created my own custom refresh controller

<?php

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use FOS\RestBundle\Controller\Annotations as Rest;

class TokenController extends Controller
{
	/**
	 * @Rest\Get("/api/token/refresh")
	 */
	public function indexAction()
	{
		$jwtManager = $this->container->get('lexik_jwt_authentication.jwt_manager');

		$token = $jwtManager->create($this->getUser());


		return [ 'token' => $token];
	}
}

now in frontend before token expires I make request to this route.
to do is to check if user is still enabled.

@thecassion
Copy link

thecassion commented Jul 3, 2018

Ok . Thanks @nicraMarcin I understand . For me I don't use FOSRestBundle . I only use Api-platform . I will try to refresh token with my own logic like you do. Thanks again

@remoteclient
Copy link

remoteclient commented Jul 3, 2018

I use this with Api Platform and it works. I use another Event I think:

use Lexik\Bundle\JWTAuthenticationBundle\Event\AuthenticationSuccessEvent;
use MWS\UserBundle\Model\UserInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;

class JWTAuthenticatedListener
{
    /**
     * @var TokenStorageInterface
     */
    private $tokenStorage;

    /**
     * @param TokenStorageInterface $tokenStorage
     */
    public function __construct(TokenStorageInterface $tokenStorage)
    {
        $this->tokenStorage = $tokenStorage;
    }

    /**
     *
     * @param AuthenticationSuccessEvent $event
     * @return void
     */
    public function onAuthenticationSuccessResponse(AuthenticationSuccessEvent $event)
    {
        $user = $event->getUser();

        if (!$user instanceof UserInterface) {
            return;
        }
        $data = $event->getData();
        $data['id'] = $user->getId();

        $event->setData($data);
    }
}

This does not encrypt the payload in the token but send the data with it. Encrypting additional data in the token wasn't possible.

Dont forget to give the listener a lower priority. The solution i found and helped me was this: #67

@wittyweb
Copy link

wittyweb commented Aug 8, 2018

Using api-platform, I had the same problem and had to declare a custom user provider in the gesdinet_jwt_refresh_token.yaml configuration file (had to create this file in /config/packages) :

user_provider: security.user.provider.concrete.<your provider defined in security.yaml>

Then it used my User's entity in the onJWTCreated event and not the default one, and I had access to my custom method (like getId())

@jspizziri
Copy link

FWIW, I was also having an issue with the JWT generated after refresh. My issue was that on login, the JWT would contain the complete set of User roles, but on refresh, it wouldn't. The fix was simply to specify the user_provider in gesdinet config to be the same as the one used during initial authentication (in my case an fos_user.user_provider:

# gesdinet_jwt_refresh_token.yaml
gesdinet_jwt_refresh_token:
    user_provider: fos_user.user_provider.username_email

@roelbeerens
Copy link

@wittyweb You saved me there! Thanks!

@peterforeman
Copy link

@jspizziri This should be in the docs! Thanks!

@steveKac01
Copy link

FWIW, I was also having an issue with the JWT generated after refresh. My issue was that on login, the JWT would contain the complete set of User roles, but on refresh, it wouldn't. The fix was simply to specify the user_provider in gesdinet config to be the same as the one used during initial authentication (in my case an fos_user.user_provider:

Hello, I have the same issue but i'm not using custom provider; can you help me please ? I'mmmm so stuck :')

@jspizziri
Copy link

@steveKac01 i haven't used symfony in several years. Good luck!

@GregDevLab
Copy link

GregDevLab commented Aug 20, 2022

@jspizziri @steveKac01 this is my first participation, I hope to help you 😉
JWTRefreshTokenBundle <== it works for me

 # config/packages/security.yaml
  app_user_provider: # the provider i use for refresh token
    entity:
      class: App\Entity\User
      property: username
  jwt:
    lexik_jwt:
      class: App\Security\UserAuthenticate
  firewalls:
    dev:
      pattern: ^/_(profiler|wdt)
      security: false
    login:
      # some config
    api:
      pattern: ^/api/
      stateless: true
      entry_point: jwt
      provider: jwt
      jwt: ~
      refresh_jwt:
        check_path: /api/token/refresh
        provider: app_user_provider # the user's provider must be declared here
# config/packages/gesdinet_jwt_refresh_token.yaml (automatically create from JWTRefreshTokenBundle recipes)
gesdinet_jwt_refresh_token:
  refresh_token_class:   @@App\Entity\RefreshToken

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

10 participants