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
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
Most helpful comment
The bundle will automatically set the cookie and recommend using cookies in the next minor version.