Авторизация в API

Реализация протокола OAuth2 в API

Для аутентификации и авторизации в API используется протокол OAuth2, из спецификации которого реализовано две стандартные схемы (flow): Client Credentials Grant и Authorization Code Grant, и одна нестандартная – Agency Client Credentials Grant.

Client Credentials Grant используется для работы с данными собственного аккаунта через API.

Agency Client Credentials Grant используется для работы с данными собственных клиентов агентств\менеджеров.

Authorization Code Grant используется для получения доступа к данным сторонних аккаунтов myTarget.

Первые две схемы доступны каждому API-клиенту, доступ к схеме Authorization Code Grant предоставляется только при выполении условий.

Инструкция по получению доступа к API

Используя любой из данных методов, будет получен объект Access Token, содержащий ключи access_token и refresh_token. Ключом access_token должен быть подписан каждый запрос к API:

GET /api/v2/campaigns.json HTTP/1.1
Host: target.my.com
Authorization: Bearer {access_token}

Следует помнить, что доступ к данным аккаунта возможен только с токеном, полученным для данного аккаунта. Это значит, что нельзя, к примеру, просмотреть кампании клиента агентства, подписав запрос токеном самого агентства. Подробнее о типах аккаунта и процессе авторизации в расширенной инструкции по авторизации. 

Client Credentials Grant

Чтобы получить токен доступа, нужно послать запрос вида:

POST /api/v2/oauth2/token.json HTTP/1.1
Host: target.my.com
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials&client_id={client_id}&client_secret={client_secret}

В случае успешного выполнения ответ будет выглядеть следующим образом:

HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
    
{
  "access_token": "{access_token}",
  "token_type": "bearer",
  "scope": "{scope}",
  "expires_in": "86400",
  "refresh_token": "{refresh_token}"
}

Agency Client Credentials Grant

Эта схема протокола OAuth2 не является стандартной. Она была реализована для того, чтобы дать возможность агентствам и менеджерам создавать токены доступа для своих клиентов без подтверждения от клиента. Схема очень похожа на стандартную Client Credentials Grant за исключением того, что в запросе нужно передавать дополнительный параметр "agency_client_name" (username из запроса AgencyClients или ManagerClients):

POST /api/v2/oauth2/token.json HTTP/1.1
Host: target.my.com
Content-Type: application/x-www-form-urlencoded
    
grant_type=agency_client_credentials&client_id={client_id}&client_secret={client_secret}&agency_client_name={client_username}

Authorization Code Grant

Эта схема протокола OAuth2 позволяет получить токен стороннего пользователя myTarget. При создании доступа к схеме Authorization Code Grant должен указываться адрес "redirect_uri" - на него myTarget будет перенаправлять пользователей после предоставления (или отказа) ими доступа к своему аккаунту API-клиенту.

Алгоритм получения доступа выглядит следующим образом:

API-клиент перенаправляет пользователя на специальную страницу https://target.my.com/oauth2/authorize, указав параметры "response_type" (со значением "code"), "state" (сгенерированный на стороне клиента токен, используется для предотвращения CSRF - может содержать происзвольный набор символов), свой "client_id" и список прав доступа "scope":

GET /oauth2/authorize?response_type=code&client_id={client_id}&state={state}&scope={scopes} HTTP/1.1
Host: target.my.com

Пользователь на странице соглашается дать доступ и сервис перенаправляет его по адресу, заданному параметром "redirect_uri" при регистрации клиента, передавая параметры "code" (специальный токен со сроком жизни в один час) и "state" (то же значение, что было передано в первоначальном запросе):

GET {redirect_uri:path}?code={code}&state={state} HTTP/1.1
Host: <redirect_uri:host>

Получив параметр "code", клиент может запросить "access_token" для дальнейшей работы с API от имени пользователя. Для этого нужно послать запрос на /api/v2/oauth2/token.json, передав параметры "grant_type" (со значением "authorization_code"), "code" (полученный при обратном редиректе на "redirect_uri" токен):

POST /api/v2/oauth2/token.json HTTP/1.1
Host: target.my.com
Content-Type: application/x-www-form-urlencoded
    
grant_type=authorization_code&code={code}&client_id={client_id}

Ответ:

HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
{
  "access_token": "{access_token}",
  "token_type": "bearer",
  "scope": "{scope}",
  "expires_in": "86400",
  "refresh_token": "{refresh_token}"
}

