Oauth, Twitter, 401 Unauthorised

早过忘川 提交于 2020-01-17 05:45:40

问题


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.

Having never had to do this I'm really struggling to implement it. I found an excellent example (http://garyshortblog.wordpress.com/2011/02/11/a-twitter-oauth-example-in-c/) 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.

This is what I have so far, thoughts appreciated.

Headers:

GOOD - Working with Twitsharp

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"

BAD - My Example

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:

     /// <summary>
        /// The set of characters that are unreserved in RFC 2396 but are NOT unreserved in RFC 3986.
        /// </summary>
        private static readonly string[] UriRfc3986CharsToEscape = new[] { "!", "*", "'", "(", ")" };

        /// <summary>
        /// Escapes a string according to the URI data string rules given in RFC 3986.
        /// </summary>
        /// <param name="value">The value to escape.</param>
        /// <returns>The escaped value.</returns>
        /// <remarks>
        /// The <see cref="Uri.EscapeDataString"/> method is <i>supposed</i> 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 <i>doesn't</i>), we can't rely on every
        /// host actually having this configuration element present.
        /// </remarks>
        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 < 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<string, string> sd =
                new SortedDictionary<string, string>();

            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" + "&";
            baseString += EscapeUriDataStringRfc3986(
                "http://api.twitter.com/1/statuses/update.json")
                + "&";

            foreach (KeyValuePair<string, string> entry in sd)
            {
                baseString += EscapeUriDataStringRfc3986(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);

            //GS - Build the signing key
            string consumerSecret =
                _consumerSecret;

            string oauth_token_secret =
                _accessTokenSecret;

            string signingKey =
                EscapeUriDataStringRfc3986(consumerSecret) + "&" +
                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...
            }



        }

回答1:


i know this doesnt answer your question but if you try linqtotwitter you can do it easy.

There are examples that work in the source area

I also want to add Tweetsharp is probably depreciated. he stopped working on it early last year or so.




回答2:


The Uri.EscapeDataString does not use correct encoding mechanism.

For instance, check this other post out on the subject:

How to get Uri.EscapeDataString to comply with RFC 3986

In that post, you will find someone pasted a correct escape routine. Give it as shot!



来源:https://stackoverflow.com/questions/9847494/oauth-twitter-401-unauthorised

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!