войти зарегистрироваться

Разработка whois

индекс
174,05

Авторизация по протоколу OAuth на примере Desktop Twitter-клиента

Потребовалось мне тут написать некий кроссплатформенный Twitter-клиент с закрытым исходным кодом, не спрашивайте зачем мне это надо, работа у меня такая, деньги я за это получаю. Что логично, языком разработки был выбран С++ с использованием Qt.
Сам API Twitter'a прост как кирзовый сапог. Но! Есть такая важная штука как авторизация, и тут есть два пути, старый — аутентификация посредством HTTP Headers и новый — использование протокола OAuth. Старый метод прост, также как и само API, но, к сожалению, он не безопасен, и самое главное команда Twitter'a предупреждает, что откажется от него в конце июня сего года. Поэтому остается второй метод OAuth. Надо сказать, что данный протокол используется не только в Twitter, но поскольку я писал Twitter-клиент, и рассматривать мы будем на примере Twitter'a.


Теория


Итак, изучив Twitter API и описание OAuth протокола, я узнал следующее, для авторизации Desktop-приложения необходимо зарегистрировать ваше будущее приложение в Twitter, это можно сделать на этой странице: twitter.com/oauth_clients, вам буду выданы два ключа: oauth_consumer_key и oauth_consumer_secret. Эти ключи следует запомнить и в дальнейшем вставить в ваше приложение как константы, так как они с ним неотрывно связаны.

Теперь рассмотрим процесс авторизации по шагам:
  1. С помощью некоего GET-запроса на адрес api.twitter.com/oauth/request_token мы должны получить первоначальное значение ключей oauth_token и oauth_secret
  2. С помощью некоего GET запроса мы должны открыть в браузере пользователя следующую страницу: api.twitter.com/oauth/authorize
  3. На этой странице у пользователя спросят его логин и пароль, если он не авторизован на сайте и спросят, действительно ли он желает разрешить данному приложению доступ в Twitter от имени его аккаунта
  4. После согласия пользователя ему будет показан PIN-код
  5. Наше приложение тем временем должно предложить пользователю диалог для ввода PIN-кода
  6. После того как пользователь скопирует из браузера PIN и вставит его в наше приложение, оно должно выполнить некий POST-запрос на адрес: api.twitter.com/oauth/access_token, это необходимо для получения настоящих ключей oauth_token и oauth_secret, которые в дальнейшем будут использоваться для идентификации пользователя в системе.

Теперь подробнее про запросы, их, напоминаю три: request_token, autorize и access_token

request_token


Данный запрос должен содержать в HTTP заголовке следующее поле:
Authorization: OAuth realm=«http%3A%2F%2Fapi.twitter.com%2F»,
oauth_consumer_key=«0685bd9184jfhq22»,
oauth_signature_method=«HMAC-SHA1»,
oauth_timestamp=«137131200»,
oauth_nonce=«4572616e48616d6d65724c61686176»,
oauth_version=«1.0»,
oauth_signature=«wOJIO9A2W5mFwDgiDvZbTSMK%2FPY%3D»

realm — хост на который отсылается запрос;
oauth_consumer_key — тот самый ключ, который нам выдали при регистрации;
oauth_signature_method — метод шифрования сигнатуры, их существует три для данного протокола: PLAINTEXT, HMAC-SHA1, RSA-SHA1, но опытным путем было выяснено, что Twitter не поддерживает PLAINTEXT;
oauth_timestamp — текущий timestamp на вашей машине;
oauth_nonce — случайно генерируемая для каждого запроса строка, своего рода соль;
oauth_version — версия протокола, всегда 1.0;
oauth_signature — о! этот параметр самый заковыристый, формируется он следующим образом:
Нам необходимо выполнять HMAC-SHA1 (или RSA-SHA1) с ключем, который формируется так: urlencode("<oauth_consumer_secret>&<oauth_token_secret>") — (на данном этапе oauth_token_secret еще не получен, поэтому он будет пустым) и базовой строкой, которая составляется следующим образом: "<метод запроса>&<urlencode(адрес запроса)>&<urlencode(key_sort(параметры запроса))>", чтобы было понятнее приведу пример:
GET&http3A%2F2Fapi.twitter.com%2Frequest_token&oauth_consumer_key%3Ddpf43f3p2l4k3l03%26oauth_nonce%3Dkllo9940pd9333jh%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1191242096%26oauth_version%3D1.0

и пример ключа:
kd94hf93k423kf44%26

После чего получившееся значение следует обработать алгоритмом base64.

Response этого запроса, если все прошло удачно, будет содержать строку вида:
oauth_token=nnch734d00sl2jdk&oauth_token_secret=pfkkdhi9sl3r4s00

Эти параметры надо распарсить и запомнить.

