OAuth authentication using Desktop Twitter client as an example

  • Tutorial
It took me here to write some cross-platform Twitter client with closed source code, do not ask why I need it, I have such work, I get money for it. Logically, C ++ was chosen using the development language using Qt.
The Twitter API itself is as simple as a tarpaulin boot. But! There is such an important thing as authorization, and there are two ways, the old one using HTTP Headers authentication and the new one using the OAuth protocol. The old method is simple, as well as the API itself, but, unfortunately, it is not safe, and most importantly, the Twitter team warns that it will abandon it at the end of June this year. Therefore, the second OAuth method remains. I must say that this protocol is used not only on Twitter, but since I wrote a Twitter client, and we will look at the example of Twitter.

Theory


So, having studied the Twitter API and the description of the OAuth protocol, I learned the following, to authorize the Desktop application, you need to register your future application on Twitter, you can do this on this page: twitter.com/oauth_clients , you will be given two keys: oauth_consumer_key and oauth_consumer_secret. These keys should be remembered and later inserted into your application as constants, since they are inextricably linked with it.

Now consider the authorization process in steps:
  1. Using some GET request to api.twitter.com/oauth/request_token, we should get the initial value of the keys oauth_token and oauth_secret
  2. Using some GET request, we should open the following page in the user's browser: api.twitter.com/oauth/authorize
  3. On this page, the user will be asked for his username and password if he is not authorized on the site and asked if he really wants to allow this application access to Twitter on behalf of his account
  4. After the user's consent, he will be shown a PIN code
  5. Our application in the meantime should offer the user a dialog for entering a PIN code.
  6. After the user copies the PIN from the browser and pastes it into our application, it must execute a certain POST request to the address: api.twitter.com/oauth/access_token , this is necessary to obtain the real keys oauth_token and oauth_secret, which will be used in the future to identify the user in the system.

Now more about requests, I remind you of three: request_token, autorize and access_token

request_token


This request should contain the following field in the HTTP header:
Authorization: OAuth realm = «http% 3A% 2F% 2F% 2Fapi.twitter.com»,
oauth_consumer_key = «0685bd9184jfhq22»,
oauth_signature_method = «HMAC-SHA1»,
oauth_timestamp = «137 131 200»,
oauth_nonce = «4572616e48616d6d65724c61686176»,
oauth_version = « 1.0 ",
oauth_signature =" wOJIO9A2W5mFwDgiDvZbTSMK% 2FPY% 3D "

realm - the host to which the request is sent;
oauth_consumer_key - the same key that was given to us during registration;
oauth_signature_method - signature encryption method, there are three for this protocol: PLAINTEXT, HMAC-SHA1, RSA-SHA1, but it was experimentally found that Twitter does not support PLAINTEXT;
oauth_timestamp - current timestamp on your machine;
oauth_nonce - a line randomly generated for each request, a kind of salt;
oauth_version - protocol version, always 1.0;
oauth_signature - oh! this parameter is the most intricate, it is formed as follows:
We need to execute HMAC-SHA1 (or RSA-SHA1) with the key, which is formed like this:urlencode ("&")- (at this stage, oauth_token_secret has not yet been received, therefore it will be empty) and the base line, which is composed as follows: "<request method> &&"to make it clearer I will give an example:
GET & http3A% 2F2Fapi.twitter.com% 2Frequest_token & oauth_consumer_key% 3Ddpf43f3p2l4k3l03% 26oauth_nonce% 3Dkllo9940pd9333jh% 26oauth_signature_method% 3DHMACohh1% 260% 3D121% 26a1% 26d1% 26d1t% 31haut1ht4a1h2a2a2a2d2a2aaaaaaaa

and an example key:
kd94hf93k423kf44% 26

After that, the resulting value should be processed with the base64 algorithm.

The response of this request, if everything went well, will contain a line of the form:
oauth_token = nnch734d00sl2jdk & oauth_token_secret = pfkkdhi9sl3r4s00

These parameters must be parse and remember.

autorize


The easiest step, you need to open the following request in the browser:
api.twitter.com/oauth/authorize?oauth_token=nnch734d00sl2jdk

The result of user actions in the browser will be the PIN code displayed to him.
Your application should request this PIN and remember it, it will be required in the next step.

access_token


The final step of authorization, now we need to get the real keys oauth_token and oauth_token_secret.
To do this, you need to make a POST request to the address: api.twitter.com/oauth/access_token .
POST parameters should contain the following:
= dpf43f3p2l4k3l03 oauth_consumer_key
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 is the PIN we received.
The signature is generated in the same way as in the first step.
The answer to this request will be the same line with the parameters as in the first step, but you need to remember these parameters forever (unless, of course, you want the user to go through the entire procedure each time the program starts).

Practice


Practice has shown that there is not a single sane implementation of HMAC-SHA1 in C ++ under LGPL, and this is sad, I tried to do something with this implementation: bit.ly/96RlAL - I did not succeed. There is an option to write your own using OpenSSL, unfortunately I did not have time for this. However, during intensive googling, I found a library called QOAuth

QOAuth


Fortunately, there was no chapel :), however, even with her everything was not so simple.
The library itself is included in the project, it is very easy and simple to compile (by the way, I could not get it to work as a separate * .so / *. Dll file, but this was already affected by the lack of time and the curvature of my hands), but this library has dependencies, and it is QCA - Qt Cryptographic Architecture and its QCA OpenSSL plugin.

QCA and QCA ossl plugin


The assembly of QCA itself is trivial, all the steps are described in README, however, the ossl-plugin refused to be assembled after flying out with this message:
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

And then I felt that this was the end :( however, the tass hub browser came to the rescue and helped google the solution: www.mail-archive.com/kde-freebsd@kde.org/msg03672.html - here it is a patch resolving the error (note patch for KDE under FreeBSD :)). Well then the simplest thing remained, to put everything together and write the request code, the code was taken from the QOAuth examples and modified, because the examples are outdated in contrast to the library itself and have become “non-compilable”, therefore I apply examples of the working request_token and 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 .


useful links


Twitter API
OAuth Protocol

Conclusion


Like in all my other articles, I do not pretend to be a complete solution, and I do not offer a step-by-step guide, I only describe the basic sequence of actions necessary to solve the problem and the pitfalls that I myself have encountered. I will also be grateful if someone tells me a working cross-platform LGPL implementation of HMAC-SHA1.

Also popular now: