Twitter OAuth Request a token returning Unauthorized

I am trying to get a request token from Twitter OAuth, but I keep getting 401 Unauthorized

, but I have no idea why. I tried to follow the signature generation as best I could, and struggled with for a while 400 Bad Request

, but finally hacked the code to get a valid request only to satisfy 401.

I feel like I'm missing something really simple, but I just can't figure out what.

Here is the code I'm using:

using (HttpClient client = new HttpClient())
{
    client.BaseAddress = new Uri("https://api.twitter.com/");
    client.DefaultRequestHeaders.Accept.Clear();
    client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

    TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);

    string oauth_nonce = Convert.ToBase64String(new ASCIIEncoding().GetBytes(DateTime.Now.Ticks.ToString()));
    string oauth_callback = Uri.EscapeUriString("oob");
    string oauth_signature_method = "HMAC-SHA1";
    string oauth_timestamp = Convert.ToInt64(ts.TotalSeconds).ToString();
    string oauth_consumer_key = OAUTH_KEY;
    string oauth_version = "1.0";

    // Generate signature
    string baseString = "POST&";
    baseString += Uri.EscapeDataString(client.BaseAddress + "oauth/request_token") + "&";
    // Each oauth value sorted alphabetically
    baseString += Uri.EscapeDataString("oauth_callback=" + oauth_callback + "&");
    baseString += Uri.EscapeDataString("oauth_consumer_key=" + oauth_consumer_key + "&");
    baseString += Uri.EscapeDataString("oauth_nonce=" + oauth_nonce + "&");
    baseString += Uri.EscapeDataString("oauth_signature_method=" + oauth_signature_method + "&");
    baseString += Uri.EscapeDataString("oauth_timestamp=" + oauth_timestamp + "&");
    baseString += Uri.EscapeDataString("oauth_version=" + oauth_version + "&");

    string signingKey = Uri.EscapeDataString(OAUTH_SECRET) + "&";
    HMACSHA1 hasher = new HMACSHA1(new ASCIIEncoding().GetBytes(signingKey));
    string oauth_signature = Convert.ToBase64String(hasher.ComputeHash(new ASCIIEncoding().GetBytes(baseString)));

    client.DefaultRequestHeaders.Add("Authorization", "OAuth " +
        "oauth_nonce=\"" + oauth_nonce + "\"," +
        "oauth_callback=\"" + oauth_callback + "\"," +
        "oauth_signature_method=\"" + oauth_signature_method + "\"," +
        "oauth_timestamp=\"" + oauth_timestamp + "\"," +
        "oauth_consumer_key=\"" + oauth_consumer_key + "\"," +
        "oauth_signature=\"" + Uri.EscapeDataString(oauth_signature) + "\"," +
        "oauth_version=\"" + oauth_version + "\""
    );

    HttpResponseMessage response = await client.PostAsJsonAsync("oauth/request_token", "");
    var responseString = await response.Content.ReadAsStringAsync();
}

      

I am using .Net 4.5, so Uri.EscapeDataString()

must be specifying the correct percentage encoded string.

EDIT

I noticed that the value oauth_nonce

sometimes included non-alphanumeric characters, so I changed the computation of the variable to this:

string nonce = Convert.ToBase64String(new ASCIIEncoding().GetBytes(DateTime.Now.Ticks.ToString()));
string oauth_nonce = new string(nonce.Where(c => Char.IsLetter(c) || Char.IsDigit(c)).ToArray());

      

This, unfortunately, did not help.

I've also tried Uri.EscapeUriData()

for all values ​​in the header Authorization

, and also added a space after each comma (as the Twitter documentation says), but that didn't help either.

+3


source to share


2 answers


I started from scratch and tried a slightly different approach, but it should give the same result. However, this didn't help and instead works as intended!

If anyone can point out a practical difference between this working code and the code in the original question, I would really appreciate it, because in my opinion they give the same results.



using (HttpClient client = new HttpClient())
{
    client.BaseAddress = new Uri("https://api.twitter.com/");
    client.DefaultRequestHeaders.Accept.Clear();
    client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

    TimeSpan timestamp = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
    string nonce = Convert.ToBase64String(new ASCIIEncoding().GetBytes(DateTime.Now.Ticks.ToString()));

    Dictionary<string, string> oauth = new Dictionary<string, string>
    {
        ["oauth_nonce"] = new string(nonce.Where(c => char.IsLetter(c) || char.IsDigit(c)).ToArray()),
        ["oauth_callback"] = "http://my.callback.url",
        ["oauth_signature_method"] = "HMAC-SHA1",
        ["oauth_timestamp"] = Convert.ToInt64(timestamp.TotalSeconds).ToString(),
        ["oauth_consumer_key"] = OAUTH_KEY,
        ["oauth_version"] = "1.0"
    };

    string[] parameterCollectionValues = oauth.Select(parameter =>
            Uri.EscapeDataString(parameter.Key) + "=" +
            Uri.EscapeDataString(parameter.Value))
        .OrderBy(kv => kv)
        .ToArray();
    string parameterCollection = string.Join("&", parameterCollectionValues);

    string baseString = "POST";
    baseString += "&";
    baseString += Uri.EscapeDataString(client.BaseAddress + "oauth/request_token");
    baseString += "&";
    baseString += Uri.EscapeDataString(parameterCollection);

    string signingKey = Uri.EscapeDataString(OAUTH_SECRET);
    signingKey += "&";
    HMACSHA1 hasher = new HMACSHA1(new ASCIIEncoding().GetBytes(signingKey));
    oauth["oauth_signature"] = Convert.ToBase64String(hasher.ComputeHash(new ASCIIEncoding().GetBytes(baseString)));

    string headerString = "OAuth ";
    string[] headerStringValues = oauth.Select(parameter =>
            Uri.EscapeDataString(parameter.Key) + "=" + "\"" +
            Uri.EscapeDataString(parameter.Value) + "\"")
        .ToArray();
    headerString += string.Join(", ", headerStringValues);

    client.DefaultRequestHeaders.Add("Authorization", headerString);

    HttpResponseMessage response = await client.PostAsJsonAsync("oauth/request_token", "");
    var responseString = await response.Content.ReadAsStringAsync();
}

      

+2


source


Thanks @GTHvidsten for your example.

Moving on to your second working example, I discovered a problem with my code and probably for the same reason that your original example didn't work.



It is not enough to encode the custom query key / value pairs that make up the main signature string, but apparently you also need to encode the entire attached key / value parameter string, which thus encodes "=" signs. In your second example, this line of code does exactly that:baseString += Uri.EscapeDataString(parameterCollection);

Your original example is missing the code that encodes the request parameters.

0


source







All Articles