diff --git a/README.md b/README.md index 88f245a6..5ff804e6 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ ## Оглавление - [Установка](#установка) - [Начало работы](#начало-работы-и-авторизация) +- [Авторизация с долгоживущим токеном](#авторизация-с-долгоживущим-токеном) - [Подход к работе с библиотекой](#подход-к-работе-с-библиотекой) - [Поддерживаемые методы и сервисы](#поддерживаемые-методы-и-сервисы) - [Обработка ошибок](#обработка-ошибок) @@ -85,6 +86,7 @@ $apiClient->getOAuthClient()->getOAuthButton( ] ); ``` +Для аккаунтов kommo.com - добавьте параметр ```is_kommo => true,```. 2. Отправив пользователя на страницу авторизации ```php @@ -122,6 +124,30 @@ $apiClient = $apiClient->setUserAgnet('App Name'); ``` +## Авторизация с долгоживущим токеном +Не так давно в amoCRM появилась возможность создавать долгоживущие токены. Их можно легко использовать с этой библиотекой. + +Для начала использования вам необходимо создать объект библиотеки: +```php +$apiClient = new \AmoCRM\Client\AmoCRMApiClient(); +``` + +После этого нужно создать объект ```AmoCRM\Client\LongLivedAccessToken```, который будет использоваться с запросами в API. + +```php +$longLivedAccessToken = new LongLivedAccessToken($accessToken); +``` + +Затем нужно установить токен и адресс аккаунта в объект библиотеки: +```php +$apiClient->setAccessToken($longLivedAccessToken) + ->setAccountBaseDomain('example.amocrm.ru'); +``` + +После этих простых шагов, вы сможете делать запросы в amoCRM до тех пор, пока токен не истечет или его не отзовут. +В случае отзыва или истечения токена - при выполнении запроса - упадет ошибка с http кодом 401. + + ## Подход к работе с библиотекой В библиотеке используется сервисный подход. Для каждой сущности имеется сервис. diff --git a/examples/long_lived_token.php b/examples/long_lived_token.php new file mode 100644 index 00000000..ab5079d3 --- /dev/null +++ b/examples/long_lived_token.php @@ -0,0 +1,33 @@ +setAccessToken($longLivedAccessToken) + ->setAccountBaseDomain($accountUrl); + +//Получим информацию об аккаунте +try { + $account = $apiClient->account()->getCurrent(); +} catch (AmoCRMApiException $e) { + var_dump($e->getTraceAsString()); + printError($e); + die; +} + +echo $account->getName(); diff --git a/src/AmoCRM/Client/AmoCRMApiClient.php b/src/AmoCRM/Client/AmoCRMApiClient.php index 33e3056a..b5eb466d 100644 --- a/src/AmoCRM/Client/AmoCRMApiClient.php +++ b/src/AmoCRM/Client/AmoCRMApiClient.php @@ -88,11 +88,12 @@ class AmoCRMApiClient /** * AmoCRMApiClient constructor. - * @param string $clientId - * @param string $clientSecret + * + * @param string|null $clientId + * @param string|null $clientSecret * @param null|string $redirectUri */ - public function __construct(string $clientId, string $clientSecret, ?string $redirectUri) + public function __construct(?string $clientId = null, ?string $clientSecret = null, ?string $redirectUri = null) { $this->oAuthClient = new AmoCRMOAuth($clientId, $clientSecret, $redirectUri); } diff --git a/src/AmoCRM/Client/AmoCRMApiRequest.php b/src/AmoCRM/Client/AmoCRMApiRequest.php index 1b70ca0d..eaa8effa 100644 --- a/src/AmoCRM/Client/AmoCRMApiRequest.php +++ b/src/AmoCRM/Client/AmoCRMApiRequest.php @@ -36,7 +36,7 @@ class AmoCRMApiRequest public const CONNECT_TIMEOUT = 5; public const REQUEST_TIMEOUT = 20; //TODO Do not forget to change this on each release - public const LIBRARY_VERSION = '1.5.1'; + public const LIBRARY_VERSION = '1.6.0'; public const USER_AGENT = 'amoCRM-API-Library/' . self::LIBRARY_VERSION; public const SUCCESS_STATUSES = [ @@ -132,8 +132,12 @@ public function __construct( * Обновляем в библиотеке oAuth access токен по refresh * @throws AmoCRMoAuthApiException */ - private function refreshAccessToken() + private function refreshAccessToken(): void { + if ($this->accessToken instanceof LongLivedAccessToken) { + throw new AmoCRMoAuthApiException('Can not update LongLivedAccessToken'); + } + $newAccessToken = $this->oAuthClient->getAccessTokenByRefreshToken($this->accessToken); $this->accessToken = $newAccessToken; } @@ -162,7 +166,7 @@ public function post( bool $needToRefresh = false, bool $isFullPath = false ): array { - if ($this->accessToken->hasExpired()) { + if ($this->isAccessTokenNeedToBeRefreshed()) { $needToRefresh = true; } @@ -216,7 +220,7 @@ public function post( try { $response = $this->parseResponse($response); } catch (AmoCRMoAuthApiException $e) { - if ($needToRefresh) { + if ($needToRefresh || $this->accessToken instanceof LongLivedAccessToken) { throw $e; } @@ -250,7 +254,7 @@ public function put( bool $needToRefresh = false, bool $isFullPath = false ): array { - if ($this->accessToken->hasExpired()) { + if ($this->isAccessTokenNeedToBeRefreshed()) { $needToRefresh = true; } @@ -304,7 +308,7 @@ public function put( try { $response = $this->parseResponse($response); } catch (AmoCRMoAuthApiException $e) { - if ($needToRefresh) { + if ($needToRefresh || $this->accessToken instanceof LongLivedAccessToken) { throw $e; } @@ -332,7 +336,7 @@ public function patch( array $headers = [], bool $needToRefresh = false ): array { - if ($this->accessToken->hasExpired()) { + if ($this->isAccessTokenNeedToBeRefreshed()) { $needToRefresh = true; } @@ -379,7 +383,7 @@ public function patch( try { $response = $this->parseResponse($response); } catch (AmoCRMoAuthApiException $e) { - if ($needToRefresh) { + if ($needToRefresh || $this->accessToken instanceof LongLivedAccessToken) { throw $e; } @@ -406,7 +410,7 @@ public function delete( array $headers = [], bool $needToRefresh = false ): array { - if ($this->accessToken->hasExpired()) { + if ($this->isAccessTokenNeedToBeRefreshed()) { $needToRefresh = true; } @@ -453,7 +457,7 @@ public function delete( try { $response = $this->parseResponse($response); } catch (AmoCRMoAuthApiException $e) { - if ($needToRefresh) { + if ($needToRefresh || $this->accessToken instanceof LongLivedAccessToken) { throw $e; } @@ -480,7 +484,7 @@ public function get( array $headers = [], bool $needToRefresh = false ): array { - if ($this->accessToken->hasExpired()) { + if ($this->isAccessTokenNeedToBeRefreshed()) { $needToRefresh = true; } @@ -532,7 +536,7 @@ public function get( try { $response = $this->parseResponse($response); } catch (AmoCRMoAuthApiException $e) { - if ($needToRefresh) { + if ($needToRefresh || $this->accessToken instanceof LongLivedAccessToken) { throw $e; } @@ -740,4 +744,9 @@ public function getUserAgent(): string ? sprintf('%s (%s)', $this->userAgent, self::USER_AGENT) : self::USER_AGENT; } + + private function isAccessTokenNeedToBeRefreshed(): bool + { + return !$this->accessToken instanceof LongLivedAccessToken && $this->accessToken->hasExpired(); + } } diff --git a/src/AmoCRM/Client/LongLivedAccessToken.php b/src/AmoCRM/Client/LongLivedAccessToken.php new file mode 100644 index 00000000..5db1b87f --- /dev/null +++ b/src/AmoCRM/Client/LongLivedAccessToken.php @@ -0,0 +1,48 @@ +parser()->parse($accessToken); + } catch (Throwable $e) { + throw new InvalidArgumentException( + 'Error parsing given access token. Prev error: ' . $e->getMessage(), + 0, + [], + 'Check access token.' + ); + } + + $claims = $parsedAccessToken->claims(); + + /** @var DateTimeImmutable $expiresAt */ + $expiresAt = $claims->get('exp'); + + $options = [ + 'expires' => $expiresAt->getTimestamp(), + 'access_token' => $accessToken, + ]; + + parent::__construct($options); + } +} diff --git a/src/AmoCRM/Exceptions/InvalidArgumentException.php b/src/AmoCRM/Exceptions/InvalidArgumentException.php index 7728f973..c360819a 100755 --- a/src/AmoCRM/Exceptions/InvalidArgumentException.php +++ b/src/AmoCRM/Exceptions/InvalidArgumentException.php @@ -1,5 +1,7 @@ oauthProvider = new AmoCRM( [ @@ -313,15 +314,21 @@ public function getOAuthButton(array $options = []): string $mode = isset($options['mode']) && in_array($options['mode'], ['popup', 'post_message']) ? $options['mode'] : 'post_message'; + try { $state = $options['state'] ?? bin2hex(random_bytes(10)); } catch (Exception $exception) { $state = rand(1, 100); } + $mainClassName = isset($options['is_kommo']) && $options['is_kommo'] ? 'kommo_oauth' : 'amocrm_oauth'; + $scriptPath = isset($options['is_kommo']) && $options['is_kommo'] + ? 'https://www.kommo.com/auth/button.min.js' + : 'https://www.amocrm.ru/auth/button.min.js'; + return '
'; } @@ -464,8 +471,8 @@ public function parseDisposableToken(string $token): DisposableTokenModel new SignedWith($signer, $key), // Проверим наш ли адресат new PermittedFor($clientBaseUri), - // Проверка жизни токена, с 4.2 deprecated use LooseValidAt - new ValidAt(FrozenClock::fromUTC()), + // Проверка жизни токена + new Constraint\LooseValidAt(FrozenClock::fromUTC()), ]; $configuration = Configuration::forSymmetricSigner($signer, $key);