xAuth in Instapaper

July 31st, 2012 | Posted by Michael in Programming

Recently I was working on an Instapaper application for Windows 8, and had to tackle the issue of xAuth as a login mechanism. For those who don’t know, xAuth is a way of bringing some of the benefits of OAuth to the traditional username/password workflow. The idea is that if you have a mobile device that cannot easily delegate the login to a webpage hosted by someone else (with callbacks and tokens and all that fun) you can just gather the username and password, get your OAuth access token & secret and then discard the username and password, never storing the sensitive details locally.

Instapaper uses xAuth for its authentication mechanism, although unlike twitter, xAuth is the only option available for logging in. After fighting with xAuth (and bad documentation) I’ve finally got it working, so here are the steps, and things to note, to get an access token.

Before you begin you need to have a Consumer Token and Secret, these can be accessed by contacting Instapaper and describing your application as outlined in http://www.instapaper.com/api/full

xAuth Access Tokens

The first step is to get an Access Token and Secret, this is what you will use to sign all requests and let Instapaper know that you are signed in. There are a couple of steps to this workflow as outlined above.

First you obviously need to gather the username and password of the user. Once you have that, you need to construct the message that will be signed by the Consumer Secret. This involves building a message that looks like the following:

POST&https://www.instapaper.com/api/1/oauth/access_token&oauth_consumer_key=CONSUMERKEY&oauth_nonce=NONCE&oauth_signature_method=HMAC-SHA1&oauth_timestamp=TIMESTAMP&oauth_version=1.0&x_auth_mode=client_auth&x_auth_password=PASSWORD&x_auth_username=USERNAME

Where the URL is the URL encoded address that you will be accessing, and PARAMS is the list of URL Encoded parameters inside the OAuth header as well as the POST body, in Alphabetical Order. It is important that these parameters are sorted into order, otherwise your signature will not match the one generate on the server, and your request will be rejected.

This is where the xAuth parameters start to come into play. You need the following parameters in this section for an Access token:

Key Expected Value Description
oauth_consumer_key Provided by Instapaper This is one of the strings provided by Instapaper, and it is connected to the consumer secret to identify your application to Instapaper.
oauth_nonce Any random/unique number. Number used ONCE is a random number that the server can use to prevent Replay Attacks.
oauth_signature_method HMAC-SHA1 In this case there is only one signature method in use, SHA1. This defines the type of hashing algorithm used to generate the signature.
oauth_timestamp Number of seconds since 0:00:00 UTC 1970-01-01 (Unix epoch) This is pretty simple, but if you weren’t sure, the value for this one is just the number of seconds since the 1st of Jan 1970. (also known as Unix Time)
oauth_version 1.0 The OAuth version in use, in this case the only value is 1.0.
x_auth_mode client_auth The xAuth mode, in this case the only value is client_auth
x_auth_password User Provided The password from the user.
x_auth_username User Provided The username from the user.

 

Once you have this POST message, you need to sign it with the Consumer Secret, using the HMAC-SHA1 hashing scheme. Instapaper only supports this scheme so you don’t really have a choice. Be sure to URL Encode the Consumer Secret, and append an ampersand (&) symbol to it. (In this case we don’t have an access secret yet so we don’t append that)

Once you have the signature, you can build the OAuth header and POST body, ready to send to Instapaper.

The OAuth header goes into the “Authorization” header, and looks like the following:

OAuth oauth_consumer_key="KEY", oauth_nonce="NONCE", oauth_signature_method="HMAC-SHA1", oauth_timestamp="TIMESTAMP", oauth_version="1.0", oauth_signature="SIGNATURE" 

Ensure that the values provided here match the values you signed. This header does not need to be in alphabetical order so feel free to construct it however you want.

Inside the POST body you need to transmit the x_auth_* parameters in a URL encoded fashion, using query string style formatting. (KEY=VALUE&KEY2=VALUE2)

x_auth_mode=client_auth&x_auth_password=PASSWORD&x_auth_username=USERNAME

Just a quick note, use UTF-8 encoding for all operations in this process. Also make sure that the POST parameters are in the same order you used when signing them.

Once you make the request you should receive a HTTP 200 message, and the response body should contain the access token and secret in qline format, which looks the same as a query string. To make requests as the logged in user, you need to add in an oauth_token parameter to your request with the access token, and append the access secret to the consumer secret using the “&” symbol when signing.

I hope this answers some questions about how to implement xAuth in your application. If you have any further questions or feedback please feel free to mention it in the comments section.

