Авторизация в 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" или "agency_client_id" (username или user id из запроса 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|agency_client_id}={client_username|client_user_id}

Authorization Code Grant

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

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

API-клиент перенаправляет пользователя на специальную страницу /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}&user_id={user_id} HTTP/1.1
Host: <redirect_uri:host>
Ответ содержит в том числе параметр user_id, в котором содержится идентификатор пользователя, предоставляющего доступ.

Так же получить user_id по code:

POST /api/v2/oauth2/code_info.json HTTP/1.1
Host: target.my.com
Content-Type: application/x-www-form-urlencoded
code={code}&client_id={client_id}&client_secret={client_secret}
Пример успешного ответа:

HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
 
{
 "user":
 {
    "id": 100500,
    "username": "mytarget@mail.ru",
    "types": ["advert", "agency_client"]
 }
} 
Если в вашем приложении уже есть токен для этого пользователя, можно продолжать использовать существующий, чтобы избежать превышения лимита токенов и не дублировать токены.
Получив параметр "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": ["{scope1}", "{scope2}"],
  "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|user_id}={username|user_id}
где "username" – это логин пользователя (user_id - id пользователя), для которого необходимо удалить токены. Если параметр "username" или "user_id" не передан, то будут удалены токены аккаунта, для которого был выдан доступ к 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-клиента сейчас можно включить только по запросу в службу поддержки (ads_api@vk.team).

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

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

При вызове любого метода 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": "Client is blocked"}
OAuth2-клиент заблокирован. OAuth2-клиент (по сути, доступ к API) может блокироваться в случае нарушений правил использования API и подобных случаях.
Обратитесь в службу поддержки.
{"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 (с использованием токена агентства или менеджера), перестал являться клиентом агентства или быть привязанным к менеджеру.
{ "error": "empty_request_body", "error_description": "Request body is empty. form-urlencoded POST-request required" }
Пустой POST-запрос
Пустой POST-запрос. Проверьте, что параметры переданы в теле POST-запроса, а не в GET-параметрах.
{ "error": "empty_grant_type", "error_description": "grant_type parameter must be non-empty string" }
Пустой grant_type
Не указано значение grant_type
{ "error": "unsupported_grant_type", "error_description": "Unsupported value "%s" of "grant_type" paramenter" }
Параметр grant_type не из следующего списка:
authorization_code,
client_credentials,
refresh_token,
agency_client_credentials
{"error": "invalid_request", "error_description": "Unknown agency client"}
У пользователя нет клиента с указанным в запросе agency_client_name/id
Вам помогла эта статья?