/**
  * @NoAuthorization
  * @Url(/oauth/token)
  */
 function postToken()
 {
     if (!$this->httpRequest->isSecured() && $this->presenter->isInProductionMode()) {
         $this->presenter->terminateWithError(self::ERROR_INVALID_REQUEST, "Secured connection required", 400);
     }
     if (!isset($this->postData['grant_type'])) {
         $this->presenter->terminateWithError(self::ERROR_INVALID_REQUEST, 'Parameter grant_type is required.', 400);
     }
     switch ($this->postData['grant_type']) {
         // RFC 6749 - 4.3. Resource Owner Password Credentials
         // @see http://tools.ietf.org/html/draft-ietf-oauth-v2-15#section-4.3
         //
         // Used for user owned resources (both user and client application authorization)
         case 'password':
             list($clientId, $clientSecret) = $this->parseClientAuthInfo();
             if (!isset($this->postData['username'])) {
                 $this->presenter->terminateWithError(self::ERROR_INVALID_REQUEST, 'Parameter username is required.', 400);
             }
             if (!isset($this->postData['password'])) {
                 $this->presenter->terminateWithError(self::ERROR_INVALID_REQUEST, 'Parameter password is required.', 400);
             }
             if (!$this->attemptLogger->getRemainingAttempts(RestPresenter::ATTEMPT_IP_TOKEN, $this->httpRequest->getRemoteAddress())) {
                 $this->presenter->terminateWithError(self::ERROR_MAXIMUM_ATTEMPTS_EXCEEDED, 'Maximum number of authorization attempts exceeded.', 403);
             }
             $this->processClientAuth($clientId, $clientSecret);
             $this->processPasswordAuth($this->postData['username'], $this->postData['password']);
             $token = $this->tokenManager->createToken($this->tokenParameters);
             break;
             // RFC 6749 - 4.4. Client Credentials
             // @see http://tools.ietf.org/html/draft-ietf-oauth-v2-15#section-4.4
             //
             // Used for resources owned solely by client application owned (no user authorization)
             // Example: B2B services
         // RFC 6749 - 4.4. Client Credentials
         // @see http://tools.ietf.org/html/draft-ietf-oauth-v2-15#section-4.4
         //
         // Used for resources owned solely by client application owned (no user authorization)
         // Example: B2B services
         case 'client_credentials':
             list($clientId, $clientSecret) = $this->parseClientAuthInfo();
             if (!$this->attemptLogger->getRemainingAttempts(RestPresenter::ATTEMPT_IP_TOKEN, $this->httpRequest->getRemoteAddress())) {
                 $this->presenter->terminateWithError(self::ERROR_MAXIMUM_ATTEMPTS_EXCEEDED, 'Maximum number of authorization attempts exceeded.', 403);
             }
             $this->processClientAuth($clientId, $clientSecret);
             $token = $this->tokenManager->createToken($this->tokenParameters);
             break;
             // RFC 6749 - 6. Refreshing and Access Token
             // @see http://tools.ietf.org/html/draft-ietf-oauth-v2-15#section-6
             //
             // Used for extending time of life of expired token
         // RFC 6749 - 6. Refreshing and Access Token
         // @see http://tools.ietf.org/html/draft-ietf-oauth-v2-15#section-6
         //
         // Used for extending time of life of expired token
         case 'refresh_token':
             list($clientId, $clientSecret) = $this->parseClientAuthInfo();
             if (!isset($this->postData['refresh_token'])) {
                 $this->presenter->terminateWithError(self::ERROR_INVALID_REQUEST, 'Parameter refresh_token is required.', 400);
             }
             if (!$this->attemptLogger->getRemainingAttempts(RestPresenter::ATTEMPT_IP_TOKEN, $this->httpRequest->getRemoteAddress())) {
                 $this->presenter->terminateWithError(self::ERROR_MAXIMUM_ATTEMPTS_EXCEEDED, 'Maximum number of authorization attempts exceeded.', 403);
             }
             $this->processClientAuth($clientId, $clientSecret);
             $token = $this->tokenManager->refreshToken($this->postData['refresh_token']);
             if (!$token) {
                 $this->attemptLogger->logFail(RestPresenter::ATTEMPT_IP_TOKEN, $this->httpRequest->getRemoteAddress());
                 $this->presenter->terminateWithError(self::ERROR_INVALID_GRANT, 'Given refresh token is not valid.', 401);
             }
             $this->attemptLogger->logSuccess(RestPresenter::ATTEMPT_IP_TOKEN, $this->httpRequest->getRemoteAddress());
             break;
             // Unsupported grant_type
         // Unsupported grant_type
         default:
             $this->presenter->terminateWithError(self::ERROR_INVALID_CLIENT, 'Unsupported grant_type requested.', 400);
     }
     // ----------------------------
     // Sanity check
     if (!$token) {
         throw new Nette\InvalidStateException("Expected token");
     }
     $this->httpResponse->setHeader('Cache-Control', 'no-store');
     $payload = new \StdClass();
     $payload->access_token = $token->getToken();
     $payload->token_type = 'Bearer';
     $payload->expires_in = $token->getTtl();
     if ($token->getRefreshToken()) {
         $payload->refresh_token = $token->getRefreshToken();
     }
     return $payload;
 }