
Недавно в работе с одним из наших клиентов мы столкнулись с проблемой в пользовательском сценарии: VK API требует конкретный, железный URL для редиректа после авторизации. А у нас были сотни ссылок с динамическими параметрами, с которым могла начаться авторизация.
Меня зовут Фёдор Макареев
, я frontend-разработчик в Evrone. В статье расскажу, как я применил Broadcast Channel API, чтобы не терять состояние до авторизации и не бесить пользователей.
Решил попробовать себя в работе с API Вконтакте и наткнулся на интересную особенность.
https://oauth.vk.com/authorize"
+ "?client_id=123" // ID вашего приложения
+ "&display=page"
+ "&redirect_uri=https://oauth.vk.com/blank.html" // URL, на который попадёт пользователь после успешной авторизации. ВК требует указания этого сайта в настройках приложения и только для приложения типа Сайт. Для остальных работает только указанный УРЛ, в целях безопасности
+ "&scope= права доступа"
+ "&response_type=token"
+ "&v=5.52"
+ "&state=123456"
Это авторизация методом Implicit flow.
Т.е. работа ведётся следующим образом — пользователь П1 авторизуется ВКонтакте через приложение. В приведённом примере я использую standalone-приложение, но, возможно, получится и с другими видами приложений. Дальше пользователь П1 нажимает на ссылку выхода из ВК, приведённую выше. После этого ни через браузер, ни через приложение зайти в ВК без авторизации не получится
. Дальше пользователь П2 авторизуется через приложение. Затем, уже через браузер пользователь попадает на страницу пользователя П1, вместо своей страницы П2. И может делать с этой страницей абсолютно всё, что угодно, приложение на данном этапе уже не нужно и не играет никакой роли.
Самое смешное то, что вы можете использовать в вашем приложении самые безобидные права. То есть не нужно пугать пользователя тем, что вы просите доступ к фоткам, аудио, видео, сообщениям и всему остальному. Можно оставить только базовые — и всё. Куки при этом всё равно будет полностью рабочими, используя их вы попадёте прямиком на страницу пользователя.
Можно ли считать данную особенность уязвимостью — думайте сами. По-моему, это как минимум не то, чего ожидает пользователь, нажимая на кнопку «Выход» и надеясь на то, что теперь уж его страница в безопасности.
При написании данной заметки ни один пользователь ВК не пострадал.
UPD. При авторизации использовался браузер IE7.
В поддержку я писал задолго до того, как опубликовал статью на здесь. Собственно, публикация статьи здесь и произошла в результате того, что поддержка данный сигнал проигнорировала в духе «это не баг, а фича, но, возможно, что-то изменится в будущем».
For the detailed information about the VK auth process, see the official
documentation: https://vk.com/dev/access_token
Ways to show the login page ¶
Using the VK SDK Android — Auth feature, you can:
- use
login
method and way to show the page will be chosen automatically - force to use the WebView using the
VkAuth.AuthMode.RequireWebView
method - force to use the VK App using the
VkAuth.AuthMode.RequireApp
methods- exception will be thrown if app is not installed, but you can manually check this fact using the
methodVkAuth.isVkAppInstalled
- exception will be thrown if app is not installed, but you can manually check this fact using the
Ways to retrieve the auth result ¶
In both cases, with the VK App or with WebView, some Activities will be opened using the startActivityForResult
method.
Using Custom Tabs, onNewIntent
listener will be added.
Use the inline callback ¶
The first param is androidx.activity.ComponentActivity
(e.g. androidx.appcompat.app.AppCompatActivity
):
// must be called before onCreate
Chrome Custom Tabs ¶
To support auth via Chrome Custom Tabs, you need:
1. Prepare your VK app ¶
First of all, you need to add the redirect URI to the app settings.
For this, go here: https://vk.com/apps?act=manage
Choose your app, and then go to the Settings
: https://vk.com/editapp?id=XXX§ion=options
And add your redirect URI to the Authorized redirect URI:
field.
2. Подготовьте свой веб-сайт ¶
Вам необходимо сделать файл JSON доступным здесь: https://domain.com/.well-known/assetlinks.json
«Возьмите SHA256 из ./gradlew SigningReport»
3. Подготовьте AndroidManifest.xml ¶
Сделайте вашу аутентификацию (откуда вы будете выполнять аутентификацию) видимой:
4. Подготовьтесь к занятию ¶
В вашей активности аутентификации, непосредственно перед onCreate
, позвони VkAuth.register(this)
и используйте возвращенное ActivityResultLauncher
.
И вуаля! ¶
Параметры ¶
// Отсюда: https://vk.com/apps?act=manage // Выберите приложение и получите идентификатор здесь: https://vk.com/editapp?id=XXX // Или зайдите сюда: https://vk.com/editapp?id=XXX§ion=options и посмотрите ID приложения // Требуется только в том случае, если вы используете аутентификацию на стороне сервера // Поддерживается только при использовании WebView или CustomTabs; игнорируется приложением ВК // См.: https://vk.com/dev/permissions // Может быть пустым, поэтому токен будет действителен в течение одного дня // Вы можете использовать строковые значения, разделенные запятыми // Или целочисленные значения // Или предопределенные константы // URL-адрес перенаправления после успешной или неудачной аутентификации // Эта страница не будет отображаться. // По умолчанию используется VkAuth. VK_REDIRECT_URI_DEFAULT = "https://oauth.vk.com/blank.html" // Но его необходимо переопределить, чтобы поддерживать пользовательские вкладки. // Тип отображения страницы авторизации // По умолчанию — VkAuth. Отображать. Мобильный // . Андроид и . Ios — это частные значения, используемые официальными приложениями ВК . // потому что официальные приложения также используют WebView // Поддерживается только при использовании WebView или CustomTabs; игнорируется приложением ВК // это значение по умолчанию и рекомендуемое значение // Произвольная строка, которая будет возвращена вместе с результатом авторизации. // Поддерживается только при использовании WebView или CustomTabs; игнорируется приложением ВК // по умолчанию пусто // Если установлено значение false, веб-страница не будет отображаться // истинно по умолчанию // См.: https://vk.com/dev/versions // 5.113 по умолчанию
// требуется для пользовательских вкладок // все остальные параметры необязательны
* в противном случае будет выдана ошибка * Официальное приложение ВК использоваться не будет, даже если оно доступно, оно не будет проверено. * В противном случае, если пользовательские вкладки Chrome доступны, они будут использоваться. * В противном случае будет использоваться WebView. * Официальное приложение ВК использоваться не будет, даже если оно доступно, оно не будет проверено. * Пользовательские вкладки Chrome не будут использоваться, даже если они доступны, они не будут проверены. * Всегда будет использоваться WebView. * Если официальное приложение ВК доступно, будет использовано оно. * В противном случае, если пользовательские вкладки Chrome доступны, они будут использоваться. * В противном случае будет использоваться WebView.
Обработка результата ¶
VkAuthResult
является запечатанным классом.
Если responseType
это VkAuth.ResponseType.AccessToken
, вы получите VkAuthResult.AccessToken
.
Если responseType
это VkAuth.ResponseType.Code
, вы получите VkAuthResult.Code
.
Если аутентификация не удалась и произошла какая-то ошибка, вы получите VkAuthResult.Error
.
Проверьте error
, errorReason
и errorDescription
поля результата.
Но если страница не отобразилась и перед процессом аутентификации произошла какая-то ошибка, эти поля будут пустыми и exception
поле будет содержать исключение.
Example for AccessToken
:
/* do something with result.error */
// before activity.onCreate
// somewhere onClick
Example for Code
:
/* do something with result.code, result.state */
/* do something with result.error, etc. or with result.exception */
// before activity.onCreate
// somewhere onClick
В этом топике я постараюсь рассказать о работе с API вконтакте из расширения для Google Chrome.
По сути, самая сложная часть это получение токена для доступа к API вконтакте, но обо всём по порядку. Для пущей наглядности я приведу пример минимально полезного расширения (что бы оно хоть что-то полезное делало, а вообще оно было сделано для удобного рехостинга гифок). И так расширение будет простое, но рабочее.
Что делает это расширение:
1. Регистрируется в контекстном меню браузера.
2. Получает URL картинки, на которой кликнули и выбрали пункт меню нашего расширения.
3. Авторизуется по протоколу OAuth 2.0 и получает доступ к API вконтакте (если его ещё нет), а также сохраняет токен в хранилище google chrome.
4. Загружает картинку из кэша браузера в blob с помощью XMLHttpRequest.
5. Выполняет несколько запросов к API вконтакте и сохраняет картинку в разделе Документы (на сайте вконтакте). Заодно показывая результат работы в конце.
Почему раздел документы? Очень просто, лимит 200мб на файл и неограниченное место под документы, плюс отличная скорость загрузки.
Я полагаю, что базовые знания по расширениям google chrome у вас уже есть, иначе вам стоит почитать документацию
– там всё очень просто. И тут на хабре
было достаточно постов даже для самых начинающих. Так же посмотрите, как регистрировать приложения
для вконтакте, там тоже всё очень просто. Вам всё равно придётся это прочитать, так что не откладывайте. По быстрому зарегистрировать приложение для вконтакте можно по этой ссылке
.
Все исходники расширения доступны на github
. Я привожу тут кусочки кода из этого самого расширения для наглядности. Само же расширение (уже собранное, одобренное и готовое к работе) находится по этой ссылке
.
Начнём с краткого описания компонентов нашего расширения:
manifest.json
– манифест, основной файл расширения. Описание нашего расширения со всеми опциями и правами доступа.
background.js
– основной скрипт который регистрирует и получает уведомления о клике на картинке, а так же получает токен для доступа к API вконтакте.
upload.html
– страница-заглушка, показывающая, что расширение чем-то занято (отображается анимированный гиф) пока работает upload.js.
upload.js
– скрипт выполняющий непосредственно загрузку картинки на сервер вконтакте.
wait.gif
– собственно гифка – прогресс бар.
imageinfo-128.png
– иконка приложения большая.
imageinfo-16.png
– иконка приложения маленькая.
imageinfo-48.png
– иконка приложения средненькая.
Теперь более детально о важных вещах:
В манифесте расширения обозначены требуемые нам для работы компоненты:
"permissions": [ "contextMenus", "tabs", "storage" , "http://*/*", "https://*/*" ]
Мы получаем доступ к контекстному меню браузера, к вкладкам, к хранилищу браузера и расширение будет активно на всех открываемых страницах.
Далее, в нашем основном скрипте, описанном в манифесте как:
"background": { "scripts": [ "background.js" ]}
Добавляем наш пунктик в контекстное меню браузера
:
chrome.contextMenus.create(
{
"title": "Rehost on vk.com",
"type": "normal",
"contexts": ["image"],
"onclick": getClickHandler()
});
Соответственно по клику на нашем пункте в контекстном меню будет вызвана функция getClickHandler(). Далее, в getClickHandler() уже начинается интересное. Для начала проверяем токен для доступа к API вконтакте в хранилище google chrome
, вдруг мы уже его получили.
chrome.storage.local.get({'vkaccess_token': {}}, function(items) { ...
storage API
в google chrome – асинхронное, поэтому приходится работать с ним таким образом. В этом расширении я использую локальное хранилище, хотя ничто не мешает использовать специальное, sync хранилище, которое будет синхронизироваться со всеми вашими браузерами(google chrome) через аккаунт google. Очень удобно для многих вещей, но смысла конкретно для этого расширения — нет. Главное не забывать, что sync хранилище ограничено в количестве сохраняемых элементов и их размере. Локальное хранилище напротив, позволяет хранить вообще безграничное количество информации.
И так, мы проверили и узнали, что токена в хранилище нет. Надо идти авторизовываться и получать токен для доступа к API вконтакте по средствам протокола OAuth 2.0
. Для этого мы откроем вкладку со специальным адресом на сервере вконтакте и передадим ему:
1. client_id=3315996
— это id вашего приложения, который вам выдаст вконтакте. Зарегистрировать своё приложение нужно по этому адресу
. Для регистрации приложения и получения своего id просто пишите название и выбираете Standalone-приложение. Не надо использовать мой id для своих приложений, ни к чему хорошему это не приведёт. Для каждого приложения должен быть зарегистрировать свой client_id.
2. scope=docs,offline
– это запрашиваемые api к которым будет иметь доступ
ваше приложение. Так как нам ничего кроме документов не надо, мы запрашиваем доступ только к ним. Доступ offline – даёт нам токен, который не будет истекать после определённого времени. Иначе придётся время от времени получать токен снова.
3. redirect_uri =
поскольку у нас отдельное приложение и никакого своего сервера нет, мы выбираем специальный uri на сервере вконтакте, на который будет перенаправлен браузер получивший токен (ну или не получивший). Токен будет в параметрах этого uri (Всё это части стандарта авторизации по протоколу OAuth 2.0. Очень просто и удобно).
4. display=page
– внешний вид окна авторизации
5. response_type=token
– то, что бы запрашиваем у сервера вконтакте.
Про все эти опции доходчиво написано на странице разработчиков вконтакте
.
И так, как происходит авторизация вконтакте. Наше расширение открывает вкладку в браузере по специально сформировванному uri на сервере вконтакте, где в параметрах запрашивает необходимый доступ.
var authUrl = 'https://oauth.vk.com/authorize?client_id=3315996&scope=docs,offline&redirect_uri=http%3A%2F%2Foauth.vk.com%2Fblank.html&display=page&response_type=token';
chrome.tabs.create({url: authUrl,selected: true}, function(tab) {...
и далее спрашивает, разрешить ли вашему приложению то, что оно запрашивает.
Если вы отвечаете — разрешить, тогда совершается переход на страницу указанную в redirect_uri
и в параметрах этого uri будет запрашиваемый нами access_token для доступа к API вконтакте. Если не разрешите, то в параметрах будет error=access_denied
и ничего у нас не выйдет.
Мы конечно разрешим доступ и в открытой нами вкладке произойдёт редирект на страницу oauth.vk.com/blank.html
с нужным нам токеном в параметрах. Чтобы получить токен, мы после открытия вкладки установим обработчик обновления вкладок.
authTabId = tab.id;
chrome.tabs.onUpdated.addListener(function tabUpdateListener(tabId, changeInfo)
{
if(tabId == authTabId && changeInfo.url != undefined && changeInfo.status == "loading")
{
if ( changeInfo.url.indexOf('oauth.vk.com/blank.html') > -1 ) { ...
Сохраним Id нашей вкладки и станем ждать, когда пользователь разрешит нам доступ и в uri вкладки появится access_token. Как только при обновлении данных вкладки мы находим access_token, мы его извлекаем и сохраняем в нашем storage:
chrome.storage.local.set({'vkaccess_token': accToken}, function() { ...
И далее сразу приступаем ко второй части нашего расширения. А именно получение картинки и загрузка картинки на сервер вконтакте.
Для этого мы перенаправим нашу вкладку (ту же самую ) на заранее подготовленную страницу в нашем расширении — upload.html
. В параметрах передадим всё, что нами нужно для работы. Это токен доступа и uri картинки. Эта страница нужна лишь для того, чтобы показать пользователю, что что-то происходит. Вдруг заргузка займёт много времени, а наша страница может в это время показывать анимированный прогресс бар. Что-то шевелится – пользователь спокоен.
Сюда же мы переходим если токен уже был получен и сохранён в storage.
chrome.tabs.update(tabId,{'url': 'upload.html#' + imgSrc + '&' + accToken,'active': true}, function(tab){});
На нашей странице upload.html
всё просто. По умолчанию она показывает прогресс бар – гифку. А в фоне работает скрипт upload.js
.
Ничего особенно там нет: в начале вы получаем нашу картинку из кэша браузера в виде blob’a, выполняя GET запрос.
var x = new XMLHttpRequest();
x.onload = function()
{
..
}
x.responseType = 'blob';
x.open('GET', imageUrl);
x.send();
Далее идёт череда запросов к вконтакте API строго в соответствии с документацией. Мы получаем адрес сервера для загрузки картинки ( docs.getUploadServer
), создаём форму с помощью FormData() и добавляем туда полученный blob с картинкой, присваивая ему имя картинки. Оправляем картинку POST’ом на сервер, сохраняем картинку на сервере ( docs.save
) и получая uri картинки в ответе переходим по нему, чтобы показать пользователю в нашей вкладке свежезагруженную картинку уже на сервере вконтакте.
Вот собственно и всё. Я не JavaScript специалист, потому уж не обессудьте за качество кода.
Шаг 3. Получение токена
Начинать процедуру аутентификации мы можем тогда и только тогда, когда к нам пришёл параметр code. Он нам нужен для того, чтобы получить специальный токен доступа, с помощью которого, в дальнейшем, мы достанем информацию о пользователе.
В первую очередь, снова сформируем нужные нам параметры для этого запроса:
Далее нам нужно отправить GET
запрос на адрес https://oauth.vk.com/access_token
, передав перечисленные параметры. В PHP выполнить GET запрос по какому-то адресу можно несколькими способами. Для данного урока я воспользуюсь функцией file_get_contents
.
Для того чтобы мы далее могли работать с данными параметрами, декодируем JSON строку с помощью функции json_decode
и помещаем данные в массив, передав в качестве второго аргумента true.
Шаг 4. Получение информации о пользователе
В результате, если всё было сделано правильно, то получим JSON ответ следующего вида:
Снова преобразуем JSON ответ в массив и обратимся к нулевому элементу, хранящемуся в массиве, доступному по ключу response:
Прошу обратить внимание, что в данном фрагменте, я добавил специальную переменную $result, равную изначально false сразу же после проверки наличия GET параметра code. Если нам удалось извлечь информацию о пользователе, то мы меняем значение этой переменной на true.
Шаг 2. Генерация ссылки для аутентификации
Для генерации ссылки нам потребуется адрес аутентификации и специальные параметры:
С помощью функции http_build_query, передав туда массив параметров, получим чередование ключей и значений, как в url адресе. Итак, генерируем ссылку и выводим на экран:
Также тут я воспользовался функцией urldecode. Если этого не сделать, то в сгенерированной ссылке могут появиться закодированные символы слешей, знаков двоеточия и так далее. Что-то вроде этого:
Если же мы пропустим данную строку через функцию urldecode, то получим:
Итак, ссылка для аутентификации у нас готова. Если мы сформировали все параметры правильным образом и получили верный url, то пройдя по ссылке, будем перенаправлены по адресу, указанному в настройках приложения (‘http://localhost/vk-auth’). Только теперь к этому адресу будет прикреплён специальный параметр code:
Аутентификация через ВКонтакте
С каждым днём влияние социальных сетей и сервисов только крепчает. Это означает, что нам, как веб разработчикам нужно это учитывать. Сегодня я расскажу и покажу, как создать аутентификацию ваших пользователей через социальную сеть ВКонтакте. Для этого мы не будем пользоваться какими-то сторонними библиотеками, а реализуем всё с нуля, собственными руками. Думаю, многие ждали подобного урока, так что томить не буду. Начнём!
Заметка.
Пример, созданный в данном уроке, предназначен для работы на локальном сервере.
Как это должно работать

В процессе авторизации нужно было получить email пользователя от VK и другую полезную информацию и, в случае с регистрацией, передать на клиент в форму регистрации.
Мы заранее формируем ссылку для перехода в VK примерно такого вида:
С нашего сайта клиент переходит по ссылке, предоставляет права на авторизацию с помощью своего VK аккаунта. V K объясняет пользователю, какие данные будут расшарены, получает подтверждение и перенаправляет на адрес, который мы указали в первом шаге redirect_uri=http://example.com/,
предоставив нам code.
Этот код отправляется на бэкенд для получения информации о пользователе.
Шаг 6. Дело за вами
Теперь, когда у нас есть такая информация, как ID пользователя, в первую очередь, нам необходимо проверить его наличие в нашей базе данных. Если пользователя с таким ID не существует, то значит он авторизовался с нашего сайта впервые, и мы внесём его в базу. Если пользователь уже есть, можем проверить не изменились ли какие-то данные о нём, например, имя или ещё что-то. Если да, обновим запись.
После этого, всё что нам осталось сделать, так это создать сессию и поместить в неё информацию о нашем пользователе.
На странице выхода из системы просто удаляем сессию с помощью функции unset
.
Шаг 1. Регистрация нового приложения
Для начала нам необходимо создать новое приложение на сайте социальной сети ВКонтакте
В открывшейся форме введите название приложения; выберите тип “Веб-сайт”; В качестве адреса сайта введите путь к папке с проектом на вашем локальном сервере. В моём случае, это http://localhost/vk-auth
. Базовый домен: localhost
.
После нажатия на кнопку “Подключить сайт”, вам наверняка придётся ввести проверочный код, который придёт по смс. Если вы пройдёте проверку, то вам должна открыться следующая форма с настройками приложения.
Сразу же хочу предупредить, что настоящие данные, относящиеся к моему приложению, я заменил на фиктивные, т.к. публикация таких значений как “Защищённый ключ” карается удалением вашего приложения или учётной записи в целом.
Из данной формы нам понадобятся такие данные, как `ID приложения`, `Защищённый ключ`, `Адрес сайта`. Запишем их в специальные переменные в файле index.php:
Решение — слушаем процесс авторизации и сохраняем состояние пользователя

Я начал изучать, как вообще можно узнать, что происходит в соседней вкладке или окне. Вот, например, статья
, которая даёт 4 возможных варианта: Local Storage Events, Broadcast Channel API, Service Worker Post Message и Window Post Message.
Local storage event использует промежуточное хранение данных в кэше, которым придется управлять вручную, это не так удобно. Service Worker Post Message — тоже не очень подходит для этой задачи, так как не дает явно настроить тип сообщения, которое я отправляю. Пришлось бы вручную добавлять в объект с данными поле type, а в месте, где я подписан на него, добавлять проверку значения этого поля. Window Post Message не подошёл по этой же причине.
Я использовал Broadcast Channel API
для обмена сообщениями и полифил для него broadcast-channel
. Вот как это работает:
В настройках приложения на стороне VK указываем один единственный redirect_uri.
После чего закрываем окно. Полученный code можно отправлять на бэкенд для обработки.
Посмотреть на полный процесс авторизации через VK вместе с бэкендом можно в этой хорошей статье
. Вместе с описанным выше подходом вы получите авторизацию через ВК без привязки к странице. Это полезно, чтобы не прерывать пользовательский сценарий и случайно не устроить взрывной отток юзеров.
Проблема — форма авторизации может быть где угодно на сайте
Виджет авторизации/регистрации не привязан к конкретному адресу и может быть вызван с множества адресов. V K требует указать в настройках приложения все возможные redirect_uri.
При этом, VK проверяет их на строгое сравнение и не даёт использовать динамические адреса.
Можно установить единую страницу для redirect_uri,
на которой пользователь продолжит регистрацию. Тогда пользователь потеряет состояние страницы, с которой он ушел для регистрации/авторизации, и ему придется всё делать по новой.