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
Ignore expired tokens and let user continue unauthenticated #298
Comments
I was using a cookie to store the JWT. I think I already solved this issue, because I had to make sure the cookie expiry time was the same as the JWT expiry time. Then, the scenario above will almost never occur. |
Hello @ruudk, What you are asking is legit to me. Though, the need is quite specific, the expected behavior stays to throw exceptions if bad credentials are given (which is the case) and don't throw them if no credentials are given, so I'm not sure that it should be solved in the bundle. A way to solve that could be to inject the Here is what I end up with: namespace AppBundle\Security;
use Lexik\Bundle\JWTAuthenticationBundle\TokenExtractor\TokenExtractorInterface;
use Lexik\Bundle\JWTAuthenticationBundle\Security\Guard\JWTTokenAuthenticator;
use Symfony\Bundle\SecurityBundle\Security\FirewallMap;
use Lexik\Bundle\JWTAuthenticationBundle\Services\JWTTokenManagerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
final class JWTAuthenticator extends JWTTokenAuthenticator
{
private $firewallMap;
public function __construct(
JWTTokenManagerInterface $jwtManager,
EventDispatcherInterface $dispatcher,
TokenExtractorInterface $tokenExtractor,
FirewallMap $firewallMap
) {
parent::__construct($jwtManager, $dispatcher, $tokenExtractor);
$this->firewallMap = $firewallMap;
}
public function getCredentials(Request $request)
{
try {
return parent::getCredentials($request);
} catch (AuthenticationException $e) {
$firewall = $this->firewallMap->getFirewallConfig($request);
if ($firewall->allowsAnonymous()) {
return;
}
throw $e;
}
}
} services:
app.jwt_authenticator:
parent: lexik_jwt_authentication.security.guard.jwt_token_authenticator
class: AppBundle\Security\JWTAuthenticator
arguments: ['@security.firewall.map'] Note that this requires symfony 3.2+ |
It works! Awesome. Thank you :) |
Would be nice to add as a doc :) |
Indeed 👍 Please let this open, I'll do it asap |
Hello @chalasr, |
@abdallahmaali the best would be to create a sample app and share it here so I can give it a look. |
Hello, @chalasr,
On the expired token case, when getCredentials() returns null I get an exception from symfony's GuardAuthenticationListener:
I guess I should make supports() to return false in the case of the expired token. What would be a correct implementation for this? Thank you very much for your help! |
You have to implement the I'm wondering if there's another solution to bypass the expired token. |
Hi there, Now, I tried the above fix by creating a JWTAuthenticator, but Symfony 4 complains that:
Any ideas? |
|
This seems to be the only way for now:
|
For me, using Symfony 4.4, the above example did not work, it was complaining about the Another similar way that worked for me was to use a decorator: <?php
declare(strict_types=1);
namespace App\Security;
use Lexik\Bundle\JWTAuthenticationBundle\Security\Guard\JWTTokenAuthenticator;
use Lexik\Bundle\JWTAuthenticationBundle\Services\JWTTokenManagerInterface;
use Lexik\Bundle\JWTAuthenticationBundle\TokenExtractor\TokenExtractorInterface;
use Symfony\Bundle\SecurityBundle\Security\FirewallMap;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
class JWTAuthenticator extends JWTTokenAuthenticator
{
/** @var FirewallMap */
private $firewallMap;
/** @var JWTTokenAuthenticator */
private $decorated;
/**
* @param JWTTokenAuthenticator $decorated
* @param JWTTokenManagerInterface $jwtManager
* @param EventDispatcherInterface $dispatcher
* @param TokenExtractorInterface $tokenExtractor
*/
public function __construct(
JWTTokenAuthenticator $decorated,
JWTTokenManagerInterface $jwtManager,
EventDispatcherInterface $dispatcher,
TokenExtractorInterface $tokenExtractor
) {
$this->decorated = $decorated;
parent::__construct($jwtManager, $dispatcher, $tokenExtractor);
}
/**
* @param FirewallMap $firewallMap
*/
public function setFirewallMap(FirewallMap $firewallMap): void
{
$this->firewallMap = $firewallMap;
}
/**
* @param Request $request
*
* @return bool
*/
public function supports(Request $request): bool
{
try {
return $this->decorated->supports($request) && $this->decorated->getCredentials($request);
} catch (AuthenticationException $e) {
if ($this->firewallMap->getFirewallConfig($request)->allowsAnonymous()) {
return false;
}
throw $e;
}
}
} And the App\Security\JWTAuthenticator:
decorates: lexik_jwt_authentication.security.guard.jwt_token_authenticator
calls:
- ['setFirewallMap', ['@security.firewall.map']] Maybe it helps somebody. |
Hi None of the above solution works for me on symfony 5.2. I Always get an allowsAnonymous = false even if I have this in the security.yml
I'm still having this exception generated if the cookie is expired... Any clue? Thanks |
A slightly different implementation than @deepmikoto, to avoid PHPStan complaining about public function supports(Request $request): bool
{
if (!$this->decorated->supports($request)) {
return false;
}
try {
$credentials = $this->decorated->getCredentials($request);
} catch (ExpiredTokenException $e) {
if ($this->firewallMap->getFirewallConfig($request)->allowsAnonymous()) {
return false;
}
}
return true;
} |
Thanks everyone for sharing your up-to-date solutions. I will try to make this behavior easier to implement in a next version. |
For anyone finding this later, I added another approach if the above do not work for your situation: #862 |
When the JWT token of a user is expired, a
JWTAuthenticationFailureResponse
is shown. Is there a way to not do that? And just stop authentication and let the user continue anonymous?Currently, there is no way to do this by subscribing to the
JWTExpiredEvent
event. Since the event has a defaultJWTAuthenticationFailureResponse
response that can only be overwritten (not set to null).I'm willing to create a PR for this use case, but first want to discuss the best approach. Any thoughts?
The text was updated successfully, but these errors were encountered: