Delphi programming blog
Источник: http://teran.karelia.pro/articles/item_4474.html
 

Проблема с авторизацией вконтакте.

Опубликовано 25.05.2011 г. 23:37

Вконтакт недавно добавил работу с использованием OAuth, так что надо бы переписать волшебный класс для работы с его API на котором основана работа плагина для аутлука. Но вот ведь загвоздка, не получается что-то авторизироваться. Подумалось кстати, что из этого класса можно и интересный невизуальный компонент сделать для работы с API.

Предположил, что дабы авторизироваться надо заслать на сервер такие же заголовки, как шлет FireFox при авторизации. Поставил плагин Live Headers про который узнал на webdelphi. Для авторизации сначала заходим на волшебную страницу с формой ввода логина и пароля:
http://api.vkontakte.ru/oauth/authorize?client_id= APPLICATION_ID &redirect_uri=http://api.vkontakte.ru/blank.html
&scope=wall,offline&response_type=token&display=page
где вместо APPLICATION_ID фигурирует ID нашего приложения. В scope запрашиваем требуемые права. Пишем, что в ответ мы хотим получить токен, с помощью которого потом и будет проходить все работа. Параметр display отвечает за вид страницы. page это обычная. Можно поставить wap, тут видимо без яваскриптов. По идее второй проще. Но в этом втором варианте у меня даже мозилла не может авторизоваться, если в пароле есть нестандартные символы типа § или что нибудь такое. Так что пытаемся сделать с display=page. В общем то тут мы берем и отправляем форму логина и пароля. В поле to формы какой то здоровенный хеш, не понятно от чего зависит, постоянный или нет. У меня всегда один и тот же рисуется. Но видимо зависит от IP адреса пользователя или еще чего нить. Пока что не проверял. Если посмотреть тут исходный код страницы, то можно найти в ней код вида
function approve() {
    location.href = "https://api.vkontakte.ru/oauth/grant_access?hash=1d0f95b6fc88f2060b&client_id=APPLICATION_ID&settings=73728
&redirect_uri=blank.html&response_type=token&state=";
  }
с хешем, который не постоянный. Не ясно зачем оно надо. Мозилла ссылку таким хешем не использует. В общем грубо говоря берем и отправляем форму методом POST на http://login.vk.com/ параметры имеют вид примерно такой:
    sl := TStringlist.Create();
    with sl do begin
        values['act']   := 'login';
        values['soft']  := '1';
        values['q']     := '1';
        values['from_host'] := 'api.vkontakte.ru';
        values['to']    := LOGIN_FORM_HASH_TO;
        values['expire']:= '0';
        values['email'] := email;
        values['pass']  := pwd;
    end;
В ответ нас редиректит на страницу
http://api.vkontakte.ru/oauth/authorize?client_id=APPLICATION_ID&redirect_uri=blank.html&response_type=token&scope=73728
&state=&display=page&hash=c136c869ed4348b2b2b37c7760b8abe6&s=1
которую мы загружаем GET запросом. Загрузив эту страницу нас редиректит на новую страницу
https://api.vkontakte.ru/oauth/grant_access?hash=9717be4f272d0b74dc&client_id=APPLICATION_ID&settings=73728
&redirect_uri=blank.html&response_type=token
Здесь TIdHTTP вызывает исключение ибо IOHandler не знает что есть https. На этом моменте мы меняем IOHandler.
    http.IOHandler := TIdSSLIOHandlerSocketOpenSSL.Create(nil);
Если мы будем юзать его изначально, то нас почему то не редиректит на такую страницу, так что меняем его только сейчас (вопрос кстати почему так?) Поменяв IOHandler загружаем эту самую страницу. Все эти запросы соответствуют тому, что делает при авторизации мозилла (за исключением одного, но об этом позже). Вот, после получения этой страницы мозиллу редиректит на страницу вида
http://api.vkontakte.ru/blank.html#access_token=4c06e8d24c12ec604c4c6390e04c0c6d75c4c124c13cc601add6b14ef61c028
&expires_in=0&user_id=USER_ID
а нас редиректит на пустой http://api.vkontakte.ru/blank.html, и ничего в якоре нет. http.URI.Bookmark тоже пуст, что и ожидалось. полный вид ссылки получаю с помощью
http.URL.GetFullURI();
параметр по умолчанию это [ofAuthInfo, ofBookmark] Так что якорная ссылка должна присутствовать. Замечу, что при получении этой страницы контент у нее следующий:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
   <head>
      <meta http-equiv="content-type" content="text/html; charset=windows-1251" />
      <title>OAuth Blank</title>
   </head>
   <body onload="document.body.innerHTML = (location.hash || '').substr(1);">
     Login success
   </body>
