Error 401, Failed to validate oauth signature and token on request_token


#1

I’m getting that error every time (I’m writing my own OAuth because all the libraries are too big for what I need - simple authentication). I’ve made many changes and looked at both the documentation and several online examples but nothing is working and I always get the same error.

Here’s an example of what I’m sending to https://api.twitter.com/request_token:
Header (with secure parts XXXed):
OAuth oauth_callback=“https%3A%2F%2Fdev.auntminnie.com%2Fsecure%2FExternalAuthentication.aspx%3Fsite%3Dtwitter”,oauth_consumer_key=“qqGXXXXXXXXXXPxFg”,oauth_nonce=“YWZkYmM4YWMxMzgwNDk2NmJkMzYzYTFiZmI2MzA5YTQ%3D”,oauth_signature=“FUSJgQsXuiU6RpPDZbd%2FWhvUfEU%3D”,oauth_signature_method=“HMAC-SHA1”,oauth_timestamp=“1421336212”,oauth_version=“1.0”

Base String (from which the signature was obtained):
POST&https%3A%2F%2Fapi.twitter.com%2Foauth%2Frequest_token&oauth_consumer_key%3DqXXXXXXXXXXjlX3zjwPxFg%26oauth_nonce%3DYWZkYmM4YWMxMzgwNDk2NmJkMzYzYTFiZmI2MzA5YTQ%3D%26oauth_signature_method%3DHMAC_SHA1%26oauth_timestamp%3D1421336212%26oauth_version%3D1.0

Any insights as to what I’m doing wrong?

Here’s the code:

  String strAuthFormat = "OAuth oauth_callback=\"{0}\"," +
                         "oauth_consumer_key=\"{1}\"," +
                         "oauth_nonce=\"{2}\"," +
                         "oauth_signature=\"{3}\"," +
                         "oauth_signature_method=\"{4}\"," +
                         "oauth_timestamp=\"{5}\"," +                             
                         "oauth_version=\"{6}\"";
  SortedDictionary<string, string> sdBaseStringParameters = new SortedDictionary<string, string>();

  //String strURL = strSecureUrl + "ExternalAuthentication.aspx?site=twitter";
  String strURL = "https://dev.auntminnie.com/secure/ExternalAuthentication.aspx?site=twitter";
  String strApiVersion = "1.0";
  String strMethod = "POST";
  String strRedirect = "";
  Guid guidState = Guid.NewGuid();
  String strSQL = "SELECT meas_ClientID, meas_ClientSecret, meas_StartURL " +
                  "FROM   tblMemberExternalAuthenticationSite " +
                  "WHERE  meas_Name = 'twitter'";
  String strOauthConsumerKey = "";
  String strOauthConsumerSecret = "";
  String strSignKey = "";
  String strRedirect = "https://api.twitter.com/request_token?site=twitter"
  String strOauthConsumerKey = "[our key]";
  String strOauthConsumerSecret = "[our secret]";
     }
  }
  var Timestamp = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
  var oauth_Timestamp = Convert.ToInt64(Timestamp.TotalSeconds).ToString();
  String strOauthNonce = Convert.ToBase64String(new ASCIIEncoding().GetBytes(guidState.ToString().Replace("-", "")));
  sdBaseStringParameters.Add("oauth_version", strApiVersion);
  sdBaseStringParameters.Add("oauth_consumer_key", strOauthConsumerKey);
  sdBaseStringParameters.Add("oauth_nonce", strOauthNonce);
  sdBaseStringParameters.Add("oauth_signature_method", "HMAC_SHA1");
  sdBaseStringParameters.Add("oauth_timestamp", oauth_Timestamp);
  StringBuilder sbBaseString = new StringBuilder();
  sbBaseString.Append(strMethod);
  sbBaseString.Append("&");
  sbBaseString.Append(TwitterEncode(Uri.EscapeDataString(strRedirect)));
  sbBaseString.Append("&");
  foreach (KeyValuePair<string, string> entry in sdBaseStringParameters)
  {
     sbBaseString.Append(TwitterEncode(Uri.EscapeDataString(entry.Key + "=" + entry.Value + "&")));
  }
  String strBaseString = sbBaseString.ToString().Substring(0, sbBaseString.Length - 3);
  String strSignature = "";
  strSignKey = TwitterEncode(Uri.EscapeDataString(strOauthConsumerKey)) + "&" + TwitterEncode(Uri.EscapeDataString(strOauthConsumerSecret));
  using (HMACSHA1 hasher = new HMACSHA1(ASCIIEncoding.ASCII.GetBytes(strSignKey)))
  {
     strSignature = Convert.ToBase64String(hasher.ComputeHash(ASCIIEncoding.ASCII.GetBytes(strBaseString)));
  }
  String strAuthHeader = string.Format(strAuthFormat,
                                        Uri.EscapeDataString(strURL), 
                                        Uri.EscapeDataString(strOauthConsumerKey),
                                        Uri.EscapeDataString(strOauthNonce),
                                        Uri.EscapeDataString(strSignature),
                                        "HMAC-SHA1",
                                        oauth_Timestamp,
                                        strApiVersion);      
  ServicePointManager.Expect100Continue = false;
  HttpWebRequest objRequest = (HttpWebRequest)WebRequest.Create(strRedirect);
  objRequest.Headers.Add("Authorization", strAuthHeader);
  objRequest.Method = strMethod;
  objRequest.ContentType = "application/x-www-form-urlencoded";
  objRequest.ContentLength = 0;
  WebResponse objResponse = null;
  try
  {
     objResponse = objRequest.GetResponse();
     objResponse.Close();
  }
  catch (WebException e)
  {
     Response.Write(e.Message);
     var objErrResponse = (WebResponse)e.Response;
     StreamReader objSR = new StreamReader(objErrResponse.GetResponseStream());
     String strResponse = objSR.ReadToEnd();
     Response.Write("<br/>" + strResponse);
  }
  Response.Write("<br/><br/>Header::<br/>");
  Response.Write(strAuthHeader);
  Response.Write("<br/><br/>");
  Response.Write("Base String::<br/>");
  Response.Write(strBaseString);

