问题
I'm trying to get a request token from Twitter OAuth, but I keep getting 401 Unauthorized
, but I have no idea why. I have tried to follow the signature creation as good as I can and was struggling with 400 Bad Request
for a while but finally cracked the code to get a valid request only to be met by the 401.
I feel like I'm missing something really simple but I just can't find out what.
Here's the code I'm using:
using (HttpClient client = new HttpClient())
{
client.BaseAddress = new Uri("https://api.twitter.com/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
string oauth_nonce = Convert.ToBase64String(new ASCIIEncoding().GetBytes(DateTime.Now.Ticks.ToString()));
string oauth_callback = Uri.EscapeUriString("oob");
string oauth_signature_method = "HMAC-SHA1";
string oauth_timestamp = Convert.ToInt64(ts.TotalSeconds).ToString();
string oauth_consumer_key = OAUTH_KEY;
string oauth_version = "1.0";
// Generate signature
string baseString = "POST&";
baseString += Uri.EscapeDataString(client.BaseAddress + "oauth/request_token") + "&";
// Each oauth value sorted alphabetically
baseString += Uri.EscapeDataString("oauth_callback=" + oauth_callback + "&");
baseString += Uri.EscapeDataString("oauth_consumer_key=" + oauth_consumer_key + "&");
baseString += Uri.EscapeDataString("oauth_nonce=" + oauth_nonce + "&");
baseString += Uri.EscapeDataString("oauth_signature_method=" + oauth_signature_method + "&");
baseString += Uri.EscapeDataString("oauth_timestamp=" + oauth_timestamp + "&");
baseString += Uri.EscapeDataString("oauth_version=" + oauth_version + "&");
string signingKey = Uri.EscapeDataString(OAUTH_SECRET) + "&";
HMACSHA1 hasher = new HMACSHA1(new ASCIIEncoding().GetBytes(signingKey));
string oauth_signature = Convert.ToBase64String(hasher.ComputeHash(new ASCIIEncoding().GetBytes(baseString)));
client.DefaultRequestHeaders.Add("Authorization", "OAuth " +
"oauth_nonce=\"" + oauth_nonce + "\"," +
"oauth_callback=\"" + oauth_callback + "\"," +
"oauth_signature_method=\"" + oauth_signature_method + "\"," +
"oauth_timestamp=\"" + oauth_timestamp + "\"," +
"oauth_consumer_key=\"" + oauth_consumer_key + "\"," +
"oauth_signature=\"" + Uri.EscapeDataString(oauth_signature) + "\"," +
"oauth_version=\"" + oauth_version + "\""
);
HttpResponseMessage response = await client.PostAsJsonAsync("oauth/request_token", "");
var responseString = await response.Content.ReadAsStringAsync();
}
I'm using .Net 4.5 so the Uri.EscapeDataString()
should give the correct percent encoded string.
EDIT
I noticed that the oauth_nonce
value sometimes included non-alphanumeric characters so I changed the variable calculation to the following:
string nonce = Convert.ToBase64String(new ASCIIEncoding().GetBytes(DateTime.Now.Ticks.ToString()));
string oauth_nonce = new string(nonce.Where(c => Char.IsLetter(c) || Char.IsDigit(c)).ToArray());
This unfortunately didn't help.
I've also tried Uri.EscapeUriData()
for all the values in the Authorization
header, as well as adding a space after each comma (as the documentation on Twitter says so), but that didn't help either.
回答1:
I started from scratch and tried a slightly different approach, but it should've yielded the same result. However, it didn't and instead works as intended!
If anybody can point of the practical difference between this working code and the code in the original question, I would really appreciate it, because to my eyes they produce identical results.
using (HttpClient client = new HttpClient())
{
client.BaseAddress = new Uri("https://api.twitter.com/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
TimeSpan timestamp = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
string nonce = Convert.ToBase64String(new ASCIIEncoding().GetBytes(DateTime.Now.Ticks.ToString()));
Dictionary<string, string> oauth = new Dictionary<string, string>
{
["oauth_nonce"] = new string(nonce.Where(c => char.IsLetter(c) || char.IsDigit(c)).ToArray()),
["oauth_callback"] = "http://my.callback.url",
["oauth_signature_method"] = "HMAC-SHA1",
["oauth_timestamp"] = Convert.ToInt64(timestamp.TotalSeconds).ToString(),
["oauth_consumer_key"] = OAUTH_KEY,
["oauth_version"] = "1.0"
};
string[] parameterCollectionValues = oauth.Select(parameter =>
Uri.EscapeDataString(parameter.Key) + "=" +
Uri.EscapeDataString(parameter.Value))
.OrderBy(kv => kv)
.ToArray();
string parameterCollection = string.Join("&", parameterCollectionValues);
string baseString = "POST";
baseString += "&";
baseString += Uri.EscapeDataString(client.BaseAddress + "oauth/request_token");
baseString += "&";
baseString += Uri.EscapeDataString(parameterCollection);
string signingKey = Uri.EscapeDataString(OAUTH_SECRET);
signingKey += "&";
HMACSHA1 hasher = new HMACSHA1(new ASCIIEncoding().GetBytes(signingKey));
oauth["oauth_signature"] = Convert.ToBase64String(hasher.ComputeHash(new ASCIIEncoding().GetBytes(baseString)));
string headerString = "OAuth ";
string[] headerStringValues = oauth.Select(parameter =>
Uri.EscapeDataString(parameter.Key) + "=" + "\"" +
Uri.EscapeDataString(parameter.Value) + "\"")
.ToArray();
headerString += string.Join(", ", headerStringValues);
client.DefaultRequestHeaders.Add("Authorization", headerString);
HttpResponseMessage response = await client.PostAsJsonAsync("oauth/request_token", "");
var responseString = await response.Content.ReadAsStringAsync();
}
回答2:
Thank you @GTHvidsten for your example.
By digging into your working second example, I discovered the issue I had with my code, and it's probably the same reason your original example wasn't working.
It's not enough to encode the individual request parameter key/value pairs that constitute the signature base string, but apparently you have to also encode the entire joined key/value request parameter string, which will thus encode the '=' signs. In your second example, this line of code does just that: baseString += Uri.EscapeDataString(parameterCollection);
In your original example, there is no code that encodes the request parameters.
来源:https://stackoverflow.com/questions/44020273/twitter-oauth-request-token-returning-unauthorized