steverb.com

Things I Felt Like Posting

Fun With OAuth – Part 2

Now that we have a clue about calling an OAuth secured service using Javascript, let’s try it again with C#. That way we can do the calling from the server side and not have to worry about exposing our secret key to the browser. This example is using the OAuth libray found at: http://oauth.googlecode.com/svn/code/csharp/OAuthBase.cs. There are other, more full featured C# OAuth libraries available (http://code.google.com/p/oauth-dot-net/ for instance), I like this one because it’s simple and I can grok everything it’s doing.

   1:  OAuthBase oAuth = new OAuthBase();
   2:   
   3:   
   4:   //gather all the values we'll need for the header and signature
   5:   
   6:  string nonce = oAuth.GenerateNonce();
   7:  string timestamp = oAuth.GenerateTimeStamp();
   8:  string key = KEY;
   9:  string secret = SECRET;
  10:  Uri url = new Uri("http://www.steverb.com");
  11:  string normedURL = "";
  12:  string requestParams = "";
  13:   
  14:   
  15:  //generate signature
  16:   
  17:  string sig = oAuth.GenerateSignature(url, key, secret, "", "", "GET", timestamp, nonce, out normedURL, out requestParams);
  18:   
  19:   
  20:  //url encode the signature
  21:  sig = HttpUtility.UrlEncode(sig);
  22:   
  23:   
  24:  //build the header string using the previously defined values
  25:  StringBuilder sb = new StringBuilder("OAuth realm=\"\"");
  26:  sb.AppendFormat(",oauth_consumer_key=\"{0}\"", key);
  27:  sb.AppendFormat(",oauth_nonce=\"{0}\"", nonce);
  28:  sb.AppendFormat(",oauth_timestamp=\"{0}\"", timestamp);
  29:  sb.AppendFormat(",oauth_signature_method=\"{0}\"", "HMAC-SHA1");
  30:  sb.AppendFormat(",oauth_version=\"{0}\"", "1.0");
  31:  sb.AppendFormat(",oauth_signature=\"{0}\"", sig);
  32:   
  33:   
  34:  string header = sb.ToString();
  35:   
  36:   
  37:  //add the header to our web request
  38:  client.Headers[HttpRequestHeader.Authorization] = header;

Not too bad, and it’s almost as simple on the service side. Basically, the service will pull the non-encrypted data out of the header, check and make sure that the nonce hasn’t already been used, and encrypt the whole thing with the secret key that both the client and service know about.

   1:  //first grab the header out of the http request
   2:  string header = WebOperationContext.Current.IncomingRequest.Headers[HttpRequestHeader.Authorization];
   3:   
   4:  OAuthBase oAuth = new OAuthBase();
   5:  Dictionary<string, string> OAuthValues = new Dictionary<string, string>();
   6:  //remove the leading "OAuth" from the header...pesky
   7:  string AuthHeader = Regex.Replace(OAuthHeader, "OAuth", "");
   8:  string[] Headers = AuthHeader.Split(',');
   9:   
  10:   
  11:  //add each of the values in the header to our dictionary
  12:  foreach (string entry in Headers)
  13:  {
  14:      string m_entry = entry.Trim().TrimEnd('"');
  15:      string[] values = Regex.Split(m_entry, "=\"");
  16:      if (values.Count() > 1)
  17:      {
  18:          OAuthValues.Add(values[0].Trim(), values[1].Trim().TrimEnd('"'));
  19:      }
  20:  }
  21:   
  22:   
  23:  //grab the values out of our dictionary for the signature method
  24:  string nonce = OAuthValues["oauth_nonce"] ?? "";
  25:  string timestamp = OAuthValues["oauth_timestamp"] ?? "";
  26:  string key = KEY;
  27:  string secret = SECRET;
  28:  Uri url = new Uri("http://www.steverb.com");
  29:  string normedURL = "";
  30:  string requestParams = "";
  31:   
  32:  //generate a signature
  33:  string sig = oAuth.GenerateSignature(url, key, secret, "", "", "GET", timestamp, nonce, out normedURL, out requestParams);
  34:   
  35:  //compare it to the signature in the header
  36:  if (sig != OAuthValues["oauth_signature"])
  37:  {
  38:      return false;
  39:  }
  40:  else
  41:  {
  42:      return true;
  43:  }


Tada! As long as the encrypted signature matches then the OAuth is good. A quick point, for a real implementation we would really want to check and make sure that the nonce had not been used before and that the timestamp on the message was fairly recent (in order to prevent replay attacks).

Comments are closed