Полученный токен доступа используется для аутентификации запросов, посылаемых в API от имени пользователя:

GET /api/v2/campaigns.json HTTP/1.1
Host: target.my.com
Authorization: Bearer {access_token}

Для получения токенов клиентов предоставившего доступа агентства или менеджера, необходимо воспользоваться расширенной схемой Agency Client Credentials. Для этого помимо параметра agency_client_name и других, необходимо указать полученных токен агентства в параметре access_token:

POST /api/v2/oauth2/token.json HTTP/1.1
Host: target.my.com
Content-Type: application/x-www-form-urlencoded
    
grant_type=agency_client_credentials&client_id={client_id}&client_secret={client_secret}&agency_client_name={client_username}&access_token={agency_access_token}

В случае успешного выполнения запроса, в ответе будет содержаться токен доступа для осуществления операций от имени клиента агентства или менеджера.

Scopes — права доступа

Права доступа определяют какие действия может произвести API-клиент с данными, предоставившего доступ аккаунта. Необходимые права указываются через запятую в параметре "scope" запроса доступа у пользователя в схеме Authorization Code Grant. В зависимости от типа пользователя запрашиваемые права доступа делятся на три группы.

Для обычного пользователя-рекламодателя:

  • read_ads — чтение статистики и РК;
  • read_payments — чтение денежных транзакций и баланса;
  • create_ads — создание и редактирование настроек РК, баннеров, аудиторий (ставки, статус, таргетинги и т.п.).

Для пользователей-агентств и пользователей-представительств:

  • create_clients — создание новых клиентов;
  • read_clients — просмотр клиентов и операции от их имени;
  • create_agency_payments — переводы средств на счёта клиентов и обратно.

Для пользователей-менеджеров:

  • read_manager_clients — просмотр клиентов и операции от их имени;
  • edit_manager_clients — изменение параметров клиентов;
  • read_payments — чтение денежных транзакций и баланса;

В одном запросе могут быть указаны права разных групп. myTarget определяет тип аккаунта текущего пользователя и открывает только соответствующие права. Более того, если в запросе, к примеру, перечислены все права, и пользователь при этом является агентством, то ему будет предложено выбрать к какому аккаунту он хочет дать доступ - к агентсткому с агентскими правами, какому-либо из менеджерских с менеджерскими или к одному из клиентских с правами доступа к клиентским данным. 

Работа с токенами

Лимит на количество токенов

Для каждой связки clientId - user одновременно может существовать не более 5 токенов, вне зависимости от статуса токена. Если один и тот же аккаунт подключен к двум различным приложениям, то каждое из приложений сможет выписать по 5 токенов для данного аккаунта. Лимит фиксирован и не может быть увеличен ни в каких случаях.

Не вечные токены автоматически удаляются по истечении месяца неактивности (указано в поле "expires_in"). 

По достижении лимита в ответ на попытку получить новый токен будет возвращена ошибка с HTTP-кодом 403.

Во избежание подобных ошибок, необходимо корректно обновлять выписанные токены и не создавать их избыточные копии.

Удаление токенов

При достижении лимита на количество токенов можно самостоятельно удалить все токены конкретного пользователя. Для этого используется запрос вида:

POST /api/v2/oauth2/token/delete.json HTTP/1.1
Host: target.my.com
Content-Type: application/x-www-form-urlencoded
    
client_id={client_id}&client_secret={client_secret}&username={username}

где "username" – это логин пользователя, для которого необходимо удалить токены. Если параметр "username" не передан, то будут удалены токены аккаунта, для которого был выдан доступ к API.

Срок действия access-токена

Каждый полученный токен доступа по умолчанию является действительным в течение суток. На это указывает свойство "expires_in" в ответе на запрос токена доступа. Ограниченный срок жизни позволяет более надёжно защитить значение "access_token". Даже завладев значением одного "access_token", злоумышленник не сможет выполнить запросы с ним по истечении срока действия или после первого обновления токена. 

Менее безопасный способ – "вечные" токены доступа. Для получения "access_token" без ограничения срока действия, необходимо добавить GET-параметр "permanent=true" в запрос создания или обновления токена. Например:

POST /api/v2/oauth2/token.json HTTP/1.1
Host: target.my.com
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials&client_id={client_id}&client_secret={client_secret}&permanent=true

Обновление токена доступа

