Statuses/user_timeline with user context authentication


#1

Hello, I have some troubles with passing parameters to statuses/user_timeline endpoint
I use the user context authentication. When I request it without specified additional parameters like count or trim_user it works fine. But with parameters I get only the (401) Unauthorized response

I’ve been trying to pass the parameters like this:
https://api.twitter.com/1.1/statuses/user_timeline.json?count=5

Is that possible to specify parameters using the user context authentication and if it is, then please help me to find out what do I do wrong.

Thanks for your attention


#2

It sounds like however the OAuth signatures are being generated are not including the parameters. Are you using an open source library to generate and make requests? Show some code of how you are interacting with the Twitter API.


#3

Hi, thank you for answering. I don’t use any third party libraries.
My C# code for interaction with API is as following:

   public void GetTimeline(string token, string secret)
   {
        Dictionary<string, string> parameters = new Dictionary<string, string>();
        parameters.Add("oauth_consumer_key", ConsumerKey);
        parameters.Add("oauth_nonce", GenerateOAuthNonce());
        parameters.Add("oauth_signature_method", SignatureMethod);
        parameters.Add("oauth_timestamp", GenerateOAuthTimestamp());
        parameters.Add("oauth_token", token);
        parameters.Add("oauth_version", OAuthVersion);

        string parameterString = GenerateParameterString(parameters);
        string url = "https://api.twitter.com/1.1/statuses/user_timeline.json?count=3";

        WebRequest request = WebRequest.Create(url);
        request.Method = "GET";

        var baseString = GenerateSignatureBaseString(request.Method, url, parameterString);
        parameters.Add("oauth_signature", EncodeUrl(GenerateOauthSignature(baseString, secret)));

        string authHeader = GenerateHeaderString(parameters);
        request.Headers.Add("Authorization", authHeader);
        var response = new StreamReader(request.GetResponse().GetResponseStream()).ReadToEnd();
    }

    private string GenerateSignatureBaseString(string MethodInUpperCase, string url, string parameterString)
    {
        return $"{MethodInUpperCase}&{EncodeUrl(url)}&{parameterString}";
    }

    private string GenerateParameterString(Dictionary<string, string> parameters)
    {
        parameters = parameters.OrderBy(x => x.Key).ToDictionary(y => y.Key, y => y.Value);
        string parameterString = "";
        foreach (string k in parameters.Keys)
        {
            if (k == "oauth_callback")
                parameterString += k + "=" + EncodeUrl(parameters[k]) + "&";
            else
                parameterString += k + "=" + parameters[k] + "&";
        }
        parameterString = parameterString.Remove(parameterString.Length - 1, 1);
        return EncodeUrl(parameterString);
    }

    private string GenerateHeaderString(Dictionary<string, string> parameters)
    {
        parameters = parameters.OrderBy(x => x.Key).ToDictionary(y => y.Key, y => y.Value);
        string headerString = "OAuth ";
        foreach (string k in parameters.Keys)
        {
            if (k == "oauth_callback")
                headerString += k + "=" + "\"" + EncodeUrl(parameters[k]) + "\", ";
            else
                headerString += k + "=" + "\"" + parameters[k] + "\", ";

        }
        headerString = headerString.Remove(headerString.Length - 2, 2);
        return headerString;
    }
    
    private string GenerateOAuthNonce()
    {
        return Guid.NewGuid().ToString().Replace("-", "");
    }
    private string GenerateOauthSignature(string baseString, string secret_token = "")
    {
        HMACSHA1 hmac = new HMACSHA1(Encoding.ASCII.GetBytes(ConsumerSecret + "&" + secret_token));
        hmac.ComputeHash(Encoding.UTF8.GetBytes(baseString));

        string hash = Convert.ToBase64String(hmac.Hash);
        hash = hash.Replace("-", "");
        return hash;
    }
    private string EncodeUrl(string raw)
    {
        raw = HttpUtility.UrlEncode(raw);
        return Regex.Replace(raw, "(%[0-9a-f][0-9a-f])", c => c.Value.ToUpper());
    }

This approach works well without parameters. So, should I add the parameters such as “count” to Authorization header and use it for generating signature? I’ve been trying to do that and that was still 401


#4

I would recommend against rolling your own OAuth code. It’s complex and developers almost always get it wrong. Instead you should implement a community supported OAuth library that’s battle tested.

If you are really set on writing your own OAuth code you will have to follow the RFC. According to string construction count=3 should not be included as part of the URL for GenerateSignatureBaseString but should be included in the parameters instead.