tags:

views:

155

answers:

2

I'm trying to convert this PHP cookie parsing snippet into C#, but my PHP is a bit rusty. It's taken from a facebook SDK sample.

<?php

define('FACEBOOK_APP_ID', 'your application id');
define('FACEBOOK_SECRET', 'your application secret');

function get_facebook_cookie($app_id, $application_secret) {
    $args = array();
    parse_str(trim($_COOKIE['fbs_' . $app_id], '\\"'), $args);
    ksort($args);
    $payload = '';
    foreach ($args as $key => $value) {
        if ($key != 'sig') {
            $payload .= $key . '=' . $value;
        }
    }
    if (md5($payload . $application_secret) != $args['sig']) {
      return null;
    }
    return $args;
}

$cookie = get_facebook_cookie(FACEBOOK_APP_ID, FACEBOOK_SECRET);
echo 'The ID of the current user is ' . $cookie['uid'];

?>

This is what I have so far, but its not quite right:

protected override void OnInit(EventArgs e)
{
    base.OnInit(e);

    HttpCookie cookie = GetCookie();
    IsLoggedIn = cookie != null;
}

private HttpCookie GetCookie()
{
    // based on the php example at http://developers.facebook.com/docs/authentication/
    HttpCookie cookie = Request.Cookies["fbs_" + FacebookClientId];
    StringBuilder payload = new StringBuilder();
    if (cookie != null)
    {
        foreach (string key in cookie.Values.Keys)
        {
            if (key != "sig")
            {
                payload.Append(key + "=" + cookie.Values[key]);
            }
        }

        string sig = cookie.Values["sig"];

        if (sig == GetMD5Hash(payload.ToString()))
        {
            return cookie;
        }
    }

    return null;
}

public string GetMD5Hash(string input)
{
    MD5CryptoServiceProvider cryptoServiceProvider = new MD5CryptoServiceProvider();
    byte[] bytes = Encoding.UTF8.GetBytes(input);
    bytes = cryptoServiceProvider.ComputeHash(bytes);
    StringBuilder s = new StringBuilder();

    foreach (byte b in bytes)
    {
        s.Append(b.ToString("x2").ToLower());
    }

    return s.ToString();
}

The one part I'm not sure about is parse_str(trim($_COOKIE['fbs_' . $app_id], '\\"'), $args);. From what I can tell its creating an array out of the trimmed cookie value. Can anyone provide some assistance?

+2  A: 

If I'm reading it correctly:

trim($_COOKIE['fbs_' . $app_id], '\\"')

Will trim \ and " from the beginning and end of the value stored in the cookie named fbs_FACEBOOK_APP_ID (The double back-slashes escape the back-slash in a single quoted string. And trim can be told what characters to trim from the string.)

Meanwhile, parse_str then parses that as if it were a query string, into an associative array. So, I'd assume that the value of that cookie should look like a query string.

Hope this helps.

George Marian
I'll give that a try, thanks.
jrummell
This helped fix one of my issues, thanks!
jrummell
+1  A: 

There were a few issues in my original C# version.

  • I forgot to include the FacebookSecret as salt in the MD5 hash.
  • The cookie.Value needs to be trimmed and parsed as a query string as George Marian and Alex JL explained.
  • The parsed cookie values need to be UrlDecoded. I guess ASP.NET UrlEncodes them when creating the Cookie object.
  • The default encoding should be used to create the hash, not UTF8.

Here's the working solution:

private HttpCookie GetCookie()
{
    // based on the php example at http://developers.facebook.com/docs/guides/canvas/#canvas
    HttpCookie cookie = Request.Cookies["fbs_" + FacebookClientId];
    if (cookie != null)
    {
        var pairs = from pair in cookie.Value.Trim('"', '\\').Split('&')
                    let indexOfEquals = pair.IndexOf('=')
                    orderby pair
                    select new
                               {
                                   Key = pair.Substring(0, indexOfEquals).Trim(),
                                   Value = pair.Substring(indexOfEquals + 1).Trim()
                               };

        IDictionary<string, string> cookieValues =
            pairs.ToDictionary(pair => pair.Key, pair => Server.UrlDecode(pair.Value));

        StringBuilder payload = new StringBuilder();
        foreach (KeyValuePair<string, string> pair in cookieValues)
        {
            Response.Write(pair.Key + ": " + pair.Value + "<br/>\n");

            if (pair.Key != "sig")
            {
                payload.Append(pair.Key + "=" + pair.Value);
            }
        }

        string sig = cookieValues["sig"];
        string hash = GetMd5Hash(payload + FacebookSecret);

        if (sig == hash)
        {
            return cookie;
        }
    }

    return null;
}

private static string GetMd5Hash(string input)
{
    MD5CryptoServiceProvider cryptoServiceProvider = new MD5CryptoServiceProvider();
    byte[] bytes = Encoding.Default.GetBytes(input);
    byte[] hash = cryptoServiceProvider.ComputeHash(bytes);

    StringBuilder s = new StringBuilder();
    foreach (byte b in hash)
    {
        s.Append(b.ToString("x2"));
    }

    return s.ToString();
}
jrummell