/**
  * {@inheritDoc}
  */
 public function createTokenResponse(ServerRequestInterface $request, Client $client = null, TokenOwnerInterface $owner = null) : ResponseInterface
 {
     $postParams = $request->getParsedBody();
     $refreshToken = $postParams['refresh_token'] ?? null;
     if (null === $refreshToken) {
         throw OAuth2Exception::invalidRequest('Refresh token is missing');
     }
     // We can fetch the actual token, and validate it
     /** @var RefreshToken $refreshToken */
     $refreshToken = $this->refreshTokenService->getToken($refreshToken);
     if (null === $refreshToken || $refreshToken->isExpired()) {
         throw OAuth2Exception::invalidGrant('Refresh token is expired');
     }
     // We can now create a new access token! First, we need to make some checks on the asked scopes,
     // because according to the spec, a refresh token can create an access token with an equal or lesser
     // scope, but not more
     $scopes = $postParams['scope'] ?? $refreshToken->getScopes();
     if (!$refreshToken->matchScopes($scopes)) {
         throw OAuth2Exception::invalidScope('The scope of the new access token exceeds the scope(s) of the refresh token');
     }
     $owner = $refreshToken->getOwner();
     $accessToken = new AccessToken();
     $this->populateToken($accessToken, $client, $owner, $scopes);
     /** @var AccessToken $accessToken */
     $accessToken = $this->accessTokenService->createToken($accessToken);
     // We may want to revoke the old refresh token
     if ($this->rotateRefreshTokens) {
         $this->refreshTokenService->deleteToken($refreshToken);
         $refreshToken = new RefreshToken();
         $this->populateToken($refreshToken, $client, $owner, $scopes);
         $refreshToken = $this->refreshTokenService->createToken($refreshToken);
     }
     // We can generate the response!
     return $this->prepareTokenResponse($accessToken, $refreshToken, true);
 }
 /**
  * @param  ServerRequestInterface $request
  * @return ResponseInterface
  * @throws OAuth2Exception If no "token" is present
  */
 public function handleRevocationRequest(ServerRequestInterface $request) : ResponseInterface
 {
     $postParams = $request->getParsedBody();
     $token = $postParams['token'] ?? null;
     $tokenHint = $postParams['token_type_hint'] ?? null;
     if (null === $token || null === $tokenHint) {
         throw OAuth2Exception::invalidRequest('Cannot revoke a token as the "token" and/or "token_type_hint" parameters are missing');
     }
     if ($tokenHint !== 'access_token' && $tokenHint !== 'refresh_token') {
         throw OAuth2Exception::unsupportedTokenType(sprintf('Authorization server does not support revocation of token of type "%s"', $tokenHint));
     }
     if ($tokenHint === 'access_token') {
         $token = $this->accessTokenService->getToken($token);
     } else {
         $token = $this->refreshTokenService->getToken($token);
     }
     $response = new Response();
     // According to spec, we should return 200 if token is invalid
     if (null === $token) {
         return $response;
     }
     // Now, we must validate the client if the token was generated against a non-public client
     if (null !== $token->getClient() && !$token->getClient()->isPublic()) {
         $requestClient = $this->getClient($request, false);
         if ($requestClient !== $token->getClient()) {
             throw OAuth2Exception::invalidClient('Token was issued for another client and cannot be revoked');
         }
     }
     try {
         if ($tokenHint === 'access_token') {
             $this->accessTokenService->deleteToken($token);
         } else {
             $this->refreshTokenService->deleteToken($token);
         }
     } catch (\Exception $exception) {
         // According to spec (https://tools.ietf.org/html/rfc7009#section-2.2.1), we should return a server 503
         // error if we cannot delete the token for any reason
         $response = $response->withStatus(503, 'An error occurred while trying to delete the token');
     }
     return $response;
 }