Problem authentication in POST


#1

Hi,
Since 14 of january 2014 we have problems to post messages on our profile by application program: the server returns error 401 (Authentication failed) only for POST method. If we use GET method to access information, it works.
We use API1.1 (https://api.twitter.com/1.1/statuses/update.json to post messages) and the script found on your server like indications.

the script used is below:

        string status = "@Hello World";
        string postBody = "status=" + Uri.EscapeDataString(status);
        string oauth_consumer_key = "ppppppppppppppppppp";
        string oauth_consumerSecret = "dddddddddddddddddddddddddddddddddddddddddd";
        string oauth_signature_method = "HMAC-SHA1";
        string oauth_version = "1.0";
        string oauth_token = "00000000-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
        string oauth_token_secret = "qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq";
									 						

        string oauth_nonce = Convert.ToBase64String(new ASCIIEncoding().GetBytes(DateTime.Now.Ticks.ToString()));
        TimeSpan timeSpan = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
        string oauth_timestamp = Convert.ToInt64(timeSpan.TotalSeconds).ToString();
        SortedDictionary<string, string> basestringParameters = new SortedDictionary<string, string>();
        //include the "in_reply_to_status_id" parameter if you need to reply to particular tweet.
        basestringParameters.Add("in_reply_to_status_id",
          "status id of the post to which we are going to reply");
        basestringParameters.Add("status", Uri.EscapeDataString(status));
        basestringParameters.Add("oauth_version", oauth_version);
        basestringParameters.Add("oauth_consumer_key", oauth_consumer_key);
        basestringParameters.Add("oauth_nonce", oauth_nonce);
        basestringParameters.Add("oauth_signature_method", oauth_signature_method);
        basestringParameters.Add("oauth_timestamp", oauth_timestamp);
        basestringParameters.Add("oauth_token", oauth_token);

        //Build the signature string
        string baseString = String.Empty;
        baseString += "POST" + "&";
        baseString += Uri.EscapeDataString("https://api.twitter.com/1.1/statuses/update.json") + "&";
        foreach (KeyValuePair<string, string> entry in basestringParameters)
        {
            baseString += Uri.EscapeDataString(entry.Key + "=" + entry.Value + "&");
        }

        //GS - Remove the trailing ambersand char, remember 
        //it's been urlEncoded so you have to remove the 
        //last 3 chars - %26
        baseString = baseString.Substring(0, baseString.Length - 3);

        //Build the signing key    
        string signingKey = Uri.EscapeDataString(oauth_consumerSecret) +
          "&" + Uri.EscapeDataString(oauth_token_secret);

        //Sign the request
        HMACSHA1 hasher = new HMACSHA1(new ASCIIEncoding().GetBytes(signingKey));
        string signatureString = Convert.ToBase64String(hasher.ComputeHash(new ASCIIEncoding().GetBytes(baseString)));

        //Tell Twitter we don't do the 100 continue thing
        ServicePointManager.Expect100Continue = false;

        HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(
          @"https://api.twitter.com/1.1/statuses/update.json");

        string authorizationHeaderParams = String.Empty;
        authorizationHeaderParams += "OAuth ";
        authorizationHeaderParams += "oauth_nonce=" + "\"" +
          Uri.EscapeDataString(oauth_nonce) + "\",";
        authorizationHeaderParams += "oauth_signature_method=" +
          "\"" + Uri.EscapeDataString(oauth_signature_method) + "\",";
        authorizationHeaderParams += "oauth_timestamp=" + "\"" +
          Uri.EscapeDataString(oauth_timestamp) + "\",";
        authorizationHeaderParams += "oauth_consumer_key=" + "\"" +
          Uri.EscapeDataString(oauth_consumer_key) + "\",";
        authorizationHeaderParams += "oauth_token=" + "\"" +
          Uri.EscapeDataString(oauth_token) + "\",";
        authorizationHeaderParams += "oauth_signature=" + "\"" +
          Uri.EscapeDataString(signatureString) + "\",";
        authorizationHeaderParams += "oauth_version=" + "\"" +
          Uri.EscapeDataString(oauth_version) + "\"";
        webRequest.Headers.Add("Authorization", authorizationHeaderParams);

        webRequest.Method = "POST";
        webRequest.ContentType = "application/x-www-form-urlencoded";
        Stream stream = webRequest.GetRequestStream();
        byte[] bodyBytes = new ASCIIEncoding().GetBytes(postBody);
        stream.Write(bodyBytes, 0, bodyBytes.Length);
        stream.Flush();
        stream.Close();

        //Allow us a reasonable timeout in case Twitter's busy
        webRequest.Timeout = 3 * 60 * 1000;
        try
        {
            //webRequest.Proxy = new WebProxy("enter proxy details/address");
            HttpWebResponse webResponse = webRequest.GetResponse() as HttpWebResponse;
            Stream dataStream = webResponse.GetResponseStream();
            // Open the stream using a StreamReader for easy access.
            StreamReader reader = new StreamReader(dataStream);
            // Read the content.
            string responseFromServer = reader.ReadToEnd();
        }
        catch (Exception ex)
        {
            AddMessage(new LogEntry() { LogLevel = LogLevels.ERROR, Message = DateTime.Now.ToString() + " - Run Twitter: " + ex.ToString() });

        }

#2

I’m hitting the same issue… Were you able to find a workaround for this?


#3

After some hair pulling, I found the solution that worked for me, and it’s two-fold:

  1. To properly set the $oauth_timestamp:
    $culture = New-Object System.Globalization.CultureInfo(“en-US”)
    $ts = [System.DateTime]::UtcNow - [System.DateTime]::ParseExact(“01/01/1970”, “dd/MM/yyyy”, $culture);
    $oauth_timestamp = [System.Convert]::ToInt64($ts.TotalSeconds).ToString();

  2. Then - you have to remember it won’t retweet the same test message if you run it again after it works - so add a date/time (get-date) to your tweet string, and voila - repeated tests that work fine for me…finally.

Good luck - a generic 401 error is tough and frustrating to resolve when there are alot of moving parts.