How to check for required permissions in a Facebook canvas app?

One of the basic things that a Facebook canvas app needs to do is check for permissions and require a user to authorize them to access a page. What's a good way to do this?

Answers


Here's a system I developed for my own apps. You can download the code from:

http://developsocialapps.com/permissions-and-basic-graph-api-calls/

This is the library that does the heavy lifting. The main method to check permissions is requireAuthorization. This will first use initOauthUserFromSignedRequest to see if there is a signed_request to see if the use is authenticated and set the userid and token. It will then use hasAllPermissions to check me/permissions from the graph API to see if the user has all the required permissions. Because I don't want to hit this API on every page load, I store the permissions in a cookie. If the user doesn't have the permissions or is not authorized, it directs the user to the authorization dialog.

class FacebookApp {               

    public $appId;
    private $appSecret;
    private $nameSpace;
    public $userId;
    public $token;
    public $tokenExpires;

    // get your own from http://www.w3.org/P3P/
    public $p3p = 'P3P:CP="IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT"'; 

    /*  construct object 
        appid, secret, and namespace from app settings  */
    public function __construct($id, $secret, $namespace) {
        $this->appId = $id;
        $this->appSecret = $secret;
        $this->nameSpace = $namespace;
    }

    /*  return json data from a graph api object or false   */
    function getGraphObject($object) {
        $return = false;
        $url = $this->getGraphUrl($object);
        $response = $this->makeCurlRequest($url);
        if ($repsonse !== false) {
            $return = json_decode($response,true);
            if (isset($return['error'])) {
                $return = false;
                // the request return an error, you can debug here
            }
        }
        return $return;
    }

    /*  constructs graphs url   */
    public function getGraphUrl($object,$limit=false) {
        $url = "https://graph.facebook.com/".$object;
        if (strpos($url,"?") === false) $url .= "?";
        else $url .= "&";
        $url .= "access_token=".$this->token;
        if ($limit !== false) $url .= "&limit=".$limit;
        return $url;
    }

    /*  uses curl to get a url  */
    public function makeCurlRequest($url) { 
        $return = false;
        try {
            $ch = curl_init(); 
            curl_setopt($ch, CURLOPT_URL, $url);
            curl_setopt($ch, CURLOPT_HEADER, false); 
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 
            $response = curl_exec($ch); 
            $responseInfo = curl_getinfo($ch); 
            curl_close($ch); 
            if ($responseInfo['http_code']==200) { 
                $return = $response; 
            } 
        } catch (Exception $e) {
            $return = false; 
        }
        return $return;
    } 

    /*  sets userid and token from signed request, return true or false if authorized   */
    public function initOauthUserFromSignedRequest() {
        $authorized = false;
        if (isset($_REQUEST['signed_request'])) {
            $data = $this->parseSignedRequest($_REQUEST['signed_request']);
            if ($data !== false) {
                if (isset($data['user_id']) && isset($data['oauth_token'])) {
                    $this->userId = $data['user_id'];
                    $this->token = $data['oauth_token'];
                    $this->tokenExpires = $data['expires'];
                    $authorized = true;
                }
            }
        }
        return $authorized;
    }

    /*  require user to authorize and have permissions for page
        redirect_uri = url to return after user has authorized like redirect.php
        success_uri = url to redirect to on successful authorization like mypage.php
        scope = comma separted list of permissions  */
    function requireAuthorization($redirect_uri,$success_uri=false,$scope=false) {
        if ($success_uri === false) {
            // if no success_uri use current page, all files for app must be in same directory
            $success_uri = substr($_SERVER['REQUEST_URI'],strrpos($_SERVER['REQUEST_URI'],"/")+1); 
        }
        $this->setCookie ("success_uri",$success_uri,0); // we will use this on the redirect_uri page
        $requireauth = true;
        if ($this->initOauthUserFromSignedRequest()) { // user has authorized
            if (($scope === false) || ($this->hasAllPermissions($scope))) { // now check for perms
                $requireauth = false;
            }
        }
        if ($requireauth) { // user is either not authorized or doesn't have permissions
            $url = $this->getAuthUrl($this->getCanvasUrl($redirect_uri),$scope);
            echo "<html>\n<body>\n<script>\ntop.location.href='".$url."';\n</script></body></html>";
            exit();
        }
    }