В объекте токена Access Token также указывается ключ "refresh_token" — специальный токен для обновления ключа access_token и продления времени жизни объекта. За это отвечает схема Refreshing an Access Token в протоколе OAuth2.

Запрос на обновление токена доступа:

POST /api/v2/oauth2/token.json HTTP/1.1
Host: target.my.com
Content-Type: application/x-www-form-urlencoded
    
grant_type=refresh_token&refresh_token={refresh_token}&client_id={client_id}&client_secret={client_secret}

Пример ответа:

HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
    
{
  "access_token": "{new_access_token}",
  "token_type": "bearer",
  "scope": "{scope}",
  "expires_in": "86400",
  "refresh_token": "{refresh_token}"
}

Важно учитывать, что обновление токена не создаёт новый экземпляр: меняется значение "access_token", старое значение ключа перестаёт работать. Это может привести к проблемам при работе с API в несколько потоков: два потока могут одновременно обнаружить, что токен истёк и отправить запрос на обновление. Запрос, пришедший первым, обновит токен и начнёт его использовать, в то время как второй поток обновит токен ещё раз, и первый поток будет пытаться использовать уже несуществующий токен.

Одним из вариантов решения этой проблемы является перехват ошибки о несуществующем токене в первом потоке и получение токена заново из общего для потоков хранилища – например, базы данных, куда каждый поток записывает токен после обновления. Нужно также учесть, что запись в хранилище тоже может быть конкурентной, и использовать, например, блокировки.

Другим вариантом решения этой проблемы может быть включение опции обновления "refresh_token" при каждом обновлении "access_token". Тогда первый поток обновит и "access_token", и "refresh_token", а во втором нужно обработать ошибку о неизвестном "refresh_token" и перечитать "access_token" из хранилища. Но при обновления "refresh_token" нужно обязательно хранить его самое последнее значение, иначе вы больше не сможете обновить "access_token", и придётся выписывать новый. Эту опцию OAuth-клиента сейчас можно включить только по запросу в службу поддержки.

Ещё один вариант: превентивное обновление всех истекающих токенов. Он заключается в том, что нужно регулярно проверять нет ли у вас токенов, истекающих, например, в ближайшие пол часа, и обновлять их в фоновом процессе. Но если совмещать его с обновлением в режиме реального времени в рабочих процессах, то всё равно придётся обрабатывать ошибки из-за возможных конфликтов.

Ошибки при использовании некорректных токенов

При вызове любого метода API могут возвращаться различные ошибки, связанные с некорректным значением или состоянием "access_token". 

Такие ошибки имеют код 401 и общую структуру: 

{"code": "Код ошибки", "message": "Описание ошибки"}

Ответ также будет содержать заголовок

WWW-Authenticate: Bearer realm="api", error="Код ошибки", error_description="Описание ошибки"

Возможные ошибки:

Ошибка Причина Решение
{"code": "invalid_token", "message": "Unknown access token"} Указанный "access_token" не существует. Ошибка также возможна в том случае, если токен не обновлялся в течение месяца и, соответственно, был автоматически удален. Выписать новый токен для данного пользователя и повторить запрос, используя этот токен.
{"code": "expired_token", "message": "Access token is expired"} У "access_token" истёк срок действия, но он ещё не удалён. Обновить "access_token" с помощью метода "refresh_token".
{"code": "invalid_client", "message": "Invalid client id or secret provided"} Ошибка может отдаваться в нескольких случаях:
Неправильный "client_id".
Неправильный "client_secret".
OAuth2-клиент заблокирован. OAuth2-клиент (по сути, доступ к API) может блокироваться в случае нарушений правил использования API и подобных случаях.
Проверьте, что вы передаёте правильные значения "client_id" и "client_secret". Обратитесь в службу поддержки.
{"code": "invalid_user", "message": "User is blocked"} Пользователь, для которого был выписан токен, заблокирован. Отправить запрос с токеном другого пользователя.
{"code": "revoked_token", "message": "Access token has been revoked"} Пользователь, ранее предоставивший вашему приложению доступ к его аккаунту, отозвал этот доступ. Запросить у пользователя повторное предоставление доступа. Также рекомендуется прекратить любые запросы от имени данного пользователя, пока он повторно не предоставит доступ.
{"code": "revoked_token", "message": "Access token has been revoked"} Пользователь, получивший токен через схему Agency Client Credentials (с использованием токена агентства или менеджера), перестал являться клиентом агентства или быть привязанным к менеджеру.