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

Adding a Set-Cookie header if 'cookie' is enabled? #646

Closed
PaulCombal opened this issue Apr 23, 2019 · 10 comments · Fixed by #753
Closed

Adding a Set-Cookie header if 'cookie' is enabled? #646

PaulCombal opened this issue Apr 23, 2019 · 10 comments · Fixed by #753

Comments

@PaulCombal
Copy link

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

@RikudouSage
Copy link
Contributor

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;

@PaulCombal
Copy link
Author

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

@thibaut-decherit
Copy link

thibaut-decherit commented Feb 24, 2020

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'.
            )
        );
    }
}

@chalasr
Copy link
Collaborator

chalasr commented Feb 24, 2020

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

@TheEyes007
Copy link

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.

@TheEyes007
Copy link

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.

@stephanfo
Copy link

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’t find any reference to this new feature in the documentation. Thanks.

@chalasr
Copy link
Collaborator

chalasr commented May 29, 2020

Implemented in #753, released in v2.7.0

@lionelkimbs
Copy link

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

@chalasr
Copy link
Collaborator

chalasr commented Aug 7, 2020

@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

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

Successfully merging a pull request may close this issue.

7 participants