autorize


Самый простой шаг, вам нужно открыть в браузере следующий запрос:
api.twitter.com/oauth/authorize?oauth_token=nnch734d00sl2jdk

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

access_token


Завершающий шаг авторизации, теперь мы должны получить настоящие ключи oauth_token и oauth_token_secret.
Для этого нужно выполнить POST запрос на адрес: api.twitter.com/oauth/access_token.
POST параметры должны содержать следующее:
oauth_consumer_key=dpf43f3p2l4k3l03
oauth_token=hh5s93j4hdidpola
oauth_signature_method=HMAC-SHA1
oauth_timestamp=1191242092&
oauth_version=1.0
oauth_nonce=dji430splmx33448
oauth_verifier=213423534
oauth_signature=kd94hf93k423kf44%26hdhd0244k9j7ao03

oauth_verifier — это полученный нами PIN-код.
Сигнатура генерируется тем же образом как и в первом шаге.
Ответом на это запрос, будет такая же строка с параметрами, как и в первом шаге, но эти параметры нужно запомнить уже навсегда (если, конечно, вы не хотите, чтобы пользователь проходил всю процедуру при каждом запуске программы).

Практика


Практика показала, что нет ни одной вменяемой реализации HMAC-SHA1 на С++ под LGPL, и это грустно, я пробовал сделать что-то с этой реализацией: bit.ly/96RlAL — у меня не получилось. Есть вариант написать свою, с использованием OpenSSL, к сожалению у меня не было на это времени. Однако, во время интенсивного гугления я нашел библиотеку под названием QOAuth

QOAuth


