Search code examples
c#facebookfacebook-graph-api

Trying to use Access Token generated in the Graph API Explorer yields incorrect permissions


I created a C# app that makes GET/POST requests to the Facebook Graph API for automating the uploading of images to a pages timeline.

The app is a console application so user logins are a no-go in terms of logging into facebook to retrieve a access token, instead i used the Graph API to generate the token and my application extends it. This worked fine for approx 12 hours, until now all the permissions are wrong.

When i generate the token in the Graph Explorer, i select the "manage_pages", publish_pages" & "publish_actions" permissions. If use the Graph explorer to do a GET request to https://graph.facebook.com/v2.3/debug_token with the Key I get the three permissions i selected, plus a "public_profile" permission and all seems well.

When i run this in my app and the C# code runs a GET request to https://graph.facebook.com/v2.3/debug_token using the same access token i did for query, however using the applications own access key that it generates at run time to authenticate against, the only permission i get is "public_profile", even though a GET request to https://graph.facebook.com/v2.3/" + PageID + "/?fields=can_post returns that the access key CAN be used for posting, yet when i attempt to post i am given the error that i am missing the 3 key permissions i selected in the first place.

What is happening?

For reference, here are the functions i am using;

//THIS IS THE FUNCTION USED TO CHECK IF THE TOKEN IS VALID
static bool checkValidToken(string inputToken)
{
    string url = "https://graph.facebook.com/v2.3/debug_token?input_token=" + inputToken + "&access_token=" + getAppAccessToken();
    WebRequest wrGETURL = WebRequest.Create(url);
    Stream objStream = wrGETURL.GetResponse().GetResponseStream();
    StreamReader objReader = new StreamReader(objStream);

    string jsonResponse = objReader.ReadToEnd();
    JObject resonse = (JObject)JObject.Parse(jsonResponse).GetValue("data");
    return (bool)resonse.GetValue("is_valid");
}
//THIS IS USED TO CHECK IF THE TOKEN CAN BE USED TO POST TO PAGE
static bool checkCanPost(string inputToken)
{
    WebRequest wrGETURL = WebRequest.Create("https://graph.facebook.com/v2.3/" + PageID + "/?fields=can_post&access_token=" + inputToken);
    Stream objStream = wrGETURL.GetResponse().GetResponseStream();
    StreamReader objReader = new StreamReader(objStream);

    string jsonResponse = objReader.ReadToEnd();
    JObject resonse = (JObject)JObject.Parse(jsonResponse);
    return (bool)resonse.GetValue("can_post");
}
//USED TO CALCULATE THE EXPIRY DATE ON THE TOKEN
static DateTime getExpiryDate(string inputToken)
{

    WebRequest wrGETURL = WebRequest.Create("https://graph.facebook.com/v2.3/debug_token?input_token=" + inputToken + "&access_token=" + getAppAccessToken());
    Stream objStream = wrGETURL.GetResponse().GetResponseStream();
    StreamReader objReader = new StreamReader(objStream);

    string jsonResponse = objReader.ReadToEnd();
    JObject resonse = (JObject)JObject.Parse(jsonResponse).GetValue("data");
    DateTime expires = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Local).AddSeconds((int)resonse.GetValue("expires_at"));
    if ((int)resonse.GetValue("expires_at") == 0)
    {
        expires = DateTime.UtcNow.AddMonths(2);
    }
    return expires;
}
//USED TO GENERATE THE APP ACCESS TOKEN
static string getAppAccessToken()
{
    WebRequest wrGETURL = WebRequest.Create("https://graph.facebook.com/v2.3/oauth/access_token?client_id=" + AppID + "&client_secret=" + AppSecret + "&grant_type=client_credentials");

    Stream objStream = wrGETURL.GetResponse().GetResponseStream();
    StreamReader objReader = new StreamReader(objStream);


    string jsonResponse = objReader.ReadToEnd();
    JObject resonse = JObject.Parse(jsonResponse);

    return (string)resonse.GetValue("access_token");
}
//USED TO EXTEND AN EXISTING TOKEN
static string extendToken(string inputToken)
{
    WebRequest wrGETURL = WebRequest.Create("https://graph.facebook.com/v2.3/oauth/access_token?grant_type=fb_exchange_token&client_id=" + AppID + "&client_secret=" + AppSecret + "&fb_exchange_token=" + inputToken);

    Stream objStream = wrGETURL.GetResponse().GetResponseStream();
    StreamReader objReader = new StreamReader(objStream);


    string jsonResponse = objReader.ReadToEnd();
    JObject resonse = JObject.Parse(jsonResponse);
    Console.WriteLine(jsonResponse);
    return (string)resonse.GetValue("access_token");
}
//USED TO GENERATE A TOKEN FOR POSTING AS THE PAGE, RATHER THAN TO THE PAGE
static string getPageToken(string inputToken)
{
    WebRequest wrGETURL = WebRequest.Create("https://graph.facebook.com/v2.3/"+PageID+"?fields=access_token&access_token="+inputToken);

    Stream objStream = wrGETURL.GetResponse().GetResponseStream();
    StreamReader objReader = new StreamReader(objStream);


    string jsonResponse = objReader.ReadToEnd();
    JObject resonse = JObject.Parse(jsonResponse);

    return (string)resonse.GetValue("access_token");
}
//FUNCTION FOR GETTING ALL PERMISSIONS FOR A TOKEN
static List<string> getTokenPermissions(string inputToken)
{
    string url = "https://graph.facebook.com/v2.3/debug_token?input_token=" + inputToken + "&access_token=" + getAppAccessToken();
    WebRequest wrGETURL = WebRequest.Create(url);
    Stream objStream = wrGETURL.GetResponse().GetResponseStream();
    StreamReader objReader = new StreamReader(objStream);

    string jsonResponse = objReader.ReadToEnd();
    JObject resonse = (JObject)JObject.Parse(jsonResponse).GetValue("data");
    JToken scopes = resonse.GetValue("scopes");

    List<string> returnList = new List<string>();
    foreach (JToken tokrow in scopes)
    {
        returnList.Add((string)tokrow);
    }
    return returnList;
}
//DEBUG FUNCTION FOR OUTPUTTING ALL OF A TOKENS USER/PAGE PERMISSIONS
static void outputPermissions(string theToken)
{
    List<string> userPermissions = getTokenPermissions(theToken);
    List<string> appPermissions = getTokenPermissions(getPageToken(theToken));
    Console.WriteLine("USER:");
    foreach (string perm in userPermissions)
    {
        Console.WriteLine(perm);
    }
    Console.WriteLine("APP:");
    foreach (string perm in appPermissions)
    {
        Console.WriteLine(perm);
    }
    Console.Read();
}
// FUNCTION THAT ACTUALLY POSTS TO THE PAGE
static async Task PostHandler(Post toPost,string theToken)
{
    using (var client = new HttpClient())
    {
        var values = new Dictionary<string, string>
                {
                   { "url", toPost.ImageURL },
                   {"access_token",getPageToken(theToken)},
                   {"caption",toPost.Caption}
                };
        Console.WriteLine("Image we are uploading is " + toPost.ImageURL);
        var content = new FormUrlEncodedContent(values);

        var response = await client.PostAsync("https://graph.facebook.com/v2.3/" + PageID + "/photos", content);
        Console.WriteLine("Uploaded!");
        var responseString = await response.Content.ReadAsStringAsync();
        Console.WriteLine(responseString);
        Console.Read();
        JObject resonse = JObject.Parse(responseString);
    }
}

When the post runs it returns

error #200 Requires extended permission: (manage_pages and pubish_pages) or (manage_pages and publish_actions)

Solution

  • The error in question was actually a bug introduced by the Facebook team that unfortunately happened to coincide with the testing of my application, since posting the bug has been logged and corrected.

    More info can be found at developers.facebook.com/bugs/380833342117530