Lexikjwtauthenticationbundle: Adding a Set-Cookie header if 'cookie' is enabled?

Created on 23 Apr 2019  路  10Comments  路  Source: lexik/LexikJWTAuthenticationBundle

Hello,

I just started using the bundle, it works great so far, but one thing bothers me. For now, I store my JWT tokens in localstorage in my frontend application, but it is not recommended and is a security risk.

I read online that the tokens are usually stored in httpOnly cookies. I would love to do that with this bundle, as it seems to work with cookies as well:

        # check token in a cookie
        cookie:
            enabled: true
            name:    BEARER

This looks great, but with this config enabled, the response does not feature a Set-Cookie header, so that makes it useless for me.

Does it seem like a good idea to the Set-Cookie header with the httpOnly parameter? And if not, how would you suggest to use the cookies in a secured manner?

Thank you

Most helpful comment

The bundle will automatically set the cookie and recommend using cookies in the next minor version.

All 10 comments

Well, creating the cookie is your own responsibility, so after generating the JWT just set the cookie, for example like this:

$response = new Response();
$response->headers->setCookie(
    new Cookie(
        'BEARER', // cookie name, should be the same as in JWT settings
        'YOUR_JWT_TOKEN', // the cookie value, e.g. the generated JWT token
        new \DateTime('+1 day'), // the expiration
        '/', // the path
        null, // the domain, null means that Symfony will generate it on its own
        true, // secure, e.g. only via https
        true, // http only cookie, which is the default so no need to specify
        false, // raw
        'strict' // the same-site parameter, can be 'lax' or 'strict'
    )
);
return $response;

Thanks for the complete reply! That's a great answer, I was just wondering why this isn't made default when cookie is enabled (or in another setting) since most people using it will have to do it themselves

For anyone still trying to do that, instead of e.g. storing the token in local storage (which is not secure albeit being suggested way too many times...), here is an implementation example for SF4:

config/packages/lexik_jwt_authentication.yaml

lexik_jwt_authentication:
  secret_key: '%env(resolve:JWT_SECRET_KEY)%'
  public_key: '%env(resolve:JWT_PUBLIC_KEY)%'
  pass_phrase: '%env(JWT_PASSPHRASE)%'
  token_ttl: '%env(JWT_TTL)%'

  # Add the following
  token_extractors:
    cookie:
      enabled: true
      name: __Host-JWT

config/services.yaml

app.event.jwt_authentication_success_listener:
  class: App\EventListener\JWTAuthenticationSuccessListener
  arguments:
    $tokenLifetime: '%env(JWT_TTL)%'
  tags:
    tag_1:
      name: kernel.event_listener
      event: lexik_jwt_authentication.on_authentication_success
      method: onAuthenticationSuccess

src/EventListener/JWTAuthenticationSuccessListener.php

<?php

namespace App\EventListener;

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

/**
 * Class JWTAuthenticationSuccessListener
 * @package App\EventListener
 */
class JWTAuthenticationSuccessListener
{
    /**
     * @var int
     */
    private $tokenLifetime;

    public function __construct(int $tokenLifetime)
    {
        $this->tokenLifetime = $tokenLifetime;
    }

    /**
     * Sets JWT as a cookie on successful authentication.
     * 
     * @param AuthenticationSuccessEvent $event
     * @throws Exception
     */
    public function onAuthenticationSuccess(AuthenticationSuccessEvent $event): void
    {
        $event->getResponse()->headers->setCookie(
            new Cookie(
                '__Host-JWT', // Cookie name, should be the same as in config/packages/lexik_jwt_authentication.yaml.
                $event->getData()['token'], // cookie value
                time() + $this->tokenLifetime, // expiration
                '/', // path
                null, // domain, null means that Symfony will generate it on its own.
                true, // secure
                true, // httpOnly
                false, // raw
                'lax' // same-site parameter, can be 'lax' or 'strict'.
            )
        );
    }
}

The bundle will automatically set the cookie and recommend using cookies in the next minor version.

