Для примера рассмотрим полный цикл получения доступа к данным, начиная от регистрации новой интеграции. При этом мы будем рассматривать прямую работу с API авторизации, но вы можете либо воспользоваться нашей готовой библиотекой на PHP для упрощения разработки, либо готовыми библиотеками сторонних вендоров, которые можно найти по ссылке.
Мы разрабатывали авторизацию, основываясь на протоколе oAuth 2.0, поэтому вы можете найти в открытых источниках много примеров и документацию, описывающих взаимодействие и логику выполнения запросов.
Все начинается с того, что вам необходимо зайти в раздел “амоМаркет” того аккаунта, в котором вы будете осуществлять поддержку интеграции в будущем. Обратите внимание:
После нажатия на кнопку Создать Интеграцию, в появившейся форме, вам необходимо указать Название интеграции, выбрать требуемые доступы и указать описание. Также необходимо указать Redirect URI – url страницы получения токенов и загрузить логотип интеграции. Кроме того, есть возможность указать url для хука об отключении интеграции – на него придет хук, когда интеграция будет отключена пользователем. Поле для хука об отключении является необязательным.
Свойства интеграции:
Кроме этих пунктов, администраторам аккаунта, в котором создана интеграция, будут доступны: ID интеграции, секретный ключ интеграции, код авторизации (после включения интеграции).
После заполнения полей вам необходимо сохранить интеграцию. После этого новая интеграция будет создана и открыто модальное окно созданной интеграции. В открывшемся модальном окне, на вкладке ключи будут отображены необходимые ключи.
Обратите внимание, что Secret key и Integration ID привязаны к интеграции и будут показаны только в вашем аккаунте разработчика.
Напомним, что код авторизации является временным ключом, который действует только 20 минут. C его помощью вам необходимо в течении этого времени получить refresh token и access токен. Он является временным, т.к. может быть перехвачен, но в случае перехвата злоумышленник не сможет с ним ничего сделать, если вы не сообщите ему ключи приложения, известные только администраторам аккаунта, в котором создана интеграция.
Получить Authorization code можно тремя способами:
Вы можете упростить разработку при использовании способа получения ключа через GET-параметры, используя готовую Кнопку amoCRM.
Полная логика способа получения ключа через GET-параметры заключается в следующем:
При переданном GET-параметре mode со значением post_message в окно предоставление доступов – перенаправление произойдет в самом окне. Ниже рассмотрим пример общения окна предоставления доступов и основного окна с использованием функции postMessage.
Код ниже размещен в основном окне:
var popup;
auth();
// 1. Открывает окно предоставления доступов
function auth() {
popup = window.open('https://www.amocrm.ru/oauth?client_id=XXX&state=XXX&mode=post_message', 'Предоставить доступ', 'scrollbars, status, resizable, width=750, height=580');
}
// 2. Регистрируем обработчика сообщений из popup окна
window.addEventListener('message', updateAuthInfo);
// 3. Функция обработчик, зарегистрированная выше
function updateAuthInfo(e) {
if (e.data.error !== undefined) {
console.log('Ошибка - ' + e.data.error)
} else {
console.log('Авторизация прошла')
}
// 4. Закрываем модальное окно
popup.close();
}
Код ниже будет отдан в модальное окно вашим backend сервером при переходе на Redirect URI:
<!doctype html>
<html lang="en">
<head>
<title>oAuth Postback</title>
<script>
//Передадим данные в родительское окно, набор данных определяете вы
if(window.opener){
window.opener.postMessage({'error': undefined, 'status': 'ok'}, "*");
}
</script>
</head>
</html>
После отработки кода выше, основное окно узнает результат. Рекомендуем закрывать модальное окно автоматически, как это сделано в примере, чтобы у пользователя не возникла путаница в окнах.
Получив Authorization code, вам необходимо сделать запрос на специальный метод /oauth2/access_token, описанный ниже. В ответ вы получите пару Access и Refresh token, а также время в секундах, через которое токен истекает.
Access токен аналогичен ключу сессии. Его можно сохранить в приложении и использовать для запросов к API до истечения времени его жизни. Токен должен быть доступен только вашему приложению, поэтому не рекомендуется сохранять его в cookies браузера, открытых конфигурационных файлах и т. п.
POST /oauth2/access_token
Параметр | Тип данных | Описание |
---|---|---|
client_id | string | ID интеграции |
client_secret | string | Секрет интеграции |
grant_type | string | Тип авторизационных данных (для кода авторизации – authorization_code) |
code | string | Полученный код авторизации |
redirect_uri | string | Redirect URI указанный в настройках интеграции. Должен четко совпадать с тем, что указан в настройках |
{
"client_id": "xxxx",
"client_secret": "xxxx",
"grant_type": "authorization_code",
"code": "xxxxxxx",
"redirect_uri": "https://test.test"
}
Content-Type: application/json
Content-Type: application/json
Код ответа | Условие |
---|---|
200 | Запрос успешно обработан |
400 | Переданы некорректные данные. Подробности доступны в теле ответа |
Параметр | Тип данных | Описание |
---|---|---|
token_type | string | Тип токена |
expires_in | int | Через сколько истекает токен |
access_token | string | Access Token в формате JWT |
refresh_token | string | Refresh Token |
{
"token_type": "Bearer",
"expires_in": 86400,
"access_token": "xxxxxx",
"refresh_token": "xxxxx"
}
curl https://subdomain.amocrm.ru/oauth2/access_token -d \
'{"client_id":"xxx-xxx-xxx-xxx-xxx","client_secret":"xxxxxx","grant_type":"authorization_code","code":"xxxxxxxx","redirect_uri":"https://test.test/"}' \
-H 'Content-Type:application/json' \
-X POST
<?php
$subdomain = 'test'; //Поддомен нужного аккаунта
$link = 'https://' . $subdomain . '.amocrm.ru/oauth2/access_token'; //Формируем URL для запроса
/** Соберем данные для запроса */
$data = [
'client_id' => 'xxxx',
'client_secret' => 'xxxx',
'grant_type' => 'authorization_code',
'code' => 'xxxxxx',
'redirect_uri' => 'https://test.ru/',
];
/**
* Нам необходимо инициировать запрос к серверу.
* Воспользуемся библиотекой cURL (поставляется в составе PHP).
* Вы также можете использовать и кроссплатформенную программу cURL, если вы не программируете на PHP.
*/
$curl = curl_init(); //Сохраняем дескриптор сеанса cURL
/** Устанавливаем необходимые опции для сеанса cURL */
curl_setopt($curl,CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl,CURLOPT_USERAGENT,'amoCRM-oAuth-client/1.0');
curl_setopt($curl,CURLOPT_URL, $link);
curl_setopt($curl,CURLOPT_HTTPHEADER,['Content-Type:application/json']);
curl_setopt($curl,CURLOPT_HEADER, false);
curl_setopt($curl,CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($curl,CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($curl,CURLOPT_SSL_VERIFYPEER, 1);
curl_setopt($curl,CURLOPT_SSL_VERIFYHOST, 2);
$out = curl_exec($curl); //Инициируем запрос к API и сохраняем ответ в переменную
$code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
curl_close($curl);
/** Теперь мы можем обработать ответ, полученный от сервера. Это пример. Вы можете обработать данные своим способом. */
$code = (int)$code;
$errors = [
400 => 'Bad request',
401 => 'Unauthorized',
403 => 'Forbidden',
404 => 'Not found',
500 => 'Internal server error',
502 => 'Bad gateway',
503 => 'Service unavailable',
];
try
{
/** Если код ответа не успешный - возвращаем сообщение об ошибке */
if ($code < 200 || $code > 204) {
throw new Exception(isset($errors[$code]) ? $errors[$code] : 'Undefined error', $code);
}
}
catch(\Exception $e)
{
die('Ошибка: ' . $e->getMessage() . PHP_EOL . 'Код ошибки: ' . $e->getCode());
}
/**
* Данные получаем в формате JSON, поэтому, для получения читаемых данных,
* нам придётся перевести ответ в формат, понятный PHP
*/
$response = json_decode($out, true);
$access_token = $response['access_token']; //Access токен
$refresh_token = $response['refresh_token']; //Refresh токен
$token_type = $response['token_type']; //Тип токена
$expires_in = $response['expires_in']; //Через сколько действие токена истекает
Из предыдущего пункта вы заметили, что вместе с Access token был также возвращен Refresh token. Он необходим для продолжения работы с API, по истечении срока действия Access токена, т.е. это довольно частая операция.
Refresh token имеет два существенных ограничения его жизни:
По истечении срока действия возможность получения Access токена по нему становится невозможной. Для обмена необходимо сделать запрос на специальный метод с действительным Refresh токеном. В ответ вы получите новый Access и Refresh токены.
POST /oauth2/access_token
Параметр | Тип данных | Описание |
---|---|---|
client_id | string | ID интеграции |
client_secret | string | Секрет интеграции |
grant_type | string | Тип авторизационных данных (для кода авторизации – refresh_token) |
refresh_token | string | Refresh токен |
redirect_uri | string | Redirect URI указанный в настройках интеграции. Должен четко совпадать с тем, что указан в настройках |
{
"client_id": "xxxx",
"client_secret": "xxxx",
"grant_type": "refresh_token",
"refresh_token": "xxxxx",
"redirect_uri": "https://test.test"
}
Content-Type: application/json
Content-Type: application/json
Код ответа | Условие |
---|---|
200 | Запрос успешно обработан |
400 | Переданы некорректные данные. Подробности доступны в теле ответа |
Параметр | Тип данных | Описание |
---|---|---|
token_type | string | Тип токена |
expires_in | int | Через сколько истекает токен |
access_token | string | Access Token в формате JWT |
refresh_token | string | Refresh Token |
{
"token_type": "Bearer",
"expires_in": 86400,
"access_token": "xxxxxx",
"refresh_token": "xxxxx"
}
curl https://subdomain.amocrm.ru/oauth2/access_token -d \
'{"client_id":"xxx-xxx-xxx-xxx-xxx","client_secret":"xxxxxx","grant_type":"refresh_token","refresh_token":"xxxxxxxx","redirect_uri":"https://test.test/"}' \
-H 'Content-Type:application/json' \
-X POST
<?php
$subdomain = 'test'; //Поддомен нужного аккаунта
$link = 'https://' . $subdomain . '.amocrm.ru/oauth2/access_token'; //Формируем URL для запроса
/** Соберем данные для запроса */
$data = [
'client_id' => 'xxxx',
'client_secret' => 'xxxx',
'grant_type' => 'refresh_token',
'refresh_token' => 'xxxxxx',
'redirect_uri' => 'https://test.ru/',
];
/**
* Нам необходимо инициировать запрос к серверу.
* Воспользуемся библиотекой cURL (поставляется в составе PHP).
* Вы также можете использовать и кроссплатформенную программу cURL, если вы не программируете на PHP.
*/
$curl = curl_init(); //Сохраняем дескриптор сеанса cURL
/** Устанавливаем необходимые опции для сеанса cURL */
curl_setopt($curl,CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl,CURLOPT_USERAGENT,'amoCRM-oAuth-client/1.0');
curl_setopt($curl,CURLOPT_URL, $link);
curl_setopt($curl,CURLOPT_HTTPHEADER,['Content-Type:application/json']);
curl_setopt($curl,CURLOPT_HEADER, false);
curl_setopt($curl,CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($curl,CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($curl,CURLOPT_SSL_VERIFYPEER, 1);
curl_setopt($curl,CURLOPT_SSL_VERIFYHOST, 2);
$out = curl_exec($curl); //Инициируем запрос к API и сохраняем ответ в переменную
$code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
curl_close($curl);
/** Теперь мы можем обработать ответ, полученный от сервера. Это пример. Вы можете обработать данные своим способом. */
$code = (int)$code;
$errors = [
400 => 'Bad request',
401 => 'Unauthorized',
403 => 'Forbidden',
404 => 'Not found',
500 => 'Internal server error',
502 => 'Bad gateway',
503 => 'Service unavailable',
];
try
{
/** Если код ответа не успешный - возвращаем сообщение об ошибке */
if ($code < 200 || $code > 204) {
throw new Exception(isset($errors[$code]) ? $errors[$code] : 'Undefined error', $code);
}
}
catch(\Exception $e)
{
die('Ошибка: ' . $e->getMessage() . PHP_EOL . 'Код ошибки: ' . $e->getCode());
}
/**
* Данные получаем в формате JSON, поэтому, для получения читаемых данных,
* нам придётся перевести ответ в формат, понятный PHP
*/
$response = json_decode($out, true);
$access_token = $response['access_token']; //Access токен
$refresh_token = $response['refresh_token']; //Refresh токен
$token_type = $response['token_type']; //Тип токена
$expires_in = $response['expires_in']; //Через сколько действие токена истекает
В феврале 2024 мы добавили возможность создавать долгосрочные токены для внешних и приватных интеграций.
Для создания необходимо перейти на вкладку “Ключи”, нажать кнопку “Сгенерировать токен”, выбрать дату окончания действия токена и скопировать токен. После того, как токен создан, вы увидете его только один раз, поэтому его необходимо скопировать.
Важно отметить, что подобные токены подойдут для небольших интеграций, которые разрабатываются под конкретный аккаунт. Срок действия токена пользователь выбирает сам и он действует от 1 дня до 5 лет. Кроме этого, во вкладке “Выданные доступы” долгосрочные токены имеют не только дату выдачи, но и дату окончания действия, но вы всегда можете отозвать сами нажав на кнопку “Отозвать доступ”.
Долгосрочные токены не имеют refresh_token и поэтому не требуют обмена и не требуют написания логики, которая будет следить за актуальностью токенов.
C помощью полученного Access токена вы можете легко делать запросы к API приложения.
Вам не нужно отправлять куки-файлы с каждым запросом, также вам нужно добавить заголовок Authorization: Bearer {access токен} во все ваши запросы к API amoCRM.
<?php
$subdomain = 'test'; //Поддомен нужного аккаунта
$link = 'https://' . $subdomain . '.amocrm.ru/api/v4/account'; //Формируем URL для запроса
/** Получаем access_token из вашего хранилища */
$access_token = 'xxxx';
/** Формируем заголовки */
$headers = [
'Authorization: Bearer ' . $access_token
];
/**
* Нам необходимо инициировать запрос к серверу.
* Воспользуемся библиотекой cURL (поставляется в составе PHP).
* Вы также можете использовать и кроссплатформенную программу cURL, если вы не программируете на PHP.
*/
$curl = curl_init(); //Сохраняем дескриптор сеанса cURL
/** Устанавливаем необходимые опции для сеанса cURL */
curl_setopt($curl,CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl,CURLOPT_USERAGENT,'amoCRM-oAuth-client/1.0');
curl_setopt($curl,CURLOPT_URL, $link);
curl_setopt($curl,CURLOPT_HTTPHEADER, $headers);
curl_setopt($curl,CURLOPT_HEADER, false);
curl_setopt($curl,CURLOPT_SSL_VERIFYPEER, 1);
curl_setopt($curl,CURLOPT_SSL_VERIFYHOST, 2);
$out = curl_exec($curl); //Инициируем запрос к API и сохраняем ответ в переменную
$code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
curl_close($curl);
/** Теперь мы можем обработать ответ, полученный от сервера. Это пример. Вы можете обработать данные своим способом. */
$code = (int)$code;
$errors = [
400 => 'Bad request',
401 => 'Unauthorized',
403 => 'Forbidden',
404 => 'Not found',
500 => 'Internal server error',
502 => 'Bad gateway',
503 => 'Service unavailable',
];
try
{
/** Если код ответа не успешный - возвращаем сообщение об ошибке */
if ($code < 200 || $code > 204) {
throw new Exception(isset($errors[$code]) ? $errors[$code] : 'Undefined error', $code);
}
} catch(\Exception $e)
{
die('Ошибка: ' . $e->getMessage() . PHP_EOL . 'Код ошибки: ' . $e->getCode());
}
Подобным образом можно делать запросы ко всем методам API, на которые у токена хватает прав. Токен имеет права пользователя, который предоставил доступ.
Начиная с ноября 2021 года, вы можете указать адрес, на который будет отправлен запрос, когда интеграция будет отключена. После получения хука вы сможете остановить интеграцию, ограничить запросы к аккаунту, в котором произошло отключение.
Мы рекомендуем указывать данную ссылку, так как это поможет вам не делать лишних запросов к API, отслеживать когда интеграция была отключена.
Что бы удостоверится, что хук действительно пришел от amoCRM, а не от злоумышлеников, рекомендуется проверять подпись хука, передаваемую в GET-параметре signature, а так же id oauth-клиента интеграции, передаваемый в GET-параметре client_uuid. В качестве подписи хука передается HMAC, сообщением является конкатенация id oauth-клиента интеграции с id аккаунта, для которого отзывается токен, ключем – секретный ключ oauth-клиента интеграции, алгоритм шифровния – sha256.
<?php
// id oauth-клиента интеграции
$clientId = 'xxx';
// секретный ключ oauth-клиента интеграции
$clientSecret = 'xxx';
if (empty($_GET['client_uuid']) || empty($_GET['signature']) || empty($_GET['account_id'])) {
throw new \HttpInvalidParamException('Wrong hook format');
}
$hookClientId = $_GET['client_uuid'];
$hookSignature = $_GET['signature'];
$hookAccountId = (int) $_GET['account_id'];
if ($clientId !== $hookClientId) {
throw new \Exception('Invalid hook signature');
}
if (!hash_equals(
hash_hmac('sha256', sprintf('%s|%s', $clientId, $hookAccountId), $clientSecret),
$hookSignature
)) {
throw new \Exception('Invalid hook signature');
}
// проверка подлинности пройденна, можно вызывать вашу логику обработки хука
В ходе отработки всей вышеуказанной логики может возникать ряд исключений, которые необходимо обрабатывать, рассмотрим каждое из них: