ページ

Saturday, August 20, 2011

facebook and twitter application implemented in silverlight.

It's easy to create facebook and twitter application implemented in silverlight, but as for twitter, it doesn't work because of Cross-Domain matter.
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.

No comments:

Post a Comment