AND

private string TwitterEncode(String strData)
{
if (strData.Contains("!"))
strData = strData.Replace("!", “%21”);
if (strData.Contains("’"))
strData = strData.Replace("’", “%27”);
if (strData.Contains("("))
strData = strData.Replace("(", “%28”);
if (strData.Contains(")"))
strData = strData.Replace(")", “%29”);
if (strData.Contains(""))
strData = strData.Replace("
", “%2A”);
if (strData.Contains(","))
strData = strData.Replace(",", “%2C”);

  return (strData);

}


#2

I’ve plugged to resulting base string into a base string validator I found online (http://quonos.nl/oauthTester/) and used their dummy consumer secret to encode it and got the same signature as they did. (So, looks like I’m encoding the base string correctly).

Is there ANY WAY to get some kind of debug information back from the Twitter API that will give me SOME indication of what’s wrong with my request?

Thanks,
Owen


#3

Hmmm, the base-string validator says my oauth_callback is not properly escaped.

Here’s the string from my base-string:
oauth_callback=https%3A%2F%2Fdev.auntminnie.com%2Fsecure%2FExternalAuthentication.aspx%3Fsite%3Dtwitter

How do I escape it any more than that?

–Owen


#4

Can I get some help here?

This seems to be a major stumbling block to getting Twitter OAuth to work (Google searches turn up tons of people having the same issue with no resolution that I’ve found)…


#5

OK. I got the base string validator to validate the base string (the oauth_callback parameter has to be url encoded TWICE for use in the base string. BUT, I’m still getting the 401 error.

How do I tell what part of the request is faulty? Where do I look next?


#6

GOT IT!

What a pain in the tush!

The last bit was to make sure, in the base string, that each parameter is completely url encoded.

So, instead of this:
sbBaseString.Append(TwitterEncode(Uri.EscapeDataString(entry.Key) + “=” + Uri.EscapeDataString(entry.Value +"&")));
Use this:
sbBaseString.Append(TwitterEncode(Uri.EscapeDataString(entry.Key + “=” + entry.Value +"&")));

VERY SNEAKY!
Hope this helps someone else having the same issue!

–Owen