The app that I've been working on in my spare time (well one of them anyway), interfaces with netflix. Netflix, for those who are unaware, has a developer API that you can use in various applications. The main issue that you'll run into is that the documentation that they give is, well, it could be better. Especially if you've never used OAuth before as it is used along with REST in their API.
OAuth is a protocol that is used to help in authorization. When it comes down to it, it's just an URL with the information added as a query string. This makes it extremely easy to use, especially if you have some experience with it. I am unfortunately one of those individuals that had never used it before, but thankfully the specs are pretty straightforward enough. Anyway, after taking a look at some other people's code, I came up with a base class to help me if I ever need to do anything with OAuth again:
/// <summary>
/// OAuth base class
/// </summary>
public class OAuth
{
#region Constructor
/// <summary>
/// Constructor
/// </summary>
public OAuth()
{
Parameters = new System.Collections.Generic.List<Pair<string,string>>();
AddParameter("oauth_consumer_key", "");
AddParameter("oauth_nonce", "");
AddParameter("oauth_signature_method", "");
AddParameter("oauth_timestamp", "");
AddParameter("oauth_version", "1.0");
RandomGenerator = new Random();
}
#endregion
#region Protected Functions
/// <summary>
/// Generates a request
/// </summary>
/// <returns>The string containing the request</returns>
protected string GenerateRequest()
{
string Url="";
string Parameters="";
string Signature=GenerateSignature(out Url,out Parameters);
return Url.ToString() + "?" + Parameters + "&oauth_signature=" + UrlEncode(Signature);
}
/// <summary>
/// Generates the signature
/// </summary>
/// <param name="Url">Url</param>
/// <param name="Parameters">Parameters</param>
/// <returns>The signature</returns>
protected string GenerateSignature(out string Url,out string Parameters)
{
Parameters = "";
Url = "";
if (this.SignatureType == Signature.HMACSHA1)
{
string Base = GenerateBase(out Url, out Parameters);
HMACSHA1 SHA1 = new HMACSHA1();
SHA1.Key = Encoding.ASCII.GetBytes(UrlEncode(ConsumerKeySecret) + "&" + (string.IsNullOrEmpty(TokenSecret) ? "" : UrlEncode(TokenSecret)));
return Convert.ToBase64String(SHA1.ComputeHash(System.Text.Encoding.ASCII.GetBytes(Base)));
}
else if (this.SignatureType == Signature.RSASHA1)
{
throw new NotImplementedException();
}
else if (this.SignatureType == Signature.PLAINTEXT)
{
return UrlEncode(ConsumerKeySecret + "&" + TokenSecret);
}
return "";
}
/// <summary>
/// Does url encoding using uppercase since that is needed for .Net
/// </summary>
/// <param name="Input">Input string</param>
/// <returns>Url encoded string</returns>
protected string UrlEncode(string Input)
{
StringBuilder Result = new StringBuilder();
for (int x = 0; x < Input.Length; ++x)
{
if (UnreservedChars.IndexOf(Input[x]) != -1)
Result.Append(Input[x]);
else
Result.Append("%" + String.Format("{0:X2}", (int)Input[x]));
}
return Result.ToString();
}
/// <summary>
/// Adds a parameter
/// </summary>
/// <param name="Key">Key text</param>
/// <param name="Value">Value text</param>
protected void AddParameter(string Key, string Value)
{
bool Found = false;
foreach (Pair<string, string> Pair in Parameters)
{
if (Pair.Left == Key)
{
Pair.Right = Value;
Found = true;
break;
}
}
if (!Found)
{
Parameters.Add(new Pair<string, string>(Key, Value));
}
}
#endregion
#region Private Functions
/// <summary>
/// Generates the info used in the signature
/// </summary>
/// <param name="UrlString">Url string</param>
/// <param name="ParameterString">Parameter string</param>
/// <returns>The base information for the signature</returns>
private string GenerateBase(out string UrlString, out string ParameterString)
{
StringBuilder UrlBuilder = new StringBuilder();
StringBuilder Builder = new StringBuilder();
StringBuilder ParameterBuilder = new StringBuilder();
string SignatureMethod = "";
if (this.SignatureType == Signature.HMACSHA1)
SignatureMethod = "HMAC-SHA1";
else if (this.SignatureType == Signature.RSASHA1)
SignatureMethod = "RSA-SHA1";
else if (this.SignatureType == Signature.PLAINTEXT)
SignatureMethod = "PLAINTEXT";
AddParameter("oauth_consumer_key", this.ConsumerKey);
AddParameter("oauth_nonce", RandomGenerator.Next(123400, 9999999).ToString());
AddParameter("oauth_signature_method", SignatureMethod);
AddParameter("oauth_timestamp", Convert.ToInt64((DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0)).TotalSeconds).ToString());
AddParameter("oauth_version", "1.0");
if (!string.IsNullOrEmpty(this.Token))
AddParameter("oauth_token", this.Token);
Parameters.Sort(new PairComparer());
string Splitter = "";
foreach (Pair<string, string> Key in Parameters)
{
ParameterBuilder.Append(Splitter)
.Append(Key.Left)
.Append("=")
.Append(UrlEncode(Key.Right));
Splitter = "&";
}
UrlBuilder.Append(Url.Scheme).Append("://").Append(Url.Host);
if ((Url.Scheme == "http" && Url.Port != 80) || (Url.Scheme == "https" && Url.Port != 443))
UrlBuilder.Append(":").Append(Url.Port);
UrlBuilder.Append(Url.AbsolutePath);
UrlString = UrlBuilder.ToString();
ParameterString = ParameterBuilder.ToString();
Builder.Append(this.Method.ToString().ToUpper())
.Append("&")
.Append(UrlEncode(UrlBuilder.ToString()))
.Append("&")
.Append(UrlEncode(ParameterBuilder.ToString()));
return Builder.ToString();
}
#endregion
#region Protected Properties
/// <summary>
/// Url that is being used
/// </summary>
protected Uri Url { get; set; }
/// <summary>
/// Consumer key
/// </summary>
protected string ConsumerKey { get; set; }
/// <summary>
/// Consumer key secret
/// </summary>
protected string ConsumerKeySecret { get; set; }
/// <summary>
/// Token
/// </summary>
protected string Token { get; set; }
/// <summary>
/// Token secret
/// </summary>
protected string TokenSecret { get; set; }
/// <summary>
/// HTTP Method
/// </summary>
protected HTTPMethod Method { get; set; }
/// <summary>
/// The hash type that is suppose to be used
/// </summary>
protected Signature SignatureType { get; set; }
#endregion
#region Private Variables
private string UnreservedChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~";
private System.Collections.Generic.List<Pair<string, string>> Parameters { get; set; }
private Random RandomGenerator { get; set; }
#endregion
#region Private Classes
/// <summary>
/// Comparer class for the pair type
/// </summary>
private class PairComparer : IComparer<Pair<string,string>>
{
public int Compare(Pair<string, string> x, Pair<string, string> y)
{
if (x.Left == y.Left)
{
return string.Compare(x.Right, y.Right);
}
else
{
return string.Compare(x.Left, y.Left);
}
}
}
#endregion
}
#region Enums
/// <summary>
/// HTTP Method
/// </summary>
public enum HTTPMethod
{
GET,
POST,
DELETE,
PUT
}
/// <summary>
/// Hash type
/// </summary>
public enum Signature
{
PLAINTEXT,
RSASHA1,
HMACSHA1
}
#endregion
The pair class that it uses can be found here:
/// <summary>
/// Pairs two items together
/// </summary>
/// <typeparam name="T1">Type for the left hand side</typeparam>
/// <typeparam name="T2">Type for the right hand side</typeparam>
public class Pair<T1, T2>
{
#region Constructor
/// <summary>
/// Constructor
/// </summary>
public Pair()
{
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="Left">Left hand side of the pair</param>
/// <param name="Right">Right hand side of the pair</param>
public Pair(T1 Left, T2 Right)
{
this.Left = Left;
this.Right = Right;
}
#endregion
#region Properties
/// <summary>
/// Left hand item
/// </summary>
public T1 Left { get; set; }
/// <summary>
/// Right hand item
/// </summary>
public T2 Right { get; set; }
#endregion
#region Public Overridden Properties
public override int GetHashCode()
{
if (Left != null && Right != null)
{
return Left.GetHashCode() ^ Right.GetHashCode();
}
return 0;
}
public override bool Equals(object obj)
{
if (obj != null && obj is Pair<T1, T2>)
{
return Equals(Left, ((Pair<T1, T2>)obj).Left) && Equals(Right, ((Pair<T1, T2>)obj).Right);
}
return false;
}
#endregion
}
The pair code should be rather straight forward, the OAuth code on the other hand may need some explaining.There is really only one function that you should need to call, GenerateRequest. However prior to calling that, you will need to set up the object (adding the parameters that it needs). Specifically you need to add the url, signature type (hashing type), HTTP method (GET, POST, DELETE, PUT), as well as the consumer key and consumer key secret. The consumer key and consumer key secret are supplied by the service provider (in my case Netflix). The signature type (the only other parameter that you may have questions with), is really the hashing algorithm that should be used. OAuth uses three different options when hashing, HMAC-SHA1, RSA-SHA1, and plain text. HMAC-SHA1 is what netflix uses and thus I put in the code for that along with plain text for testing purposes. RSA-SHA1 isn't implemented but it should be fairly easy to add
There are a couple quirks though when dealing with OAuth. First, the query string needs to be sorted by the key. On top of that, since it is simply an url that you will be getting back, it needs to use url encoding. However you will notice that I'm not using the built in function in .Net. The reason for this is the simple fact that the .Net implementation uses lower case and OAuth seems to require upper case. That's it though (and the code above takes care of all of that). All you need to do is inherit from the base class here and set things up. So try it out, leave feedback, and happy coding.
38a7b743-e50d-457d-bfff-73b5172ce3e9|1|5.0