Cчастью моему не было придела :), однако, даже с ней все оказалось не так просто.
Сама библиотека включается в проект, очень легко и просто компилируется (кстати мне не удалось заставить ее работать как отдельный *.so/*.dll файл, но это уже сказалась нехватка времени и кривизна моих рук), но у этой библиотеки есть зависимости, а именно QCA — Qt Cryptographic Architecture и ее плагин QCA OpenSSL.

QCA и QCA ossl plugin


Сборка самой QCA тривиальна, все шаги описаны в README, однако, ossl-плагин собираться отказался вылетев с таким вот сообщением:
qca-ossl.cpp: In function 'X509_EXTENSION*
opensslQCAPlugin::new_subject_key_id(X509*)':
qca-ossl.cpp:330: warning: deprecated conversion from string constant to
'char*'
qca-ossl.cpp: In member function 'virtual QCA::Provider::Context*
opensslProvider::createContext(const QString&)':
qca-ossl.cpp:6815: error: 'EVP_whirlpool' was not declared in this scope
*** Error code 1

И тут я почувствовал, что это конец :( однако, на помощь пришел хаброюзер tass и помог нагуглить решение: www.mail-archive.com/kde-freebsd@kde.org/msg03672.html — вот он патч разрешающий ошибку (обратите внимание патч для KDE под FreeBSD :) ). Ну дальше осталось самое простое, собрать все воедино и написать код запросов, код был взять из примеров QOAuth и модифицирован, потому как примеры устарели в отличие от самой библиотеки и стали «некомпилябельными», поэтому прикладываю примеры работающих request_token и access_token

request_token


  1. QOAuth::Interface *qoauth = new QOAuth::Interface(this);
  2. qoauth->setConsumerKey( consumerKey.toAscii());
  3. qoauth->setConsumerSecret( consumerSecret.toAscii() );
  4. qoauth->setRequestTimeout( 10000 );
  5.  
  6. QOAuth::ParamMap reply = qoauth->requestToken( oAuthRequestTokenUrl, QOAuth::GET, QOAuth::HMAC_SHA1 );
  7. if ( qoauth->error() == QOAuth::NoError )
  8. {
  9.   token = reply.value( QOAuth::tokenParameterName() );
  10.   tokenSecret = reply.value( QOAuth::tokenSecretParameterName() );
  11. }
* This source code was highlighted with Source Code Highlighter.


acess_token


  1. QOAuth::ParamMap otherArgs;
  2. otherArgs.insert( QByteArray("oauth_verifier"), pin.toAscii() );
  3.  
  4. QOAuth::ParamMap reply = qoauth->accessToken( oAuthAccessTokenUrl, QOAuth::POST, token, tokenSecret, QOAuth::HMAC_SHA1, otherArgs );
  5.  
  6. if ( qoauth->error() == QOAuth::NoError ) {
  7.   token = reply.value( QOAuth::tokenParameterName() );
  8.   tokenSecret = reply.value( QOAuth::tokenSecretParameterName() );
  9.   QString userName = reply.value( "screen_name" );
  10. }
* This source code was highlighted with Source Code Highlighter.


Полезные ссылки


Twitter API
OAuth протокол

Заключение


Я как и во всех остальных моих статьях не претендую на полноту решения, и не предлагаю пошаговое руководство, я лишь описываю базовую последовательность действий необходимую для решения задачи и подводные камни, с которыми столкнулся сам. Также буду благодарен, если кто-то подскажет работающую кроссплатформенную LGPL-ную реализацию HMAC-SHA1.

Мой Twitter: @VasiliySorokin

_________
Текст подготовлен в ХабраРедакторе

комментарии (28)

  • с такими новшествами проще будет использовать авторизацию по кукис :)
    • Да нет, протокол обалденный, просто он поражает своей параноидальностью, однако, я бы его рекомендовал и для более серьезных сервисов, например тех, которые работают с деньгами.
      • Насколько я помню, гугл использует этот протокол как один из возможных для авторизации в своих сервисах.
  • НЛО прилетело и опубликовало эту надпись здесь.
    • Обоснуйте? В чем хитрость?
      • ты его не слушай, это ж Кулябин))
        • НЛО прилетело и опубликовало эту надпись здесь.
      • НЛО прилетело и опубликовало эту надпись здесь.
        • Дак и чего? Для этого клиента все уже написано, а реализация HMAC-SHA1 нужна лично мне.
          • НЛО прилетело и опубликовало эту надпись здесь.
          • Обязательно нужно под LGPL? Как-то попадалась реализация на сайте codeproject.com под вполне вменяемой лицензией.
            • Ее видел (на нее ссылка даже есть в статье), но она почему то так и не смогла сгенерировать мне то что я хочу, хотя мб я был не прав, я этого не исключаю.
  • Пост однозначно в избранное. Что интересно, не далее как вчера, мне самому приходила идея создание подобного twitter клиента.
  • OAuth: Описание протокола простым и понятным языком
    (с) Дмитрий Котеров
    • На базе php да. У меня как заметили наверно речь идет про C++/Qt,
      Если бы мне потребовалось реализовать это на PHP статьи бы не было, потому что в сети хватает примеров, с С++/Qt все немного сложнее.
      • Конечно, заметил.
        Ссылка приведена лишь в качестве альтернативного источника информации по теме.
  • >команда Twitter'a предупреждает, что откажется от него в конце июня сего года
    откуда такая информация?
    • Пробегало в twitter-development-talk, в июне по-моему выключают. Там еще появилась штука под названием xAuth, которая дает возможность обменять username/password на OAuth access tokens, ее однако дают не всем.
      • пробегало, было дело. но закончилось тем, что было названо 20+ доводов за BasicAuth и 20+ против его отмены. так что ждем решения.
        вообще, с точки зрения простейшего метода взлома, а именно «социальной инженерии», BasicAuth много защищеннее, нежели OAuth. но это уже другой разговор.
        • Да ладно, кончилось: bit.ly/whatsupwithoauth

          В частности:

          *BASIC AUTHENTICATION DEPRECATION*

          yup — it's still happening. we're targeting June 2010. everybody,
          including legacy applications, will have to move over.
          • а вы правы. не видел этот тред. извиняюсь за дезориентацию.
    • от BasicAuth никто отказываться не собирается, разве что в далеком-далеком будущем. это автор статьи пугает нас.
      отказаться будет тяжело, учитывая то, что из мобильных клиентов организовать OAuth крайне тяжело, а порой и просто невозможно.
      «OAuth is the Twitter preferred method of authentication moving forward. While we have no plans in the near term to require OAuth, new applications should consider it best practice to develop for OAuth. We eventually would like to suspend Basic Auth support. However we realize that Basic Auth has been a large part of the API's success, and that the barrier to entry if OAuth is the only solution is substantially higher. Many applications rely on Twitter accounts as their means of account management. Additionally, Basic Auth allows a developer with a command line, cURL, and his account credentials to start poking at Twitter data. There are still a number of archetectural use cases to work through before we consider the deprication of Basic Auth. Before any changes begin to happen, we will discuss them with the community through the support channels, and give at least 6 months lead time before making any policy changes.»
      • Ага, по этому для мобильных клиентов сделали xAuth.
        • xAuth удобная штука. плюс хорошая мотивация в виде увеличенных RateLimit.
          • Точно, плюс возможность указывать source тег в твитах. Клиентам использующим Basic Auth его давно не давали.
  • прикрутить бы QOAuth к QML Twitter demo а то оно поломаное слегка.
  • Если кому интересно, могу рассказать о работе с twitter oauth на PHP
    • рассказывай конечно, вот только с пыхом там проще все, ибо есть уже готовые проверенные либы в вики по апи твиттера
Только авторизованные пользователи могут оставлять комментарии. Авторизуйтесь, пожалуйста.