Thanks @chalasr . I wait this version impatiently.With body and localstorage(angular), i have not problem but with cookie by following previous advice and this tutorial (https://www.youtube.com/watch?v=uboIb2__qqs), i have invalid jwt token by using postman.
I have setcookie but i have error with LcobucciJWTEncoder.

Have a nice day.

Hello,

May I contact you because i always have the same error with my application symfony 5 and angular 8. The JWT token is invalid. When I put in localstorage, it's good but with cookie I have this error :

[2020-03-24T19:42:34.972706+01:00] request.INFO: Matched route "user_profiles_infos". {"route":"user_profiles_infos","route_parameters":{"_route":"user_profiles_infos","_controller":"App\\UI\\Actions\\Security\\UserProfilesInfosAction"},"request_uri":"http://127.0.0.1:8080/api/v1/user/profile","method":"GET"} [] [2020-03-24T19:42:34.988094+01:00] security.DEBUG: Checking for guard authentication credentials. {"firewall_key":"api","authenticators":1} [] [2020-03-24T19:42:34.988151+01:00] security.DEBUG: Checking support on guard authenticator. {"firewall_key":"api","authenticator":"Lexik\\Bundle\\JWTAuthenticationBundle\\Security\\Guard\\JWTTokenAuthenticator"} [] [2020-03-24T19:42:34.988197+01:00] security.DEBUG: Calling getCredentials() on guard authenticator. {"firewall_key":"api","authenticator":"Lexik\\Bundle\\JWTAuthenticationBundle\\Security\\Guard\\JWTTokenAuthenticator"} [] [2020-03-24T19:42:34.995053+01:00] security.DEBUG: Passing guard token information to the GuardAuthenticationProvider {"firewall_key":"api","authenticator":"Lexik\\Bundle\\JWTAuthenticationBundle\\Security\\Guard\\JWTTokenAuthenticator"} [] [2020-03-24T19:42:35.024745+01:00] php.INFO: User Deprecated: Creating Doctrine\ORM\Mapping\UnderscoreNamingStrategy without making it number aware is deprecated and will be removed in Doctrine ORM 3.0. {"exception":"[object] (ErrorException(code: 0): User Deprecated: Creating Doctrine\\ORM\\Mapping\\UnderscoreNamingStrategy without making it number aware is deprecated and will be removed in Doctrine ORM 3.0. at C:\\Projets\\Applications_Web\\odazik-api\\vendor\\doctrine\\orm\\lib\\Doctrine\\ORM\\Mapping\\UnderscoreNamingStrategy.php:66)"} [] [2020-03-24T19:42:35.120589+01:00] doctrine.DEBUG: SELECT u0_.id AS id_0, u0_.username AS username_1, u0_.email AS email_2, u0_.password AS password_3, u0_.is_active AS is_active_4, u0_.roles AS roles_5, u0_.civilite AS civilite_6, u0_.name AS name_7, u0_.firstname AS firstname_8, u0_.expire_at AS expire_at_9, u0_.create_at AS create_at_10, u0_.postcode AS postcode_11, u0_.city AS city_12, u0_.country AS country_13, u0_.validation_account_token AS validation_account_token_14, u0_.media_id AS media_id_15 FROM security.users u0_ WHERE u0_.username = ? OR u0_.email = ? ["admin","admin"] [] [2020-03-24T19:42:35.130969+01:00] security.INFO: Guard authentication successful! {"token":{"Lexik\\Bundle\\JWTAuthenticationBundle\\Security\\Authentication\\Token\\JWTUserToken":"JWTUserToken(user=\"admin\", authenticated=true, roles=\"ROLE_ADMIN\")"},"authenticator":"Lexik\\Bundle\\JWTAuthenticationBundle\\Security\\Guard\\JWTTokenAuthenticator"} [] [2020-03-24T19:42:35.137940+01:00] security.DEBUG: Guard authenticator set no success response: request continues. {"authenticator":"Lexik\\Bundle\\JWTAuthenticationBundle\\Security\\Guard\\JWTTokenAuthenticator"} [] [2020-03-24T19:42:35.137999+01:00] security.DEBUG: Remember me skipped: it is not configured for the firewall. {"authenticator":"Lexik\\Bundle\\JWTAuthenticationBundle\\Security\\Guard\\JWTTokenAuthenticator"} [] [2020-03-24T19:42:35.183408+01:00] request.CRITICAL: Uncaught PHP Exception Lexik\Bundle\JWTAuthenticationBundle\Exception\JWTDecodeFailureException: "Invalid JWT Token" at C:\Projets\Applications_Web\odazik-api\vendor\lexik\jwt-authentication-bundle\Encoder\LcobucciJWTEncoder.php line 55 {"exception":"[object] (Lexik\\Bundle\\JWTAuthenticationBundle\\Exception\\JWTDecodeFailureException(code: 0): Invalid JWT Token at C:\\Projets\\Applications_Web\\odazik-api\\vendor\\lexik\\jwt-authentication-bundle\\Encoder\\LcobucciJWTEncoder.php:55)\n[previous exception] [object] (InvalidArgumentException(code: 0): The JWT string must have two dots at C:\\Projets\\Applications_Web\\odazik-api\\vendor\\lcobucci\\jwt\\src\\Parser.php:95)"} []

The header request give : Cookie: REFRESH_TOKEN=0a3fa8859db72db27e11d6a97cc04bf0c7af6786d66f9b1b273f29ffa4069c707f8cba94b363f5ff63515c92d2ddfacbf0382c7125c09f6a50d27afef2e2d088; BEARER=eyJ0eXAiOiJKV1QiL.eyJpYXQiOjE1ODUwNzUzNTMsIFkbWluIn0.fI5fcb7abFQe3f1-OE

I delete a part of letter for secret but i have two dots. So I don't understand this problem.

Could you help me ! I have no issue.

Thanks a lot.

The bundle will automatically set the cookie and recommend using cookies in the next minor version.

Is this new minor version now released? I can鈥檛 find any reference to this new feature in the documentation. Thanks.

Implemented in #753, released in v2.7.0

Anyone tried this ?
Cause, I updated the bundle, but still no difference. Is there something to do ?

@lionelkimbs You need some additional config to enable this feature, see https://github.com/lexik/LexikJWTAuthenticationBundle/blob/master/Resources/doc/1-configuration-reference.md#automatically-generating-cookies

Was this page helpful?
0 / 5 - 0 ratings

Related issues

reducio picture reducio  路  4Comments

rolandrajko picture rolandrajko  路  3Comments

astronati picture astronati  路  5Comments

pbalan picture pbalan  路  5Comments

christophe-mailfert picture christophe-mailfert  路  5Comments