    /*  checks to see if has permissions, scope is comma separated list */
    public function hasAllPermissions($scope) {
        $return = false;
        $cookiename = "permissions_".$this->appId."_".$this->userId;
        $requiredpermissions = explode(",",$scope);
        // first check cookie
        if (isset($_COOKIE[$cookiename])) {
            $return = true;
            $permissions = json_decode($_COOKIE[$cookiename],true);
            foreach ($requiredpermissions as $perm) {
                if ($permissions['data'][0][$perm] != 1) {
                    $return = false;
                    break;
                }
            }
        }
        // if didn't have all in cookie, then see if it is in graph 
        if ($return == false) {
            $permissions = $this->getGraphObject("me/permissions");
            if ($permissions !== false) {
                $this->setCookie($cookiename,json_encode($permissions),0);
                $return = true;
                foreach ($requiredpermissions as $perm) {
                    if ($permissions['data'][0][$perm] != 1) {
                        $return = false;
                        break;
                    }
                }   
            }
        }
        return $return;
    }

    /*  sets a cookie with p3p headers  */
    public function setCookie($name,$value,$expires) {
        if ($this->p3p != '') {
            header($this->p3p);
            $this->p3p = '';
        }
        setcookie ($name,$value,$expires,"/"); 
    }

    /*  returns url for oauth authorization
        redirect_uri = url to return after user has authorized
        scope = comma separted list of permissions  */
    public function getAuthUrl($redirect_uri,$scope=false) {
        $url = "https://www.facebook.com/dialog/oauth/?client_id=".$this->appId."&redirect_uri=".rawurlencode($redirect_uri);
        if ($scope !== false) $url .= "&scope=".rawurlencode($scope);
        return $url;
    }


    /* returns url to app canvas page, $page like mypage.php?foo=bar    */
    public function getCanvasUrl($page) {
        if ($_SERVER['HTTPS'] == "on") $protocol = "https";
        else $protocol = "http";
        return $protocol."://apps.facebook.com/".$this->nameSpace."/".$page;
    }

    /*  parses signed_request parameter and returns data object, returns false if sigs don't match  */
    public function parseSignedRequest($signed_request) {
        list($encoded_sig, $payload) = explode('.', $signed_request, 2); 
        $data = json_decode(base64_decode(strtr($payload, '-_', '+/')), true);
        $sig = base64_decode(strtr($encoded_sig, '-_', '+/'));
        $expected_sig = hash_hmac('sha256', $payload, $this->appSecret, true);
        if ($sig == $expected_sig) {
            return $data;
        } else {
            return false;
        }
    }
}

Use it on a page like this:

$facebookapp = new FacebookApp("id1234...","secret...","appnamespace");

$facebookapp->requireAuthorization("redirect.php","thispage.php","user_about_me,user_likes,more_permissions");

redirect.php is a page that will detect if the user has accepted or canceled the authorization. If accepted it will redirect back to the first page, if canceled, it will display a message.

<?php 
require_once("../lib/facebookapp.php");
require_once("config.php");

$facebookapp = new FacebookApp($GLOBALS['facebookAppId'],$GLOBALS['facebookAppSecret'],$GLOBALS['facebookNamespace']);

if ((!isset($_GET['error'])) && isset($_GET['code'])) {
    // user has auhtorized this app, go the the page from success_uri cookie
    if (isset($_COOKIE['success_uri'])) $url = $facebookapp->getCanvasUrl($_COOKIE['success_uri']);
    else $url = $facebookapp->getCanvasUrl('');
    echo "<html>\n<body>\n<script>\ntop.location.href='".$url."';\n</script></body></html>";
    exit();
}

?><html>
<body>
<h1>Permission Denied</h1>
<p>You have denied permissions to this app.</p>
<p><a href="<?php echo $facebookapp->getCanvasUrl(''); ?>" target="_top">Home</a></p>
</body>
</html>

Need Your Help

Vector of type int not replacing int when get is used

java arrays vector clone

I have a Vector&lt;int[][]&gt; and when I give it a clone of my int[][] array and than change something in my int[][] array and call int[][] = Vector.get(0) the array doesn't revert back to the val...

What is the purpose of tag “guzzle.client” in guzzle client bundle in symfony2 ?

symfony2 bundle guzzle

I am using the guzzle client bundle in a symfony2 project and I checked the configuration provided here :