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

Cookie expiration date does not match token expiration date when used with event listener #1239

Open
jerzypinkas opened this issue Sep 30, 2024 · 0 comments

Comments

@jerzypinkas
Copy link

Hi,

I am using JWTAuthenticationBundle in combination with GesdinetJWTRefreshTokenBundle. I have an event listener created, which in my case checks if the user is an admin. If so, I set him a longer token expiration time (also refresh token).

I tried to change the expiration time of the cookies, but only the time in the token changes. When setting in cookie lifetime: null, the value of the ttl parameter is taken and the listener does not modify the cookie time value.

My current solution is to use the lexik_jwt_authentication.on_jwt_created event listener described in the documentation, where I modify the token time stored in the cookie. Then using lexik_jwt_authentication.on_authentication_success, which only accesses the getResponse method, I set a new cookie, having previously deleted the ones created by the bundle.

Summary: event listener changes the validity time of the token, but does not change the validity time of cookies.

My files:

// config/packages/lexik_jwt_authentication.yaml

lexik_jwt_authentication:
  secret_key: '%kernel.project_dir%/config/jwt/private.pem'
  public_key: '%kernel.project_dir%/config/jwt/public.pem'
  pass_phrase: '%env(JWT_TOKEN)%'
  token_ttl: '%env(int:JWT_TOKEN_TTL)%'
  token_extractors:
    authorization_header:
        enabled: false
        prefix:  Bearer
        name:    Authorization
    cookie:
        enabled: true
        name:    TOKEN
    query_parameter:
        enabled: false
        name:    bearer
    split_cookie:
        enabled: true
        cookies:
            - jwt_hp
            - jwt_s
  set_cookies:
    jwt_hp:
        lifetime: null
        samesite: lax
        path: /
        domain: null
        secure: false
        httpOnly: false
        partitioned: false
        split:
            - header
            - payload
    jwt_s:
        lifetime: null
        samesite: lax
        path: /
        domain: null
        secure: false
        httpOnly: true
        partitioned: false
        split:
            - signature
  remove_token_from_body_when_cookies_used: true
// config/packages/gesdinet_jwt_refresh_token.yaml

gesdinet_jwt_refresh_token:
  refresh_token_class: App\Entity\RefreshToken
  user_provider: portal_user
  ttl: '%env(int:JWT_REFRESH_TTL)%'
  ttl_update: true
  single_use: true
  cookie:
    enabled: true
    same_site: lax
    path: /
    domain: null
    http_only: true
    secure: false
    partitioned: false
    remove_token_from_body: true
// .env
// ...
JWT_TOKEN_TTL=3600
JWT_REFRESH_TTL=4500
JWT_TOKEN_TTL_ADMIN=432000
JWT_REFRESH_TTL_ADMiN=432900
// ...
// config/services.yaml
// ...
    App\EventListener\JWTCreatedListener:
        tags:
            - { name: 'kernel.event_listener', event: 'lexik_jwt_authentication.on_jwt_created', method: 'onJWTCreated' }

    App\EventListener\JWTAuthenticationSuccessListener:
        tags:
            - { name: 'kernel.event_listener', event: 'lexik_jwt_authentication.on_authentication_success', method: 'onJWTRefreshCreated', priority: -20 }
// ...
// src/EventListener/JWTCreatedListener.php
//This code works correctly, the validity time encoded in the token changes as expected. But not in the cookie

<?php

namespace App\EventListener;

use Lexik\Bundle\JWTAuthenticationBundle\Event\JWTCreatedEvent;

class JWTCreatedListener
{
    public function onJWTCreated(JWTCreatedEvent $event)
    {
        $user = $event->getUser();
        $payload = $event->getData();

        // If admin change token tll to value of JWT_TOKEN_TTL_ADMIN env variable 
        if (in_array('ROLE_ADMIN', $user->getRoles())) {
            $expiration = new \DateTime('+' . $_ENV['JWT_TOKEN_TTL_ADMIN'] . ' seconds');
            $payload['exp'] = $expiration->getTimestamp();
        }

        $event->setData($payload);
    }
}
// src/EventListener/JWTAuthenticationSuccessListener.php
// Very ugly temporary code ;), but working properly. Written quickly as an example of a workaround

<?php

namespace App\EventListener;

use Lexik\Bundle\JWTAuthenticationBundle\Event\AuthenticationSuccessEvent;
use Symfony\Component\HttpFoundation\Cookie;

class JWTAuthenticationSuccessListener
{
    public function onJWTRefreshCreated(AuthenticationSuccessEvent $event)
    {
        $user = $event->getUser();
        $payload = $event->getData();

        $cookiesAdminNamesWithEnv = [
            'jwt_hp' => [
                'user' => 'JWT_TOKEN_TTL',
                'admin' => 'JWT_TOKEN_TTL_ADMIN'
            ],
            'jwt_s' => [
                'user' => 'JWT_TOKEN_TTL',
                'admin' => 'JWT_TOKEN_TTL_ADMIN'
            ],
            'refresh_token' => [
                'user' => 'JWT_REFRESH_TTL',
                'admin' => 'JWT_REFRESH_TTL_ADMiN'
            ],
        ];

        if (in_array('ROLE_ADMIN', $user->getRoles())) {
            $cookies = $event->getResponse()->headers->getCookies();
            foreach ($cookies as $cookie) {
                $cookieName = $cookie->getName();
                if(array_key_exists($cookieName, $cookiesAdminNamesWithEnv)) {
                    $newCookie = new Cookie(
                        $cookieName,
                        $cookie->getValue(),
                        $cookie->getExpiresTime() - (int) $_ENV[$cookiesAdminNamesWithEnv[$cookieName]['user']] + (int) $_ENV[$cookiesAdminNamesWithEnv[$cookieName]['admin']],
                        $cookie->getPath(),
                        $cookie->getDomain(),
                        $cookie->isSecure(),
                        $cookie->isHttpOnly(),
                        $cookie->isRaw(),
                        $cookie->getSameSite(),
                        $cookie->isPartitioned(),
                    );

                    $event->getResponse()->headers->removeCookie($cookieName);
                    $event->getResponse()->headers->setCookie($newCookie);
                }
            }
        }

        $event->setData($payload);
    }
}
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

1 participant