So far as I can tell, I'm following the instructions accurately according to http://dev.twitter.com/pages/auth, but regardless of any minute variations, I keep getting a 401, authorization error on posting (i.e. the method pushMessage() fails). So far as I can tell, I'm not doing anything different, other than using POST, than I did during the initial authorization.
public class Twitter { private string tokenSecret = ""; private string createTimestamp() { var now = DateTime.UtcNow; var then = new DateTime(1970, 1, 1); var timespan = now - then; return ("" + (long)timespan.TotalSeconds); } private string createNonce() { const string ALPHANUMERIC = "qwertyuiopasdfghjklzxcvbnm" + "QWERTYUIOPASDFGHJKLZXCVBNM" + "1234567890" ; var sb = new StringBuilder(); var random = new Random(); for (int L = 0; L < 42; L++) { sb.Append( ALPHANUMERIC[random.Next(ALPHANUMERIC.Length)] ); } return sb.ToString(); } private string createSignature(string signatureBase) { string key = clientSecret + "&" + tokenSecret; var keyBytes = Encoding.UTF8.GetBytes(key); var sigMethod = new HMACSHA1(keyBytes); byte[] data = Encoding.UTF8.GetBytes(signatureBase); var hash = sigMethod.ComputeHash(data); var sig = Convert.ToBase64String(hash); sig = Uri.EscapeDataString(sig); return sig; } private void myInit() { int found = 0; string[] words = sessionData.Split('&'); for (uint L = 0; L < words.Length; L++) { if (words[L].StartsWith("oauth_token=")) { ++found; accessToken = words[L]; } else if (words[L].StartsWith("oauth_token_secret=")) { ++found; tokenSecret = words[L].Substring("oauth_token_secret=".Length); } else if (words[L].StartsWith("user_id=")) { ++found; userID = words[L].Substring("user_id=".Length); } else if (words[L].StartsWith("screen_name=")) { ++found; screenName = words[L].Substring("screen_name=".Length); } } if (found != 4) { throw new Exception("Unknown response from server"); } } public new void createSession(System.Web.UI.Page page) { if ( !string.IsNullOrEmpty(page.Request.QueryString["oauth_token"]) ) { string timestamp = createTimestamp(); string nonce = createNonce(); var sigBase = "GET" + "&" + Uri.EscapeDataString("https://api.twitter.com/oauth/ access_token") + "&" + Uri.EscapeDataString("oauth_consumer_key=" + clientID) + "%26" + Uri.EscapeDataString("oauth_nonce=" + nonce) + "%26" + Uri.EscapeDataString("oauth_signature_method=HMAC-SHA1") + "%26" + Uri.EscapeDataString("oauth_timestamp=" + timestamp) + "%26" + Uri.EscapeDataString("oauth_token=" + page.Request.QueryString["oauth_token"]) + "%26" + Uri.EscapeDataString("oauth_version=1.0") ; string signature = createSignature(sigBase); string url = "https://api.twitter.com/oauth/access_token"; WebClient client = new WebClient(); client.Headers["Authorization"] = "OAuth " + "oauth_consumer_key=\"" + clientID + "\", " + "oauth_nonce=\"" + nonce + "\", " + "oauth_signature=\"" + signature + "\", " + "oauth_signature_method=\"HMAC-SHA1\", " + "oauth_timestamp=\"" + timestamp + "\", " + "oauth_token=\"" + page.Request.QueryString["oauth_token"] + "\", " + "oauth_version=\"1.0\"" ; byte[] data = client.DownloadData(url); sessionData = Encoding.UTF8.GetString(data); myInit(); } else { string timestamp = createTimestamp(); string nonce = createNonce(); var sigBase = "GET" + "&" + Uri.EscapeDataString("https://api.twitter.com/oauth/ request_token") + "&" + Uri.EscapeDataString("oauth_consumer_key=" + clientID) + "%26" + Uri.EscapeDataString("oauth_nonce=" + nonce) + "%26" + Uri.EscapeDataString("oauth_signature_method=HMAC-SHA1") + "%26" + Uri.EscapeDataString("oauth_timestamp=" + timestamp) + "%26" + Uri.EscapeDataString("oauth_version=1.0") ; string signature = createSignature(sigBase); string url = "https://api.twitter.com/oauth/request_token"; WebClient client = new WebClient(); client.Headers["Authorization"] = "OAuth oauth_consumer_key=\"" + clientID + "\", " + "oauth_nonce=\"" + nonce + "\", " + "oauth_signature=\"" + signature + "\", " + "oauth_signature_method=\"HMAC-SHA1\", " + "oauth_timestamp=\"" + timestamp + "\", " + "oauth_version=\"1.0\"" ; byte[] data = client.DownloadData(url); string requestToken = Encoding.UTF8.GetString(data); bool found = false; string[] words = requestToken.Split('&'); for (uint L = 0; L < words.Length; L++) { if (words[L].StartsWith("oauth_token=")) { found = true; requestToken = words[L]; break; } } if (!found) { throw new Exception("Unknown response from Twitter server"); } page.Response.Redirect( "https://api.twitter.com/oauth/authorize?" + requestToken ); } } public new void continueSession(string data) { sessionData = data; myInit(); } public new void pushMessage(string message) { string timestamp = createTimestamp(); string nonce = createNonce(); var sigBase = "POST" + "&" + Uri.EscapeDataString("http://api.twitter.com/1/statuses/ update.json") + "&" + Uri.EscapeDataString("oauth_consumer_key=" + clientID) + "%26" + Uri.EscapeDataString("oauth_nonce=" + nonce) + "%26" + Uri.EscapeDataString("oauth_signature_method=HMAC-SHA1") + "%26" + Uri.EscapeDataString("oauth_timestamp=" + timestamp) + "%26" + Uri.EscapeDataString(accessToken) + "%26" + Uri.EscapeDataString("oauth_version=1.0") + "%26" + Uri.EscapeDataString("status=" + message) ; string signature = createSignature(sigBase); string url = "http://api.twitter.com/1/statuses/update.json"; var request = (HttpWebRequest)WebRequest.Create(url); request.Method = "POST"; request.Headers["Authorization"] = "OAuth " + "oauth_nonce=\"" + nonce + "\"," + "oauth_signature_method=\"HMAC-SHA1\"," + "oauth_timestamp=\"" + timestamp + "\"," + "oauth_consumer_key=\"" + clientID + "\"," + "oauth_token=\"" + accessToken.Substring("oauth_token=".Length) + "\"," + "oauth_signature=\"" + signature + "\"," + "oauth_version=\"1.0\"" ; byte[] data = Encoding.UTF8.GetBytes("status=" + message); request.ServicePoint.Expect100Continue = false; request.ContentType = "application/x-www-form-urlencoded"; request.ContentLength = data.Length; using (Stream stream = request.GetRequestStream()) { stream.Write(data, 0, data.Length); } request.GetResponse(); } } -- Twitter developer documentation and resources: http://dev.twitter.com/doc API updates via Twitter: http://twitter.com/twitterapi Issues/Enhancements Tracker: http://code.google.com/p/twitter-api/issues/list Change your membership to this group: http://groups.google.com/group/twitter-development-talk?hl=en