Facebook Requires extended permission: publish_actions

mykisscool picture mykisscool · Aug 23, 2012 · Viewed 18.1k times · Source

I'm in the process of developing a Contest and Promotion-related Facebook app and my intent is to to create a tab on my company's page that will provide access to the app.

Once their, users can nominate local companies for awards. Later on; once the nominations are in, users can vote for a winner.

I've integrated Open Graph into my app so that I can take advantage of Object Types (Organization), Action Types (Nominate, Vote For), and Aggregations (Nominations). My main objective is to then transfer these actions onto the user's timeline.

I've used the recipebox example as my base. I've provided my code to demonstrate authentication and the post action required to submit an action type/object type combination.

<html xmlns="http://www.w3.org/1999/xhtml" dir="ltr" lang="en-US"> 
<head prefix="og: http://ogp.me/ns# og_<<app namespace>>: http://ogp.me/ns/apps/<<app namespace>>#"> 
    <meta property="fb:app_id" content="<<app id>>" /> 
    <meta property="og:type" content="<<app namespace>>:organization" /> 
    <meta property="og:title" content="Client 1" /> 
    <meta property="og:image" content="<<client image path>>" /> 
    <meta property="og:description" content="Client 1 description here ... " /> 
    <meta property="og:url" content="<<custom client URL>>">
    <script src="http://connect.facebook.net/en_US/all.js"></script>
    <script type="text/javascript">
        // Load the SDK Asynchronously
        (function(d){
            var js, id = 'facebook-jssdk', ref = d.getElementsByTagName('script')[0];
            if (d.getElementById(id)) {return;}
            js = d.createElement('script'); js.id = id; js.async = true;
            js.src = "//connect.facebook.net/en_US/all.js";
            ref.parentNode.insertBefore(js, ref);
        }(document));

        // Init the SDK upon load
        window.fbAsyncInit = function() {
            FB.init({
                appId      : '<<app id>>', // App ID
                status     : true, // check login status
                cookie     : true, // enable cookies to allow the server to access the session
                xfbml      : true  // parse XFBML
            });

            // listen for and handle auth.statusChange events
            FB.Event.subscribe('auth.statusChange', function(response) {
                if (response.authResponse) {
                    // user has auth'd your app and is logged into Facebook
                    FB.api('/me', function(me){
                        if (me.name) {
                            document.getElementById('auth-displayname').innerHTML = me.name;
                        }
                    })
                    document.getElementById('auth-loggedout').style.display = 'none';
                    document.getElementById('auth-loggedin').style.display = 'block';
                } else {
                    // user has not auth'd your app, or is not logged into Facebook
                    document.getElementById('auth-loggedout').style.display = 'block';
                    document.getElementById('auth-loggedin').style.display = 'none';
                }
            });

            // respond to clicks on the login and logout links
            document.getElementById('auth-loginlink').addEventListener('click', function(){
                FB.login();
            });
            document.getElementById('auth-logoutlink').addEventListener('click', function(){
                FB.logout();
            });
        }       

        function nominate () {
            FB.api('/me/<<app namespace>>:Nominate&organization=<<custom client URL>>', 'post',  function(response) {

                if (! response || response.error) {
                    alert('Error occured');
                    console.log(response);
                } else {
                    alert('Post was successful! Action ID: ' + response.id);
                }
            });
        }
    </script>
</head> 
<body> 
    <div id="fb-root"></div>
    <div id="auth-status">
        <div id="auth-loggedout">
            <a href="#" id="auth-loginlink">Login</a>
        </div>
        <div id="auth-loggedin" style="display:none">
            Hi, <span id="auth-displayname"></span>  
            (<a href="#" id="auth-logoutlink">logout</a>)
        </div>
    </div>
    <h2>Client 1</h2> 
    <form>
        <input type="button" value="Nominate" onclick="nominate()" />
    </form> 
    <fb:activity actions="<<app namespace>>:nominate"></fb:activity>
</body> 
</html>

A test user is encountering the following error:

Requires extended permission: publish_actions

And I am encountering the following error (I am an Admin for this app):

This method must be called with an app access_token 

The first error is troubling to me. I cannot select publish_actions from Apps | <My App> | Permissions | Extended Permissions. Also, remedies I've encounted suggest I re-categorize my app to Games (this does not work) and to complete my Aggregations (I did; still does not work).

  1. How can I overcome this error?
  2. What can I do to fix my This method must be called with an app access_token error?

Thanks in advance,

Mike

EDIT

I believe the access_token error I was encountering was due to the fact that I was testing/interacting with the app directly on the Page Tab URL; not through Facebook.

I am no longer receiving the Requires extended permission: publish_actions error; however, my testers and developers are. I know that I am encountering this error because the publish_actions permission is not requested at the initial facebook.com login prompt.

If my Developers/Testers logout of Facebook, then log back in with my prompt:

FB.login(function (response) { }, { scope:'publish_actions'});

Then this permission and app is integrated into their Facebook session.

My final question is- is there a defacto, preferred method to request this permission without logging in/out of Facebook?

Answer

mykisscool picture mykisscool · Sep 7, 2012

I have fixed both of my errors. Thank you to those who provided me with feedback and put me on the right path.

These errors have been resolved:

1. Requires extended permission: publish_actions

2. This method must be called with an app access_token 

For starters, CBroe is right. Since I require extended permissions (publish_actions) I need to specify this is my options object after the callback function in FB.login() like so:

FB.login(function (response) {
    if (response.authResponse) { // The user has authenticated
        authenticatedSoDoSomething();
    } 
    else { // The user has not authenticated
        notAuthenticatedSoDoSomethingElse(); 
    }
}, { scope:'publish_actions' });

Lastly, I had no success using the JavaScript SDK to publish an action to a user's timeline via FB.api(). This is the code I was using:

FB.api('/me/<<app namespace>>:<<my action>>&<<my object>>=<<a URL>>', 'post',
    function(response) {
        if (! response || response.error) {
            alert('Error occured');
        } else {
            alert('Post was successful! Action ID: ' + response.id);
        }
})

I neglected to include the app access token. The app access token can be constructed by concatenating the app id, followed by a pipe, followed by the app secret.

I used the PHP SDK to fulfill this request as I wanted to keep my api secret a secret. Here is [most] of the code I used:

require_once('facebook-php-sdk/src/facebook.php');

$facebook = new Facebook(
    array(
        'appId' => '<<my app id>>', 
        'secret' => '<<my app secret>>'));

$user = $facebook->getUser();

if ($user) {
    try {
        $user_profile = $facebook->api('/me');
    } 
    catch (FacebookApiException $e) {
        error_log($e);
        $user = null;
        echo json_encode(array('success' => false));
    }
}

try {
    $facebook->api('/' . $user . '/<<app namespace>>:<<my action>>&<<my object>>=<<a URL>>', 'POST', array('access_token' => $facebook->getAppId() . '|' . $facebook->getApiSecret()));
}
catch (FacebookApiException $e) {
    echo var_dump($e->getResult());
}

Hope this helps!