markitosgv/JWTRefreshTokenBundle

Programmatically generate a refresh token

geoffroyp opened this issue ยท 6 comments

Greetings,

Whenever we programmatically generate a JWT token in lexik/JWT (using the method listed here), no refresh token is generated.

Is there a way to achieve this? is there a event I could bind to and/or a service/method I could call to start the refresh_token generation process?

Hi, sorry for the late answer.

Yes, this method could be useful if I was just passing refresh token as an array key in my response, but unfortunately it's not the case. I am using httpOnly cookie and using this method directly forces me to build the http only cookie "by myself"

Isn't there another entry point that would take my whole JWTRefreshToken configuration into consideration? (httpOnly, domain, TTL, secure, path, same site, single use, ....)

This is how I did it. You'll have to be very careful as the maintainers of this package don't really consider programmatic management a priority, and didn't mark some things as deprecated, like moving the get function to the Manager Interface and it broke my code.

use Gesdinet\JWTRefreshTokenBundle\Generator\RefreshTokenGeneratorInterface;
use Gesdinet\JWTRefreshTokenBundle\Model\RefreshTokenManagerInterface;

class JwtTokenManager
{
    public function __construct(
        private RefreshTokenGeneratorInterface $refreshTokenGenerator,
        private RefreshTokenManagerInterface $refreshTokenManager,
        private JWTTokenManagerInterface $JWTManager,
        private EntityManagerInterface $em,
    ){}

....

    public function createAndSetUserJwtTokens($user) 
    {
        $token = $this->JWTManager->create($user);

        $refreshToken = $this->refreshTokenGenerator->createForUserWithTtl($user, SELF::TTL);
        $this->refreshTokenManager->save($refreshToken);

        $user->setRefreshToken($refreshToken->getRefreshToken());

        if ($token) {
            $user->setToken($token);
        }
        return $user;
    }

    public function refreshToken($refreshTokenString) {

        $refreshToken = $this->refreshTokenManager->get($refreshTokenString);
        if ($refreshToken && $refreshToken->isValid()) {
            $userRepository = $this->em->getRepository("App:User");
            $user = $userRepository->findOneBy(['id' => $refreshToken->getUsername()]);
            $token = $this->JWTManager->create($user);
            $user->setRefreshToken($refreshTokenString);
            $user->setToken($token);
            return $user;
        }
        throw new CustomException(ErrorCodes::RefreshTokenInvalid, "RefreshTokenInvalid");
    }

I'm implementing something similar myself and it works but it's not perfect. Because of how the package is currently designed, by implementing programmatic generation you effectively throw away some parts of the functionality it offers for no reason. For e.g. you have to manually take care of setting token TTL (the config value becomes useless). So while it works it definitely feels like you're using the package in a way it was not intended to be used.

@demonzarov thanks!!!!

Because of how the package is currently designed, by implementing programmatic generation you effectively throw away some parts of the functionality it offers for no reason. For e.g. you have to manually take care of setting token TTL (the config value becomes useless). So while it works it definitely feels like you're using the package in a way it was not intended to be used.

I wouldn't necessarily say this bundle isn't intended to support programmatic generation, but its original design heavily favors a very specific configuration and workflow (automatic generation after initial login using the lexik_jwt_authentication.on_authentication_success event and after refresh by proxying through to the LexikJWTAuthenticationBundle's services for a single firewall). And because it favors that specific workflow, it doesn't do well at other workflows, including:

  • Programmatic generation (the generator service requires you to pass through a TTL, if you want to use the config you have to dig through the bundle internals and know you need the gesdinet_jwt_refresh_token.ttl container param)
  • Multi-firewall support (no differentiator between a refresh token generated for me@github.com on two separate firewalls, no support for differing configurations (TTL, request param name, cookie config, etc.) between firewalls)

It's something I've thought on and off about for almost two years and I'm no closer to having a good idea how to improve those other workflows today than I was back then.