2-legged OAuth implementation in C#
I'm trying to implement a two-legged OAuth authentication, so I can get to create a User on a website. I managed to craft this ruby code that performs that task, but I need to convert it to C#.
I'm getting a 401 error when making the last POST in C#, but the code is practically the same. I'm getting crazy here. Can someone jump in and help me know what's wrong with my code?
Ruby
consumer = OAuth::Consumer.new(KEY, SECRET, :http_method => :post)
# response = consumer.request(:get, "#{SITE}/oauth/request_token.json")
# request_token_hash = JSON.parse(response.body)
# puts request_token_hash
access_token = OAuth::AccessToken.new consumer
user = {
"user[name]" => 'John'
}
response = access_token.post("#{SITE}/users.json", user)
user_hash = JSON.parse(response.body)
puts user_hash
C#
string consumerKey = "KEY";
string consumerSecret = "SECRET";
Uri uri = new Uri("#{SITE}/oauth/request_token.json");
var oAuth = new OAuthBase(); // this class generates signatures
string nonce = oAuth.GenerateNonce();
string timeStamp = oAuth.GenerateTimeStamp();
string outURL;
string queryString;
string sig = oAuth.GenerateSignature(
uri,
consumerKey,
consumerSecret,
string.Empty,
string.Empty,
"POST",
timeStamp,
nonce,
OAuthBase.SignatureTypes.HMACSHA1,
out outURL,
out queryString);
sig = HttpUtility.UrlEncode(sig);
var sb = new StringBuilder(uri.ToString());
sb.AppendFormat("?oauth_consumer_key={0}&", consumerKey);
sb.AppendFormat("oauth_nonce={0}&", nonce);
sb.AppendFormat("oauth_timestamp={0}&", timeStamp);
sb.AppendFormat("oauth_signature_method={0}&", "HMAC-SHA1");
sb.AppendFormat("oauth_version={0}&", "1.0");
sb.AppendFormat("oauth_signature={0}", sig);
var tokenRequest = (HttpWebRequest)WebRequest.Create(sb.ToString());
tokenRequest.Method = "POST";
var response = tokenRequest.GetResponse().GetResponseStream();
if (response != null)
{
var responseReader = new StreamReader(response);
var requestToken = JsonConvert.DeserializeObject<RequestToken>(responseReader.ReadToEnd());
System.Diagnostics.Debug.WriteLine("REQUEST TOKEN: " + requestToken.token.oauth_token);
// EVERYTHING IS OK UNTIL HERE
// Creating user
nonce = oAuth.GenerateNonce();
timeStamp = oAuth.GenerateTimeStamp();
var usersUri = new Uri("#{SITE}/users.json");
// Generate signature
string userSig = oAuth.GenerateSignature(
userUri,
consumerKey,
consumerSecret,
requestToken.token.oauth_token,
requestToken.token.oauth_token_secret,
"POST",
timeStamp,
nonce,
OAuthBase.SignatureTypes.HMACSHA1,
out outURL,
out queryString);
userSig = HttpUtility.UrlEncode(userSig);
// Generate request URL
sb = new StringBuilder(userUri.ToString());
sb.AppendFormat("?oauth_consumer_key={0}&", consumerKey);
sb.AppendF开发者_运维知识库ormat("oauth_nonce={0}&", nonce);
sb.AppendFormat("oauth_timestamp={0}&", timeStamp);
sb.AppendFormat("oauth_signature_method={0}&", "HMAC-SHA1");
sb.AppendFormat("oauth_version={0}&", "1.0");
sb.AppendFormat("oauth_token={0}&", requestToken.token.oauth_token);
sb.AppendFormat("oauth_signature={0}&", userSig);
sb.Append("user[name]=John");
// Prepare web request...
var myRequest = (HttpWebRequest)WebRequest.Create(sb.ToString());
myRequest.Method = "POST";
// Get response and read it
var reader = new StreamReader(myRequest.GetResponse().GetResponseStream()); // THROWS AN UNAUTHORIZED EXCEPTION (401 Status Error)
System.Diagnostics.Debug.WriteLine("RESPONSE USER: " + reader.ReadToEnd());
Thanks,
I am not sure if the answer is still helpful for you or not. Here is a workable version of my implementation.
public static string GetOAuth_1_TwoLeggedHeaderString(string url, string httpMethod
, string consumerKey, string consumerSecret)
{
var timeStamp = ((int)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds).ToString();
var nonce = Guid.NewGuid().ToString("D");
var signatureMethod = "HMAC-SHA1";
var version = "1.0";
var signatureBase = Encode(httpMethod.ToUpper()) + "&";
signatureBase += Encode(url.ToLower()) + "&";
signatureBase += Encode("oauth_consumer_key=" + Encode(consumerKey) + "&" +
"oauth_nonce=" + Encode(nonce) + "&" +
"oauth_signature_method=" + Encode(signatureMethod) + "&" +
"oauth_timestamp=" + Encode(timeStamp) + "&" +
"oauth_version=" + Encode(version));
string signatureString;
using (HMACSHA1 crypto = new HMACSHA1())
{
string key = Encode(consumerSecret) + "&";
crypto.Key = Encoding.ASCII.GetBytes(key);
string hash = Convert.ToBase64String(crypto.ComputeHash(Encoding.ASCII.GetBytes(signatureBase)));
crypto.Clear();
signatureString = hash;
}
string SimpleQuote(string s) => '"' + s + '"';
return
"OAuth " +
"oauth_consumer_key=" + SimpleQuote(Encode(consumerKey)) + ", " +
"oauth_signature_method=" + SimpleQuote(Encode(signatureMethod)) + ", " +
"oauth_timestamp=" + SimpleQuote(Encode(timeStamp)) + ", " +
"oauth_nonce=" + SimpleQuote(Encode(nonce)) + ", " +
"oauth_version=" + SimpleQuote(Encode(version)) + ", " +
"oauth_signature=" + SimpleQuote(Encode(signatureString));
}
private static string Encode(string input)
{
if (string.IsNullOrEmpty(input))
return string.Empty;
return Encoding.ASCII.GetString(EncodeToBytes(input, Encoding.UTF8));
}
private static byte[] EncodeToBytes(string input, Encoding enc)
{
if (string.IsNullOrEmpty(input))
return new byte[0];
byte[] inbytes = enc.GetBytes(input);
// Count unsafe characters
int unsafeChars = 0;
char c;
foreach (byte b in inbytes)
{
c = (char)b;
if (NeedsEscaping(c))
unsafeChars++;
}
// Check if we need to do any encoding
if (unsafeChars == 0)
return inbytes;
byte[] outbytes = new byte[inbytes.Length + (unsafeChars * 2)];
int pos = 0;
for (int i = 0; i < inbytes.Length; i++)
{
byte b = inbytes[i];
if (NeedsEscaping((char)b))
{
outbytes[pos++] = (byte)'%';
outbytes[pos++] = (byte)IntToHex((b >> 4) & 0xf);
outbytes[pos++] = (byte)IntToHex(b & 0x0f);
}
else
outbytes[pos++] = b;
}
return outbytes;
}
private static bool NeedsEscaping(char c)
{
return !((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')
|| c == '-' || c == '_' || c == '.' || c == '~');
}
private static char IntToHex(int n)
{
if (n < 0 || n >= 16)
throw new ArgumentOutOfRangeException("n");
if (n <= 9)
return (char)(n + (int)'0');
else
return (char)(n - 10 + (int)'A');
}
Here is how to use HttpClient to call remote service.
string url = "[Remote Service Endpoint URL]";
// Call Drupal API to create new node
httpClient.DefaultRequestHeaders.Remove("Authorization");
httpClient.DefaultRequestHeaders.Add("Authorization"
, WebServiceUtils.GetOAuth_1_TwoLeggedHeaderString(url, "POST", "[Consumer Key]", "[Consumer Secret]"));
const string Format = @"{
""type"": ""sharing"",
""title"": ""test"",
""created"": ""12323233"",
""body"": {
""und"": {
""0"": {
""value"": ""test""
}
}
},
""name"": ""test""
}";
var bodyContent = new StringContent(Format, Encoding.UTF8, "application/json");
var result = await httpClient.PostAsync(url, bodyContent);
精彩评论