Search code examples
phpsymfonysecurityjwtlexikjwtauthbundle

Generate short lifetime JWT token with LexikJWTAuthenticationBundle


I try to secure some files via Symfony. Let's say I access them via the following url:

https://test.com/file/?token=AAAABBBBCCCC

I already implemented authorization via LexikJWTAuthenticationBundle. I log in; get an access token - this is working just fine. So I used my login access token to put in the url.

But login tokens have a far too long lifespan. So I thought about creating a short lifetime token (< 2 seconds), to just access the file once. As soon as I refresh the page, access to the file should be gone.

On the symfony page I found out how to create a token: $JWTManager->create($user) (https://symfony.com/bundles/LexikJWTAuthenticationBundle/current/7-manual-token-creation.html)

But I cannot configure lifespan of this token. This is usually configured in lexik_jwt_authentication.yaml, but I don't want change lifespan of the login token :/

I'm using Symfony 5.

Any ideas?


Solution

  • I solved it now. First create a token and add any data to identify its use case. I use for example a scoped:

    public function createToken(JWTTokenManagerInterface $jwt): JsonResponse
    {
        $token = $jwt->createFromPayload(
            $this->getUser(),
            ['scope' => 'fileScope']
        );
    
        return new JsonResponse($token);
    }
    

    Then use an EventListener to set expiry date based on scope:

    // EventListener/JWTCreatedListener.php
    
    declare(strict_types=1);
    
    namespace App\EventListener;
    
    use App\Controller\Authentication\GetDocumentFileToken;
    use Lexik\Bundle\JWTAuthenticationBundle\Event\JWTCreatedEvent;
    
    /**
     * Class JWTCreatedListener
     */
    class JWTCreatedListener
    {
        /**
         * @param JWTCreatedEvent $event
         *
         * @return void
         */
        public function onJWTCreated(JWTCreatedEvent $event): void
        {
            $payload = $event->getData();
    
            if (($payload['scope'] ?? '') === 'fileScope') {
                $expiration = new \DateTime('+1 minute');
                $payload['exp'] = $expiration->getTimestamp();
            }
    
            $event->setData($payload);
        }
    }
    

    And then, in the controller where only specific scope tokens are allowed:

    public function getDocumentFile(JWTTokenManagerInterface $jwt): JsonResponse
    {
        $token = /* Get token from request or something */;
        $tokenData = $jwt->parse($token);
    
        if (($tokenData['scope'] ?? '') !== 'fileScope') {
            return new JsonResponse('Permission denied', 400);
        }
    
        // Token is valid and fileScope so output file
    }