You can follow any responses to this entry through the RSS 2.0 You can leave a response, or trackback.

  • Sam Naseri

    Wh

  • Sam Naseri

    I am just curious to know if xAuth is supported by DotNetOpenAuth or not? If is supported, then why you simply did not use that library?

  • http://mquandt.com/blog Chr0n1x

    I didn’t know about this library when I wrote the article, also from what I can see it doesn’t support xAuth directly. It could be possible to try and use parts of it to get xAuth working but it looks like that could also be difficult.

  • Benecore

    Hi, I trying use xAuth with instapper, but i still get message “oauth_signature does not match expected value” all parameters has been sorted like on this post. Any idea

  • http://mquandt.com/blog Chr0n1x

    Have you got all of the parameters listed in the example? You need to make sure you only have those in there, and that their order is the same (alphabetical order) between what you send through, and what you sign. You also need to make sure that the casing you use is the same.

    I can provide a C# sample if you haven’t resolved it already, I’ll just need a little time to clean it up.

  • mbda2

    Thanks for the write-up. I’ve verified my code against your recommendations as well as other sources but I’m still coming up short unfortunately. If you could post/email your code it would be appreciated.

  • Ludo

    HI, i trying too use this API, but like Benecore said, I get message “oauth_signature does not match”

    I test my “signature encoder” with a sample value of twitter and i have the same signature.

    I suspect an error in Http request, do you have any idea to solve problem?

    /**** Generation of request and signature ****/////
    TimeSpan SinceEpoch = (DateTime.Now – new DateTime(1970, 1, 1, 0, 0, 0,0).ToLocalTime());
    Random Rand = new Random();
    String InstapaperUrl = url;
    Int32 Nonce = Rand.Next(1000000000);
    String SigBaseStringParams = “”;
    SigBaseStringParams += “oauth_consumer_key=” + ConsummerKey;
    SigBaseStringParams += “&” + “oauth_nonce=” + Nonce.ToString();
    SigBaseStringParams += “&” + “oauth_signature_method=HMAC-SHA1″;
    if (oauth_token != “”)
    SigBaseStringParams += “&” + “oauth_token=” + oauth_token;
    SigBaseStringParams += “&” + “oauth_timestamp=” + Math.Round(SinceEpoch.TotalSeconds);
    SigBaseStringParams += “&” + “oauth_version=1.0″;
    SigBaseStringParams += “&” + “x_auth_mode=client_auth”;
    SigBaseStringParams += “&” + “x_auth_password=” + passsword;
    SigBaseStringParams += “&” + “x_auth_username=” + username;
    String SigBaseString = “POST&”;
    SigBaseString += Uri.EscapeDataString(InstapaperUrl) + “&” + Uri.EscapeDataString(SigBaseStringParams);
    IBuffer KeyMaterial = CryptographicBuffer.ConvertStringToBinary(ConsummerSecret + “&” + oauth_tokenSecret, BinaryStringEncoding.Utf8);
    MacAlgorithmProvider HmacSha1Provider = MacAlgorithmProvider.OpenAlgorithm(“HMAC_SHA1″);
    CryptographicKey MacKey = HmacSha1Provider.CreateKey(KeyMaterial);
    IBuffer DataToBeSigned = CryptographicBuffer.ConvertStringToBinary(SigBaseString, BinaryStringEncoding.Utf8);
    IBuffer SignatureBuffer = CryptographicEngine.Sign(MacKey, DataToBeSigned);
    String Signature = CryptographicBuffer.EncodeToBase64String(SignatureBuffer);
    String DataToPost = “”;
    if (oauth_token != “”)
    DataToPost = “oauth_consumer_key=”" + ConsummerKey + “”, oauth_nonce=”" + Nonce.ToString() + “”, oauth_signature_method=”HMAC-SHA1″, oauth_token=”" + oauth_token + “”, oauth_timestamp=”" + Math.Round(SinceEpoch.TotalSeconds) + “”, oauth_version=”1.0″, oauth_signature=”";
    else
    DataToPost = “oauth_consumer_key=”" + ConsummerKey + “”, oauth_nonce=”" + Nonce.ToString() + “”, oauth_signature_method=”HMAC-SHA1″, oauth_timestamp=”" + Math.Round(SinceEpoch.TotalSeconds) + “”, oauth_version=”1.0″, oauth_signature=”";
    m_PostResponse = await PostData(InstapaperUrl, DataToPost, Uri.EscapeDataString(Signature) + “”", username, passsword);

    /*****************/

    /********* Http request *******/

    var post = Data + signature;
    HttpClient httpClient = new HttpClient();
    httpClient.MaxResponseContentBufferSize = int.MaxValue;
    httpClient.DefaultRequestHeaders.ExpectContinue = false;
    HttpRequestMessage requestMsg = new HttpRequestMessage();
    requestMsg.Headers.Authorization = new AuthenticationHeaderValue(“OAuth”, post);
    requestMsg.Headers.Add(“Accept”, “*/*”);
    requestMsg.Method = new HttpMethod(“POST”);
    requestMsg.Content = new StringContent(Uri.EscapeDataString(“x_auth_mode=client_auth&x_auth_password=” + password + “&x_auth_username=” + username),Encoding.UTF8,”application/x-www-form-urlencoded”);
    requestMsg.RequestUri = new Uri(Url, UriKind.Absolute);
    requestMsg.Content.Headers.ContentType = new MediaTypeHeaderValue(“application/x-www-form-urlencoded”);
    String send = await requestMsg.Content.ReadAsStringAsync();
    HttpResponseMessage response = await httpClient.SendAsync(requestMsg);
    return await response.Content.ReadAsStringAsync();
    /***********/

    Thank’s,

    Ludo

  • http://mquandt.com/blog Chr0n1x

    Here is the code I am currently using. I’m having my own issues with retrieving stuff from Instapaper so it may be connected to what you’re seeing. I can’t look into it until late next week unfortunately. https://gist.github.com/aed197c8433781faf0f3