17

I'm able to successfully validate the signed request for a Facebook canvas app using the example here, but I'm unable to decode the payload. The Facebook documentation states that the 2nd parameter in signed_request is a base64url encoded JSON object. In PHP the payload is decoded using json_decode:

$data = json_decode(base64_url_decode($payload), true);

What is the equivalent in C#?

Community
  • 1
  • 1
user330468
  • 381
  • 2
  • 4
  • 6

6 Answers6

24

The following should help you out..

(Note: The JObject reference is from JSON.NET available via http://james.newtonking.com/projects/json-net.aspx and http://json.codeplex.com/)

Namespaces used:

using System;
using System.Collections.Generic;
using System.Text;
using Newtonsoft.Json.Linq; // JSON.NET project 

Code:

public Dictionary<string,string> DecodePayload(string payload)
    {
        var encoding = new UTF8Encoding();
        var decodedJson = payload.Replace("=", string.Empty).Replace('-', '+').Replace('_', '/');
        var base64JsonArray = Convert.FromBase64String(decodedJson.PadRight(decodedJson.Length + (4 - decodedJson.Length % 4) % 4, '='));
        var json = encoding.GetString(base64JsonArray);
        var jObject = JObject.Parse(json);

        var parameters = new Dictionary<string, string>();
        parameters.Add("user_id", (string)jObject["user_id"] ?? "");
        parameters.Add("oauth_token", (string)jObject["oauth_token"] ?? "");
        var expires = ((long?) jObject["expires"] ?? 0);
        parameters.Add("expires", expires > 0 ? expires.ToString() : "") ;
        parameters.Add("profile_id", (string)jObject["profile_id"] ?? "");

        return parameters;
    }

It is what I'm using in FaceSharp.. hope it helps

John K
  • 28,441
  • 31
  • 139
  • 229
Patrick Gidich
  • 1,127
  • 1
  • 8
  • 12
  • 1
    +1 Very helpful. I updated the answer with namespaces and reference to the JSON library used (in case the FaceSharp software isn't installed - it likely provides the JSON.NET library as part of itself.) – John K Jan 16 '11 at 00:55
  • This code does not work at the moment. See Bosshog's answer below for a working version. – rboarman May 25 '12 at 00:19
10

Same code but without Json.NET dependency:

public IDictionary<string, object> DecodePayload(string payload)
{
    string base64 = payload.PadRight(payload.Length + (4 - payload.Length % 4) % 4, '=')
        .Replace('-', '+').Replace('_', '/');
    string json = Encoding.UTF8.GetString(Convert.FromBase64String(base64));
    return (IDictionary<string, object>)new JavaScriptSerializer().DeserializeObject(json);
}

You can use it like this:

public ActionResult Index()
{
    var dict = DecodePayload(Request["signed_request"].Split('.')[1]);
    return Content("Access Token: " + (string)dict["oauth_token"]);
}
Pavel Chuchuva
  • 22,633
  • 10
  • 99
  • 115
  • dict["oauth_token"] - is no longer a key: there is 'code' one, is this what I should use? – Alexa Adrian Jan 23 '12 at 12:45
  • Thanks @Pavel, your code worked for decoding the payload. – Prakash May 12 '18 at 19:00
  • @Pavel - Now, I am working on the "Workplace of Facebook". In workplace also, we will have the App. I am able to get the signed_request when the Workplace app is uninstalled. I can decode the payload but I am not able to validate the signature. – Prakash May 15 '18 at 02:57
9

Sorry, bit of a StackOverflow noob, but for anyone trying to use JohnK's method to decode, it works brilliantly, just a couple of implementation tips for anyone like me and the others with the base64 encoding issue....

The Json reference is also available from nuGet

Install-Package Newtonsoft.Json

http://developers.facebook.com/docs/guides/canvas/#auth explains the ["signed_request"] element in more detail, but put simply, when Facebook posts back (in my case after a user registration request), you can get the data from the post, but the string is in TWO PARTS, separated by a '.' - As such, trying to decode ["signed_request"] will fail as '.' isn't a Base64 char. The first part is the signature to allow you to validate that the post came from Facebook (only us and them know the sig to decode) and the second is the payload.

So, I got this to work with the following code (in a MVC controller), source is a Facebook registration button....

<fb:registration fields="name,email"  redirect-uri="http://dev.devurlgoeshere.co.uk/Account/Register" width="530">
</fb:registration>

and then the Controller code responds to the registration request

   [HttpPost]
    public ActionResult Register(object postData )
    {
        string requestData = Request.Form["signed_request"];
        string[] splitPayload = requestData.Split('.');
        string sig = splitPayload[0];
        string payload = splitPayload[1];
        var decodedObj = DecodePayload(payload);
        // get the items from the decodedObject
        string userFacebookID = decodedObj["user_id"];
        // now do what you want with their FacebookID
        return View();
    }

hope this helps someone, and sorry if this should have been edit/feedback or whatever...

dave heywood
  • 791
  • 8
  • 13
2

Check out the Facebook .Net SDK on Codeplex http://facebooksdk.codeplex.com. It will handle all the 'dirty work' for you. For example, I could call the following code either from a controller action or on Page_Load.

FacebookApp app = new FacebookApp();
string accessToken = app.Session.AccessToken;
long userId = app.UserId;

Thats it. You don't really need to worry about how facebook is returning the data to you or decoding it. The SDK handles all that for you.

Nate Totten
  • 8,904
  • 1
  • 35
  • 41
  • FacebookApp app = new FacebookApp(); is now FacebookApp app = new FacebookApp(); I don't see anyway to get the userId without calling app.Get("me").. – AyKarsi Sep 07 '11 at 11:34
  • You didn't explain how the SDK decodes the signed_request :( I am setting up a Deauthorize() action to handle signed_request which is a POST by Facebook to my action with the parameter "signed_request" as a string. I want to decode it to get the user_id – Korayem Nov 25 '11 at 12:40
2

i have change the DecodePayload by this and it work fine for me:

    public Dictionary<string, string> DecodePayload(string payload) 
    {
        //Remove the bad part of signed_request
        //Begin
        string[] sB64String = payload.Split('.');
        payload = payload.Replace((sB64String[0] + "."), string.Empty);
        //End
        var encoding = new UTF8Encoding(); 
        var decodedJson = payload.Replace("=", string.Empty).Replace('-', '+').Replace('_', '/'); 
        var base64JsonArray = Convert.FromBase64String(decodedJson.PadRight(decodedJson.Length + (4 - decodedJson.Length % 4) % 4, '=')); 
        var json = encoding.GetString(base64JsonArray); 
        var jObject = JObject.Parse(json); 
        var parameters = new Dictionary<string, string>(); 
        parameters.Add("user_id", (string)jObject["user_id"] ?? ""); 
        parameters.Add("oauth_token", (string)jObject["oauth_token"] ?? ""); 
        var expires = ((long?)jObject["expires"] ?? 0); 
        parameters.Add("expires", expires > 0 ? expires.ToString() : ""); 
        parameters.Add("profile_id", (string)jObject["profile_id"] ?? ""); 
        return parameters; 
    }
Bosshog
  • 31
  • 1
  • 2
    Why not to do payload = sB64String[1]; instead of payload = payload.Replace((sB64String[0] + "."), string.Empty); – Pit Digger Jun 03 '11 at 19:35
2

Here's how to do it using Facebook SDK

var parsedSignedRequest = FacebookSignedRequest.Parse(FacebookApplication.Current, signed_request);
Korayem
  • 12,108
  • 5
  • 69
  • 56