</html>
Можно заметить тут надпись Login Success. Но таки никакого токена у нас нет. Скажите мне я дебил или нет?:) где токен? (: Мозилла между делом, перед тем как запросить https страницу, отправляет еще один POST запрос на сервер http://ocsp.godaddy.com/ с бинарными данными c
Content-Type: application/ocsp-request
я в сетях не силен, да и особо не работал в Delphi с подобным, так что не знаю для чего это. Мб это самой мозилле надо на кой то черт. Вот в общем то и вопрос, где я что делаю не так, или недопонимаю в использовании TIdHTTP и где взять токен?. Может он вообще его выкидывает как ненужное? Кажется где то даже такое читал, или что то подобное, да найти не могу. Так то в принципе на кой ему якорные ссылки, они в бразуерах используются обычно только. Честно говоря документацию про него особо не читал. Может кто подсказать? (: вохрень. посмотрел что происходит в OnRedirect. там с якорем и токеном. можно считать что проблема стала половину меньше. Таки без этого события можно как нить якорную ссылку то отловить или нет? в http.Response.Location/RawHeaders тоже без нее. хотя именно response.location передается в DoOnRedirect. Все таки где то видимо правильно читал что он обрезает их. Так что новый вопрос, как извлечь якорную ссылку без события?
Метки:  vkontakte API  |  idhttp 

Комментарии

Vlad
26.05.2011 в 01:15
>>Вот в общем то и вопрос, где я что делаю не так, или недопонимаю в использовании TIdHTTP и где взять токен?
Ну вообще-то OAuth простой подставой заголовков и куков победить довольно трудно, а ежели и победится на одном компе, то врядли заработает на втором - слишком много там генерится всякой "чуши" для защиты, чего стоит BaseString правильно собрать :) Я с ВКонактом вообще не дружу и соответственно на их API мне пофих, но я бы посветовал пристально глянуть на то как изменилась форма запросов к ресурсам API после того как они привинтили туда OAuth. Потому как если они сделали ВСЁ согласно протоколу, то получить токен - это будет такая мелочь по сравнению с самим API :)
Могу предложить глянуть класс, который я "затачивал" под работу с OAuth Twitter'а http://www.webdelphi.ru/2010/05/twitter-oauth-i-russkie-simvoly-resheno/
Может его можно прикрутить к ВКонтакту - будет токен по всем законам науки и техники получаться :)
Vlad
26.05.2011 в 01:17
И кстати, забыл сказать - токен во время авторизации может меняться пару раз :) Собственно линк на доку по протоколу http://tools.ietf.org/html/draft-ietf-oauth-v2-16#section-1.1 глянь на схему рис.1 - это то как проходит авторизация, в общем замучиишся собирать токены, параметры и куки :)
Vlad
26.05.2011 в 02:46
Сорри за флуд (пост зацепил за "живое") хы. Глянул API/ Вобщем чего могу сказать:
1. По официальным докам авторизовался вообще без проблем. И токен в якоре есть и время жизни и всё прочее. Выдирать токен надобно из URL потому как разрабы API чё-то там намудрили...походу впадлу было по человечьи отдать ключик - замутили JavaScript на кой-то фик...
2. Для твоего случая авторизовываться надо так, чтобы юзер сам вбивал в браузере логин и пароль. Может показаться "фу! ацтой!" и т.д., но такова философия OAuth - твоя прога вообще не должна брать/хранить чужие пароли - только через браузер. И между прочим я с этим делом полностью согласен ибо мало ли кто ты есть - мож твоя прога парольки коллекционирует по-тихой? А так - всё по-чесноку - юзер на офф.сайте вбивает логин/пароль, а тебе ключик выдает шифрованый на 150 рядов и не вскроешь :)
3. Те непостоянные хэши о которых идет речь (не могу сказать, что уверен на все 100) скорее всего это логин и пароль закрученные через HMAC-SHA1 или тупо параметр шума - куда впихивается обычно текущее время (такое задолбаешься вскрывать)- отсюда и их постоянная смена
4. Если надо авторизовываться "вслепую" без браузера, то, судя по докам тебе в этом случае светят тока secure-методы, которых не так уж и много. Но тогда тебе браузер вообще не нужен, а токен прилетит в виде простенького JSON'а
Вот собственно, что наковырялось за часик с перекурами.Будут вопросы - стучи в аську: 391867649 или мыль на мыло, а то я тут уже пост накатал целый своими комментами :)
ter
26.05.2011 в 10:21
пасиб за комменты ((:
1. ну да, по инструкции авторизация через браузера труда не составляет.
2. ну как бы я согласен что авторизироваться надо бы через браузер, но у меня сначала аутлук спрашивает пароль, ему вводишь учетные данные, и он вызывает процедуру login(user,pwd) у плагина. Если тут показывать окно браузера и спрашивать логин еще раз, то будет скажем так выглядеть не очень (: +невозможность автовхода.
3. с хэшами то без разницы откуда они берутся и для чего нужны (:
4. сам особо не читал много по апи через oauth но авторизовавшись должны работать все методы (:
Юрий
06.07.2011 в 13:35
Андрей, я вот решил по использовать vkontakte.
Почитал на блогах статьи. И вот возникала делема или непонимание.
1) Либо просто использовать get/post запросы (как обычный юзер)
2) Либо использовать api vkontakte, и я так понимаю учётку связывать с "приложением"
3) Либо к этому + OAuth
4) Ещё какой-то мех. о котором я не знаю

Что по вашему проще? какие плюсы/минусы?
ter
06.07.2011 в 20:49
если вы собираетесь создать desktop-приложение для вконтакта, то не понятно как вы предполагаете вообще использовать вариант №1.
так что варианты либо 2 либо 3. 3 кажется более удобным и безопасным.
Mr_F
09.07.2011 в 20:26
>>но такова философия OAuth - твоя прога вообще не должна брать/хранить чужие пароли - только через браузер. И между прочим я с этим делом полностью согласен ибо мало ли кто ты есть - мож твоя прога парольки коллекционирует по-тихой? А так - всё по-чесноку - юзер на офф.сайте вбивает логин/пароль, а тебе ключик выдает шифрованый на 150 рядов и не вскроешь

А посмотреть значения, которые передает WebBrowser?)) На vbnet делается это так:
password = WebBrowser1.Document.GetElementById("pass").GetAttribute("value")
На дельфи примерно также, думаю.
Короче, если юзверь вводит свои данные в программу, то это только на свой страх и риск :)

ter, Выложи полный листинг или сорцы, а то не совсем понятно. Я на дельфи недавно перебрался
ter
10.07.2011 в 01:28
http://blog.karelia.ru/teran/?p=570 тут исходный код в конце статьи есть.
- Имя
- e-mail*
- Сайт
вы можете использовать теги [i],[b],[code],[quote]
Дополнительно