How to decode OAuth 2.0 for Canvas signed_request in C#?

18,977

Solution 1

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

Solution 2

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"]);
}

Solution 3

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...

Solution 4

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.

Solution 5

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; 
    }
Share:
18,977

Related videos on Youtube

user330468
Author by

user330468

Updated on April 30, 2022

Comments

  • user330468
    user330468 about 2 years

    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#?

    • Romhein
      Romhein over 13 years
      Have you tried to use the Facebook C# SDK: github.com/facebook/csharp-sdk?
    • Nate Totten
      Nate Totten over 13 years
      The Facebook C# SDK on github does not handle anything with authentication, it only does api requests.
  • John K
    John K over 13 years
    +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.)
  • Pit Digger
    Pit Digger almost 13 years
    Why not to do payload = sB64String[1]; instead of payload = payload.Replace((sB64String[0] + "."), string.Empty);
  • AyKarsi
    AyKarsi over 12 years
    FacebookApp app = new FacebookApp(); is now FacebookApp app = new FacebookApp(); I don't see anyway to get the userId without calling app.Get("me")..
  • IDIR Samir
    IDIR Samir over 12 years
    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
  • Alexa Adrian
    Alexa Adrian over 12 years
    dict["oauth_token"] - is no longer a key: there is 'code' one, is this what I should use?
  • rboarman
    rboarman almost 12 years
    This code does not work at the moment. See Bosshog's answer below for a working version.
  • Prakash
    Prakash almost 6 years
    Thanks @Pavel, your code worked for decoding the payload.
  • Prakash
    Prakash almost 6 years
    @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.