Note that there are some explanatory texts on larger screens.

plurals
  1. POOauth, Twitter, 401 Unauthorised
    primarykey
    data
    text
    <p>I want to break away from Twitsharp and utilise the User Streaming in twitter, in order to do this I need to write my own oAuth for authenticating my requests against the API. </p> <p>Having never had to do this I'm really struggling to implement it. I found an excellent example (<a href="http://garyshortblog.wordpress.com/2011/02/11/a-twitter-oauth-example-in-c/" rel="nofollow">http://garyshortblog.wordpress.com/2011/02/11/a-twitter-oauth-example-in-c/</a>) which I'm attempting to use to understand how to write my own. However I can't even get the example to work. Each time run it I always encounter 401 Unauthorised. My tokens etc. are fine, they work under Twitsharp. If I make a comparison using fiddler between a Twitsharp request and mine, they are exactly the same with the exception of the oauth_nonce and oauth_signature.</p> <p>This is what I have so far, thoughts appreciated. </p> <p><strong>Headers:</strong></p> <p>GOOD - Working with Twitsharp</p> <pre><code>oauth_consumer_key="xxx", oauth_nonce="eyn5x7hhj06tr8ic", oauth_signature="aZa5Fg7%2FO%2BbSlO9cYTL7OYLpkAM%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1332540179", oauth_token="xxx", oauth_version="1.0" </code></pre> <hr> <p>BAD - My Example</p> <pre><code>oauth_consumer_key="xxx", oauth_nonce="NjM0NjgxMzgyNDQ5MTgxMDk5", oauth_signature="bSryjrvc1t4kMaIpXCGe7uAFmUI%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1332541445", oauth_token="xxx", oauth_version="1.0" </code></pre> <p>Code:</p> <pre><code> /// &lt;summary&gt; /// The set of characters that are unreserved in RFC 2396 but are NOT unreserved in RFC 3986. /// &lt;/summary&gt; private static readonly string[] UriRfc3986CharsToEscape = new[] { "!", "*", "'", "(", ")" }; /// &lt;summary&gt; /// Escapes a string according to the URI data string rules given in RFC 3986. /// &lt;/summary&gt; /// &lt;param name="value"&gt;The value to escape.&lt;/param&gt; /// &lt;returns&gt;The escaped value.&lt;/returns&gt; /// &lt;remarks&gt; /// The &lt;see cref="Uri.EscapeDataString"/&gt; method is &lt;i&gt;supposed&lt;/i&gt; to take on /// RFC 3986 behavior if certain elements are present in a .config file. Even if this /// actually worked (which in my experiments it &lt;i&gt;doesn't&lt;/i&gt;), we can't rely on every /// host actually having this configuration element present. /// &lt;/remarks&gt; internal static string EscapeUriDataStringRfc3986(string value) { // Start with RFC 2396 escaping by calling the .NET method to do the work. // This MAY sometimes exhibit RFC 3986 behavior (according to the documentation). // If it does, the escaping we do that follows it will be a no-op since the // characters we search for to replace can't possibly exist in the string. StringBuilder escaped = new StringBuilder(Uri.EscapeDataString(value)); // Upgrade the escaping to RFC 3986, if necessary. for (int i = 0; i &lt; UriRfc3986CharsToEscape.Length; i++) { escaped.Replace(UriRfc3986CharsToEscape[i], Uri.HexEscape(UriRfc3986CharsToEscape[i][0])); } // Return the fully-RFC3986-escaped string. return escaped.ToString(); } public static void UserStream() { //GS - Get the oAuth params string status = "statusUpdate112"; string postBody = "status=" + EscapeUriDataStringRfc3986(status); string oauth_consumer_key = _consumerKey; string oauth_nonce = Convert.ToBase64String( new ASCIIEncoding().GetBytes( DateTime.Now.Ticks.ToString())); string oauth_signature_method = "HMAC-SHA1"; string oauth_token = _accessToken; TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); string oauth_timestamp = Convert.ToInt64(ts.TotalSeconds).ToString(); string oauth_version = "1.0"; //GS - When building the signature string the params //must be in alphabetical order. I can't be bothered //with that, get SortedDictionary to do it's thing SortedDictionary&lt;string, string&gt; sd = new SortedDictionary&lt;string, string&gt;(); sd.Add("status", status); sd.Add("oauth_version", oauth_version); sd.Add("oauth_consumer_key", oauth_consumer_key); sd.Add("oauth_nonce", oauth_nonce); sd.Add("oauth_signature_method", oauth_signature_method); sd.Add("oauth_timestamp", oauth_timestamp); sd.Add("oauth_token", oauth_token); //GS - Build the signature string string baseString = String.Empty; baseString += "POST" + "&amp;"; baseString += EscapeUriDataStringRfc3986( "http://api.twitter.com/1/statuses/update.json") + "&amp;"; foreach (KeyValuePair&lt;string, string&gt; entry in sd) { baseString += EscapeUriDataStringRfc3986(entry.Key + "=" + entry.Value + "&amp;"); } //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); //GS - Build the signing key string consumerSecret = _consumerSecret; string oauth_token_secret = _accessTokenSecret; string signingKey = EscapeUriDataStringRfc3986(consumerSecret) + "&amp;" + EscapeUriDataStringRfc3986(oauth_token_secret); //GS - Sign the request HMACSHA1 hasher = new HMACSHA1( new ASCIIEncoding().GetBytes(signingKey)); string signatureString = Convert.ToBase64String( hasher.ComputeHash( new ASCIIEncoding().GetBytes(baseString))); //GS - Tell Twitter we don't do the 100 continue thing ServicePointManager.Expect100Continue = false; //GS - Instantiate a web request and populate the //authorization header HttpWebRequest hwr = (HttpWebRequest)WebRequest.Create( @"https://api.twitter.com/1/statuses/update.json"); string authorizationHeaderParams = String.Empty; authorizationHeaderParams += "OAuth "; authorizationHeaderParams += "oauth_consumer_key=" + "\"" + EscapeUriDataStringRfc3986( oauth_consumer_key) + "\","; authorizationHeaderParams += "oauth_nonce=" + "\"" + EscapeUriDataStringRfc3986(oauth_nonce) + "\","; authorizationHeaderParams += "oauth_signature=" + "\"" + EscapeUriDataStringRfc3986(signatureString) + "\","; authorizationHeaderParams += "oauth_signature_method=" + "\"" + EscapeUriDataStringRfc3986(oauth_signature_method) + "\","; authorizationHeaderParams += "oauth_timestamp=" + "\"" + EscapeUriDataStringRfc3986(oauth_timestamp) + "\","; authorizationHeaderParams += "oauth_token=" + "\"" + EscapeUriDataStringRfc3986(oauth_token) + "\","; authorizationHeaderParams += "oauth_version=" + "\"" + EscapeUriDataStringRfc3986(oauth_version) + "\""; hwr.Headers.Add( "Authorization", authorizationHeaderParams); //added user agent hwr.UserAgent = "XserT"; //GS - POST off the request hwr.Method = "POST"; hwr.ContentType = "application/x-www-form-urlencoded"; Stream stream = hwr.GetRequestStream(); byte[] bodyBytes = new ASCIIEncoding().GetBytes(postBody); stream.Write(bodyBytes, 0, bodyBytes.Length); stream.Flush(); stream.Close(); //GS - Allow us a reasonable timeout in case //Twitter's busy hwr.Timeout = 3 * 60 * 1000; try { HttpWebResponse rsp = hwr.GetResponse() as HttpWebResponse; hwr.KeepAlive = false; //GS - Do something with the return here... } catch (WebException e) { //GS - Do some clever error handling here... } } </code></pre>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload