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

How to change a token to be invalid status? #643

Closed
lichnow opened this issue Apr 17, 2019 · 5 comments
Closed

How to change a token to be invalid status? #643

lichnow opened this issue Apr 17, 2019 · 5 comments

Comments

@lichnow
Copy link

lichnow commented Apr 17, 2019

If I logout or after the password changed,I should let the token to be invalid status.Have any method like which in onJWTDecoded event's $event->markAsInvalid() method to be use in controller or any other service?

@RikudouSage
Copy link
Contributor

You need to store the invalid tokens somewhere and then check if the token does not exist in the stored database (I would recommend Redis for such tasks). When you encounter such invalidated token, you can use the $event->markAsInvalid() you mentioned.

@lichnow
Copy link
Author

lichnow commented Apr 18, 2019

@RikudouSage If store the token to redis or database will make the jwt like session.so ,I may be found a better solution for it.I check if it in logout request and $event->markAsInvalid() to clear the token,last,I will send the successful message.

public function onJWTDecoded(JWTDecodedEvent $event)
    {
        if($this->request->get('_route') !== 'api_users_logout_collection'){
            return;
        }
        if($this->tokenFactory->clearToken($event)){
            $response = new JsonResponse(['message' => 'Logout successfully.']);
            $response->headers->set('Access-Control-Allow-Origin', '*');
            $response->prepare($this->request);
            $response->send();
        }
    }
public function clearToken(JWTDecodedEvent $event)
    {
        $result = false;
        $refreshQueryParams = [];
        $payload = $event->getPayload();
        if ($payload && isset($payload['username'])){
            $event->markAsInvalid();
            $refreshQueryParams['username'] = $payload['username'];
            $result = true;
        }
        $refreshToken = $this->getRefreshToken();
        if($refreshToken){
            $repo = $this->doctrine->getRepository(RefreshTokenEntity::class);
            $refreshQueryParams['refreshToken'] = $refreshToken;
            $RTObject = $repo->findOneBy($refreshQueryParams);
            if ($RTObject){
                $em = $this->doctrine->getManager();
                $em->remove($RTObject);
                $em->flush();
            }
        }
        return $result;
    }

@RikudouSage
Copy link
Contributor

@lichnow
Yeah, that doesn't work the way you probably think it does.

The $event->markAsInvalid() is used only to inform the security system that they should treat the token as invalid for current request. But it doesn't store the information anywhere. It's there so that you can decide whether the token is valid or not after you decoded the data.

In your code nothing happens. The token you "invalidated" can be used again with no problem. To actually invalidate it, you need to store the invalidated tokens somewhere then in an event listener check if the token you received is invalidated (e.g. is stored in database) and then use the $event->markAsInvalid() to inform the security system that it should treat the token as invalid, even though it's technically valid.

As I said, Redis is great for stuff like this for two reasons (and probably some others):

  1. The check will happen on every request, Redis is quick
  2. You can set ttl for the token, set the ttl to the time the token expires, that way you don't have to clean up the database, Redis will delete the rows on its own

@lichnow
Copy link
Author

lichnow commented Apr 19, 2019

@RikudouSage
thanks for you perfect answer.now I solve the problem by redis.it is like this:

 public function invalidToken(array $payload)
    {
        if (!isset($payload['exp'])) return;
        if (($token = $this->getToken()) !== null && !$this->isInvalidToken()){
            $key = "token_blacklist.{$token}";
            $this->redis->set($key,$token);
            $this->redis->expireat($key,$payload['exp']);
        }
    }

    public function isInvalidToken()
    {
        if (null === ($token = $this->getToken())) return false;
        $allInvalids = $this->redis->keys('token_blacklist.*');
        $this->redis->set("token_blacklist.ddd",'fff');
        if (!count($allInvalids)) return false;
        $key = "token_blacklist.{$token}";
        return in_array($key,$allInvalids) && $this->redis->get($key) === $token;
    }

    protected function getToken()
    {
        if(false === ($jsonWebToken = $this->tokenExtractor->extract($this->request))) return null;
        return $jsonWebToken;
    }
public function onJWTDecoded(JWTDecodedEvent $event)
    {
        $payload = $event->getPayload();
        if (!$payload) return;
        if ($this->tokenFactory->isInvalidToken()) {
            $event->markAsInvalid();
            return;
        }
        if ($this->request->get('_route') === 'api_users_logout_collection'){
            $this->tokenFactory->invalidToken($payload);
            $this->tokenFactory->clearRefreshToken($payload);
            $this->responseLogout();
            return;
        }
        return;
    }

This code is work fine.Thank you again for your answer.

@chalasr
Copy link
Collaborator

chalasr commented Apr 20, 2019

Closing as solved, thanks for the help @RikudouSage

@chalasr chalasr closed this as completed Apr 20, 2019
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

No branches or pull requests

3 participants