So as one of the solutions, It should be implemented with 'Out of browser' & 'Elevated Trusted mode'.
Then, it will be a kind of desktop application.( I actually left 'Callback URL' blank. )
However, there's an important reminder about it.
How we can implant 'consumer_secret' into the application.
or
How we can let the application get/download 'consumer_secret' from somewhere else.
Well, I don't know the best practice, but I did encrypt the key as a countermeasure.
* Maybe, It's better to use web pages like asp.net before getting oauth_token and oauth_token_secret.
Anyway, I tried to create the applications because I wanted to know how 'OAuth 1.0a' and 'OAuth 2.0' work.
Silverlight client for facebook.
https://social-media-applications.appspot.com/facebook/silverlight/index.html
Silverlight client for twitter ( Out Of Browser ).
https://social-media-applications.appspot.com/twitter/silverlight/index.html
Get uri, query and headers
private void OAuth(string url, string httpMethod, Dictionary<string, string> parameters, out Uri uri, out string query, out Dictionary<HttpRequestHeader, string> headers) { query = string.Empty; uri = null; headers = new Dictionary<HttpRequestHeader, string>(); parameters["oauth_signature"] = string.Empty; var param = parameters.OrderBy(x => x.Key); StringBuilder sb = new StringBuilder(); foreach (var p in param) { if (!string.IsNullOrEmpty(p.Value)) sb.Append(@"&" + p.Key + "=" + UrlEncode(p.Value)); } sb.Remove(0, 1); string signatureBase = string.Format(@"{0}&{1}&{2}", httpMethod.ToUpper(), UrlEncode(url), UrlEncode(sb.ToString())); HMACSHA1 hmacsha1 = new HMACSHA1(); hmacsha1.Key = Encoding.UTF8.GetBytes(string.Format("{0}&{1}", UrlEncode(parameters["consumer_secret"]), UrlEncode(parameters["oauth_token_secret"]))); byte[] dataBuffer = Encoding.UTF8.GetBytes(signatureBase); byte[] hashBytes = hmacsha1.ComputeHash(dataBuffer); string sig = Convert.ToBase64String(hashBytes); sb.Append(@"&oauth_signature=" + UrlEncode(sig)); parameters["oauth_signature"] = sig; query = sb.ToString(); string getURL = url; if (httpMethod.ToUpper().Equals("GET")) { if (!string.IsNullOrEmpty(query)) getURL += '?' + query; } uri = new Uri(getURL, UriKind.RelativeOrAbsolute); if (httpMethod.ToUpper().Equals("POST")) { headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded"; headers[HttpRequestHeader.Authorization] = GetHeader(uri, parameters); } }
URL Encode
public string UrlEncode(string value) { string urlChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~"; StringBuilder result = new StringBuilder(); byte[] data = Encoding.UTF8.GetBytes(value); for (int i = 0; i < data.Length; i++) { int c = data[i]; if (c < 0x80 && urlChars.Contains((char)c)) result.Append((char)c); else result.Append('%' + String.Format("{0:X2}", (int)data[i])); } return result.ToString(); }
Nonce
private string GetNonce() { string nonceChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; StringBuilder result = new StringBuilder(8); Random random = new Random(); for (int i = 0; i < 8; ++i) result.Append(nonceChars[random.Next(nonceChars.Length)]); return result.ToString(); }
Timestamp
private string GetTimestamp() { TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0); return Convert.ToInt64(ts.TotalSeconds).ToString(); }
Authorization Header
private string GetHeader(Uri uri, Dictionary<string, string> parameters) { StringBuilder realm = new StringBuilder(); realm.Append(uri.Scheme); realm.Append("://"); realm.Append(uri.Host); if (uri.Port != 80 && uri.Port != 443) realm.Append(':' + uri.Port); StringBuilder header = new StringBuilder(); header.Append("Authorization: "); header.Append("OAuth realm=\"" + realm.ToString() + "\""); foreach (var p in parameters) { header.Append(", " + p.Key + "=\"" + UrlEncode(p.Value) + "\""); } header.Append("\r\n"); return header.ToString(); }
Usage : Post message on your twitter
public void Tweet(string message) { Dictionary<string, string> param = new Dictionary<string, string>(); param["oauth_consumer_key"] = "xxxxxxxxxxx"; // .... setting required parameters param["oauth_version"] = "1.0"; param["status"] = message; Uri uri; string query; Dictionary<HttpRequestHeader, string> headers; OAuth("https://api.twitter.com/statuses/update.json", "POST", param, out uri, out query, out headers); WebClient wc = new WebClient(); foreach (var header in headers) wc.Headers[header.Key] = header.Value; wc.UploadStringCompleted += OnUploadStringCompleted; wc.UploadStringAsync(uri, query); } void OnUploadStringCompleted(object sender, UploadStringCompletedEventArgs e) { // Get Json formatted data by e.Result if e.Error equals NULL. }
OAuth 1.0a is really harder to be implemented than OAuth 2.0, but this taught me a good lesson.