From 229a9ec9068f0e002918e8174f8cc869bf09f539 Mon Sep 17 00:00:00 2001 From: Nikita Date: Mon, 2 Sep 2019 15:11:05 +0300 Subject: [PATCH] update for new oauth2-client --- .travis.yml | 2 + LICENSE | 2 +- README.md | 65 +++++++++++---- composer.json | 3 +- example.php | 152 ++++++++++++++++++++++++++++++++++++ src/AmoCRM.php | 116 +++++++++++++++++---------- src/AmoCRMException.php | 26 ++++++ src/AmoCRMResourceOwner.php | 67 ++++++++++++++++ test.php | 129 ------------------------------ 9 files changed, 372 insertions(+), 190 deletions(-) create mode 100644 example.php create mode 100644 src/AmoCRMException.php create mode 100644 src/AmoCRMResourceOwner.php delete mode 100644 test.php diff --git a/.travis.yml b/.travis.yml index 8e6f022..768f8c2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,6 +13,8 @@ php: - 5.6 - 7.0 - 7.1 + - 7.2 + - 7.3 matrix: include: diff --git a/LICENSE b/LICENSE index 54d98e9..f99ecb7 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2017 amoCRM. +Copyright (c) 2017-2019 amoCRM. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 19b3ca2..61f823a 100644 --- a/README.md +++ b/README.md @@ -17,12 +17,10 @@ composer require amocrm/oauth2-amocrm ### Обработка кода авторизации (Authorization Code) ```php -$provider = new AmoCRM\OAuth2\Client\Provider\AmoCRM([ - 'clientId' => 'YOUR_CLIENT_ID', - 'clientSecret' => 'YOUR_CLIENT_SECRET', - 'redirectUri' => 'http://your-redirect-uri', - 'clientSubdomain' => 'example', - 'clientTopLevelDomain' => 'ru', //use com for us accounts +$provider = new AmoCRM([ + 'clientId' => 'xxx', + 'clientSecret' => 'xxx', + 'redirectUri' => 'https://test.test', ]); if (isset($_GET['code']) && $_GET['code']) { @@ -30,23 +28,56 @@ if (isset($_GET['code']) && $_GET['code']) { 'code' => $_GET['code'] ]); - // Returns an instance of League\OAuth2\Client\User - $user = $this->provider->getUserDetails($token); - $uid = $provider->getUserUid($token); - $email = $provider->getUserEmail($token); - $screenName = $provider->getUserScreenName($token); + //Вызов функции setBaseDomain требуется для установки контектс аккаунта. + if (isset($_GET['referer'])) { + $provider->setBaseDomain($_GET['referer']); + } + + //todo сохраняем access, refresh токены и привязку к аккаунту и возможно пользователю + + /** @var \AmoCRM\OAuth2\Client\Provider\AmoCRMResourceOwner $ownerDetails */ + $ownerDetails = $provider->getResourceOwner($token); + + printf('Hello, %s!', $ownerDetails->getName()); } ``` ### Обновление access токена ```php -$provider = new AmoCRM\OAuth2\Client\Provider\AmoCRM([ - 'clientId' => 'YOUR_CLIENT_ID', - 'clientSecret' => 'YOUR_CLIENT_SECRET', - 'redirectUri' => 'http://your-redirect-uri', +$provider = new AmoCRM([ + 'clientId' => 'xxx', + 'clientSecret' => 'xxx', + 'redirectUri' => 'https://test.test', ]); -$grant = new \League\OAuth2\Client\Grant\RefreshToken(); -$token = $provider->getAccessToken($grant, ['refresh_token' => $refreshToken]); +//todo получение токена из хранилища + +$provider->setBaseDomain($token['baseDomain']); +/** + * Проверяем активен ли токен и делаем запрос или обновляем токен + */ +if (time() >= $token['expires']) { + /** + * Получаем токен по рефрешу + */ + try { + $accessToken = $provider->getAccessToken(new League\OAuth2\Client\Grant\RefreshToken(), [ + 'refresh_token' => $token['refreshToken'], + ]); + + //todo сохраняем новые access, refresh токены и привязку к аккаунту и возможно пользователю + + } catch (Exception $e) { + die((string)$e); + } +} + +//todo повторяем исходный запрос ``` + +### Пример +В рамках данного репозитория имеется файл **example.php**, который рееализует простейшую логику авторизации, сохранения токена, а также совеершения запросов. +Для использования нужно указать корректные значения при создании провайдера. Дальше для теста можно переейти на страницу example.php, после чего будет редирект в приложение amoCRM для авторизации. +После получения доступов вы увидете имя пользователя на экране. +Если добавить GET параметр - request=1, то будет совершен запрос за информацией об аккаунте с сохраненным ранее токеном. \ No newline at end of file diff --git a/composer.json b/composer.json index bc3601c..3c8d1c4 100644 --- a/composer.json +++ b/composer.json @@ -19,7 +19,8 @@ ], "require": { "php": ">=5.6.0", - "league/oauth2-client": "^2.0" + "ext-json": "*", + "league/oauth2-client": "2.4.*" }, "require-dev": { "phpunit/phpunit": "~4.5" diff --git a/example.php b/example.php new file mode 100644 index 0000000..d42d216 --- /dev/null +++ b/example.php @@ -0,0 +1,152 @@ + 'xxx', + 'clientSecret' => 'xxx', + 'redirectUri' => 'https://ya.ru', +]); + +if (isset($_GET['referer'])) { + $provider->setBaseDomain($_GET['referer']); +} + +if (!isset($_GET['request'])) { + if (!isset($_GET['code'])) { + /** + * Получаем ссылку для авторизации и дальше редиректим + */ + $authorizationUrl = $provider->getAuthorizationUrl(); + $_SESSION['oauth2state'] = $provider->getState(); + header('Location: ' . $authorizationUrl); + } elseif (empty($_GET['state']) || ($_GET['state'] !== $_SESSION['oauth2state'])) { + unset($_SESSION['oauth2state']); + exit('Invalid state'); + } + + /** + * Ловим обратный код + */ + try { + /** @var \League\OAuth2\Client\Token\AccessToken $access_token */ + $accessToken = $provider->getAccessToken(new League\OAuth2\Client\Grant\AuthorizationCode(), [ + 'code' => $_GET['code'], + ]); + + if (!$accessToken->hasExpired()) { + saveToken([ + 'accessToken' => $accessToken->getToken(), + 'refreshToken' => $accessToken->getRefreshToken(), + 'expires' => $accessToken->getExpires(), + 'baseDomain' => $provider->getBaseDomain(), + ]); + } + } catch (Exception $e) { + die((string)$e); + } + + /** @var \AmoCRM\OAuth2\Client\Provider\AmoCRMResourceOwner $ownerDetails */ + $ownerDetails = $provider->getResourceOwner($accessToken); + + printf('Hello, %s!', $ownerDetails->getName()); +} else { + $accessToken = getToken(); + + $provider->setBaseDomain($accessToken->getValues()['baseDomain']); + + /** + * Проверяем активен ли токен и делаем запрос или обновляем токен + */ + if ($accessToken->hasExpired()) { + /** + * Получаем токен по рефрешу + */ + try { + $accessToken = $provider->getAccessToken(new League\OAuth2\Client\Grant\RefreshToken(), [ + 'refresh_token' => $accessToken->getRefreshToken(), + ]); + + saveToken([ + 'accessToken' => $accessToken->getToken(), + 'refreshToken' => $accessToken->getRefreshToken(), + 'expires' => $accessToken->getExpires(), + 'baseDomain' => $provider->getBaseDomain(), + ]); + + } catch (Exception $e) { + die((string)$e); + } + } + + $token = $accessToken->getToken(); + + try { + /** + * Делаем запрос к АПИ + */ + $data = $provider->getHttpClient() + ->request('GET', $provider->urlAccount() . 'api/v2/account', [ + 'headers' => $provider->getHeaders($accessToken) + ]); + + $parsedBody = json_decode($data->getBody()->getContents(), true); + printf('ID аккаунта - %s, название - %s', $parsedBody['id'], $parsedBody['name']); + } catch (GuzzleHttp\Exception\GuzzleException $e) { + var_dump((string)$e); + } +} + + +function saveToken($accessToken) { + if ( + isset($accessToken) + && isset($accessToken['accessToken']) + && isset($accessToken['refreshToken']) + && isset($accessToken['expires']) + && isset($accessToken['baseDomain']) + ) { + $data = [ + 'accessToken' => $accessToken['accessToken'], + 'expires' => $accessToken['expires'], + 'refreshToken' => $accessToken['refreshToken'], + 'baseDomain' => $accessToken['baseDomain'], + ]; + + file_put_contents(TOKEN_FILE, json_encode($data)); + } else { + exit('Invalid access token ' . var_export($accessToken, true)); + } +} + +/** + * @return \League\OAuth2\Client\Token\AccessToken + */ +function getToken() { + $accessToken = json_decode(file_get_contents(TOKEN_FILE), true); + + if ( + isset($accessToken) + && isset($accessToken['accessToken']) + && isset($accessToken['refreshToken']) + && isset($accessToken['expires']) + && isset($accessToken['baseDomain']) + ) { + return new \League\OAuth2\Client\Token\AccessToken([ + 'access_token' => $accessToken['accessToken'], + 'refresh_token' => $accessToken['refreshToken'], + 'expires' => $accessToken['expires'], + 'baseDomain' => $accessToken['baseDomain'], + ]); + } else { + exit('Invalid access token ' . var_export($accessToken, true)); + } +} \ No newline at end of file diff --git a/src/AmoCRM.php b/src/AmoCRM.php index e012986..1872d0c 100644 --- a/src/AmoCRM.php +++ b/src/AmoCRM.php @@ -1,26 +1,24 @@ clientSubdomain = $options['clientSubdomain']; + if (isset($options['baseDomain'])) { + $this->baseDomain = $options['baseDomain']; } + } - if (isset($options['clientTopLevelDomain'])) { - $this->clientTopLevelDomain = $options['clientTopLevelDomain']; - } + /** + * @param string $domain + */ + public function setBaseDomain($domain) { + $this->baseDomain = $domain; } - public function urlAccount() - { - return $this->protocol . $this->clientSubdomain . '.amocrm.' . $this->clientTopLevelDomain . '/'; - } + /** + * @return string + */ + public function getBaseDomain() { + return $this->baseDomain; + } - public function urlAuthorize() + /** + * Get authorization url to begin OAuth flow + * + * @return string + */ + public function getBaseAuthorizationUrl() { - return $this->protocol . 'www.amocrm.' . $this->clientTopLevelDomain . '/oauth/'; + return $this->protocol . $this->baseDomain . '/oauth/'; } - public function urlAccessToken() + /** + * Get access token url to retrieve token + * + * @param array $params + * + * @return string + */ + public function getBaseAccessTokenUrl(array $params) { - return $this->protocol . $this->clientSubdomain . '.amocrm.' . $this->clientTopLevelDomain . '/oauth2/access_token'; + return $this->protocol . $this->baseDomain . '/oauth2/access_token'; } - public function urlUserDetails(AccessToken $token) + /** + * Get the default scopes used by this provider. + * + * @return array + */ + protected function getDefaultScopes() { - return $this->protocol . $this->clientSubdomain . '.amocrm.' . $this->clientTopLevelDomain . '/v3/user'; + return []; } - public function userDetails($response, AccessToken $token) - { - $user = new User(); - $user->exchangeArray([ - 'uid' => isset($response->id) ? $response->id : null, - 'name' => isset($response->name) ? $response->name : null, - 'email' => isset($response->email) ? $response->email : null, - ]); + public function urlAccount() + { + return $this->protocol . $this->baseDomain . '/'; + } - return $user; + /** + * Generate a user object from a successful user details request. + * + * @param array $response + * @param AccessToken $token + * @return AmoCRMResourceOwner + */ + protected function createResourceOwner(array $response, AccessToken $token) + { + return new AmoCRMResourceOwner($response); } - - public function getAuthorizationUrl($options = []) + /** + * @inheritDoc + */ + protected function checkResponse(ResponseInterface $response, $data) { - $this->state = isset($options['state']) ? $options['state'] : md5(uniqid(rand(), true)); - - $params = [ - 'client_id' => $this->clientId, - 'redirect_uri' => $this->redirectUri, - 'state' => $this->state, - ]; - - return $this->urlAuthorize().'?'.$this->httpBuildQuery($params, '', '&'); + if ($response->getStatusCode() >= 400) { + throw AmoCRMException::errorResponse($response, $data); + } } + /** + * Get provider url to fetch user details + * + * @param AccessToken $token + * + * @return string + */ + public function getResourceOwnerDetailsUrl(AccessToken $token) + { + return $this->urlAccount() . '/v3/user'; + } } diff --git a/src/AmoCRMException.php b/src/AmoCRMException.php new file mode 100644 index 0000000..7e66d4a --- /dev/null +++ b/src/AmoCRMException.php @@ -0,0 +1,26 @@ +getStatusCode(); + $body = (string)$response->getBody(); + return new static($message, $code, $body); + } +} \ No newline at end of file diff --git a/src/AmoCRMResourceOwner.php b/src/AmoCRMResourceOwner.php new file mode 100644 index 0000000..4553aa5 --- /dev/null +++ b/src/AmoCRMResourceOwner.php @@ -0,0 +1,67 @@ +response = $response; + } + + /** + * Get resource owner id + * + * @return string + */ + public function getId() + { + return $this->getValueByKey($this->response, 'id'); + } + + /** + * Get resource owner name + * + * @return string + */ + public function getName() + { + return $this->getValueByKey($this->response, 'name'); + } + + /** + * Get resource owner email + * + * @return string + */ + public function getEmail() + { + return $this->getValueByKey($this->response, 'email'); + } + + /** + * Return all of the owner details available as an array. + * + * @return array + */ + public function toArray() + { + return $this->response; + } +} \ No newline at end of file diff --git a/test.php b/test.php deleted file mode 100644 index 0dd6265..0000000 --- a/test.php +++ /dev/null @@ -1,129 +0,0 @@ - 'xxx', - 'clientSecret' => 'xxx', - 'redirectUri' => '', - 'clientSubdomain' => 'xxx', - 'clientTopLevelDomain' => 'ru', //use com for us accounts -]); - -if (!isset($_GET['request'])) { - if (!isset($_GET['code'])) { - /** - * Получаем ссылку для авторизации и дальше редиректим - */ - $authorizationUrl = $provider->getAuthorizationUrl(); - $_SESSION['oauth2state'] = $provider->state; - header('Location: ' . $authorizationUrl); - } elseif (empty($_GET['state']) || ($_GET['state'] !== $_SESSION['oauth2state'])) { - unset($_SESSION['oauth2state']); - exit('Invalid state'); - } - - /** - * Ловим обратный код - */ - try { - /** @var \League\OAuth2\Client\Token\AccessToken $access_token */ - $accessToken = $provider->getAccessToken(new League\OAuth2\Client\Grant\AuthorizationCode(), [ - 'code' => $_GET['code'], - ]); - if (isset($accessToken) && - isset($accessToken->accessToken) && - isset($accessToken->refreshToken) && - isset($accessToken->expires) - ) { - saveToken($accessToken); - } - } catch (Exception $e) { - die((string)$e); - } - - /** - * Получаем данные о пользователе - */ - $user = $provider->getUserDetails($accessToken); - - var_dump($user); -} else { - $accessToken = getToken(); - /** - * Проверяем активен ли токен и делаем запрос или обновляем токен - */ - if (time() >= $accessToken->expires) { - /** - * Получаем токен по рефрешу - */ - try { - $accessToken = $provider->getAccessToken(new League\OAuth2\Client\Grant\RefreshToken(), [ - 'refresh_token' => $accessToken->refreshToken, - ]); - - saveToken($accessToken); - - } catch (Exception $e) { - die((string)$e); - } - } - - - try { - /** - * Делаем запрос к АПИ - */ - $data = $provider->getHttpClient() - ->createRequest('GET', $provider->urlAccount() . 'api/v2/account', $provider->getHeaders($accessToken->accessToken), null, ['debug' => true]) - ->send(); - - $parsedBody = json_decode($data->getBody(true), true); - - var_dump($parsedBody); - } catch (Guzzle\Http\Exception\ClientErrorResponseException $e) { - var_dump((string)$e); - } -} - - -function saveToken($accessToken) { - if (isset($accessToken) && - isset($accessToken->accessToken) && - isset($accessToken->refreshToken) && - isset($accessToken->expires) - ) { - $data = [ - 'accessToken' => $accessToken->accessToken, - 'expires' => $accessToken->expires, - 'refreshToken' => $accessToken->refreshToken, - ]; - - file_put_contents(TOKEN_FILE, json_encode($data)); - } else { - exit('Invalid access token ' . var_export($accessToken, true)); - } -} - - -function getToken() { - $accessToken = json_decode(file_get_contents(TOKEN_FILE)); - - if (isset($accessToken) && - isset($accessToken->accessToken) && - isset($accessToken->refreshToken) && - isset($accessToken->expires) - ) { - return $accessToken; - } else { - exit('Invalid access token ' . var_export($accessToken, true)); - } -} \ No newline at end of file