1

I am using Facebook SDK for PHP. Months ago, when Facebook requested, I have implemented some helper classes for the new Facebook SDK. I am overall satisfied with the result. However, I have seen a strange exception in the exception logs and I do not really understand it. One of these helper classes is this:

namespace Facebook\FacebookAccessor;
use Facebook\FacebookSession;
use Facebook\FacebookRequest;
use User;
use App;

class FacebookActionPrivilege {

    public static $ACTION_LOGIN_KEY = "login";
    public static $ACTION_LOGOUT_KEY = "logout";
    public static $ACTION_POST_KEY = "post";
    public static $ACTION_FIND_FRIENDS_KEY = "find_friends";
    public static $ACTION_PICTURE_KEY = "picture";

    public static $PRIVILEGE_PUBLIC_PROFILE_KEY = "public_profile";
    public static $PRIVILEGE_FIND_FRIENDS_KEY = "user_friends";
    public static $PRIVILEGE_EMAIL_KEY = "email";
    public static $PRIVILEGE_USER_PHOTOS_KEY = "user_photos";
    public static $PRIVILEGE_PUBLISH_ACTIONS_KEY = "publish_actions";

    private static $initialized = false;
    private static $session;
    private static $isSessionValid = false;
    private static $distantPrivileges = null;

    private static $facebookActionPrivileges = array();

    public static function getSession() {
        return self::$session;
    }

    public static function getDistantPrivileges() {
        if (!self::$session) {
            return array();
        }
        if (self::$distantPrivileges === null) {
            $request = new FacebookRequest(
              FacebookActionPrivilege::getSession(),
              'GET',
              '/me/permissions'
            );
            self::$distantPrivileges = $request->execute()->getResponse()->data; //Line 44
        }
        return self::$distantPrivileges;
    }

    public static function init() {
        FacebookSession::setDefaultApplication(App::env()->get('facebook', 'appId'), App::env()->get('facebook', 'appSecret'));
        if (self::$initialized) {
            return;
        }
        //some custom logic
        if ((User::isLoggedIn()) && (User::current()->hasFacebookAccount())) {
            try {
                self::$session = new FacebookSession(User::current()->getFacebookAccessToken());
                self::$isSessionValid = self::$session->validate();
                if (self::$isSessionValid) {
                    if (!self::$session->getAccessToken()->isLongLived()) {
                        self::$session = self::$session->getLongLivedSession();
                    }
                    if (User::current()->getFacebookAccessToken() !== self::$session->getToken()) {
                        User::current()->setFacebookAccessToken(self::$session->getToken())->store();
                    }
                }
            } catch (\Facebook\FacebookSDKException $exception) {
                User::current()->disableFacebook();
                self::init();
            }
        } else {
            $helper = new \Facebook\FacebookRedirectLoginHelper(\BaseClass::$mainSiteName);
            self::$session = $helper->getSessionFromRedirect();            
            if (!!self::$session) {
                $request = new \Facebook\FacebookRequest( self::$session, 'GET', '/me' );
                $response = $request->execute();
                // get response
                $graphObject = $response->getGraphObject()->asArray();
                $user = \User::isLoggedIn() ? \User::current() : \User::getUserByFacebookID($graphObject["id"]);
                if (!self::$session->getAccessToken()->isLongLived()) {
                    self::$session = self::$session->getLongLivedSession();
                }
                $myToken = self::$session->getToken();
                if (!!$user) {
                    if (($user->getFacebookAccessToken() !== $myToken) || ($user->getFacebookId() !== $graphObject["id"])) {
                        $user->setFacebookAccessToken($myToken)->setFacebookId($graphObject["id"])->store();
                    }
                    if (!User::isLoggedIn()) {
                        $user->loginWithoutAuthentification();
                    }
                } else {
                    //create user
                }
            }
        }
        //login
        self::createFacebookActionPrivilege(self::$ACTION_LOGIN_KEY, self::$PRIVILEGE_PUBLIC_PROFILE_KEY, true);
        self::createFacebookActionPrivilege(self::$ACTION_LOGIN_KEY, self::$PRIVILEGE_EMAIL_KEY, false);

        //post
        self::createFacebookActionPrivilege(self::$ACTION_POST_KEY, self::$PRIVILEGE_PUBLISH_ACTIONS_KEY, true);

        //find friends
        self::createFacebookActionPrivilege(self::$ACTION_FIND_FRIENDS_KEY, self::$PRIVILEGE_FIND_FRIENDS_KEY, true);

        self::$initialized = true;
    }

    public static function getUsefulPrivileges($actionKey, $mandatory = false) {
        $privileges = array();
        foreach (self::$facebookActionPrivileges as $currentActionKey => $facebookActionPrivilege) {
            if ($currentActionKey === $actionKey) {
                foreach ($facebookActionPrivilege as $currentPrivilegeKey => $privilege) {
                    if ((!$mandatory) || ($privilege)) {
                        $privileges[] = array("key" => $currentPrivilegeKey, "mandatory" => $privilege);
                    } 
                }
            }
        }
        return $privileges;
    }

    public static function getPrivilegedURL($actionKey, $toLogin = false) {
        FacebookActionPrivilege::init();
        $distantPrivileges = FacebookActionPrivilege::getDistantPrivileges(); //Line 147
        if ((!is_array($distantPrivileges)) && (!is_object($distantPrivileges))) {
            $distantPrivileges = array();
        }
        $privilegeDataElements = array();
        $privileges = FacebookActionPrivilege::getUsefulPrivileges($actionKey);
        if ($toLogin) {
            $privileges = array_merge($privileges, FacebookActionPrivilege::getUsefulPrivileges(self::$ACTION_LOGIN_KEY));
        }
        foreach ($privileges as $privilege) {
            $found = false;
            foreach ($distantPrivileges as $distantPrivilege) {
                if ((!$found) && ($privilege["key"] === $distantPrivilege->permission)) {
                    $found = true;
                    $privilegeDataElements[$privilege["key"]] = array("mandatory" => $privilege["mandatory"], "granted" => ((isset($distantPrivilege->status)) && ($distantPrivilege->status === "granted")));
                }
            }
            if (!$found) {
                $privilegeDataElements[$privilege["key"]] = array("mandatory" => $privilege["mandatory"], "granted" => false);
            }
        }

        $privilegesToBeAsked = array();
        foreach ($privilegeDataElements as $key => $privilegeDataElement) {
            if ((!isset($privilegeDataElement["granted"])) || (!$privilegeDataElement["granted"])) {
                $privilegesToBeAsked[] = $key;
            }
        }
        $isPossible = true;

        foreach ($privilegesToBeAsked as $privilegeToBeAsked) {
            $isPossible = $isPossible && $privilegeDataElements[$privilegeToBeAsked]["granted"];
        }
        $url = "";
        if (count($privilegesToBeAsked) > 0) {
            $helper = new \Facebook\FacebookRedirectLoginHelper(\BaseClass::$mainSiteName);
            $url .= $helper->getLoginUrl($privilegesToBeAsked);
        }

        return array("possible" => $isPossible, "url" => $url);
    }

    private static function createFacebookActionPrivilege($actionKey, $privilegeKey, $mandatory) {
        if (!isset(self::$facebookActionPrivileges[$actionKey])) {
            self::$facebookActionPrivileges[$actionKey] = array();
        }
        self::$facebookActionPrivileges[$actionKey][$privilegeKey] = $mandatory;
    }

}

A user, who has a Facebook account tried to find his or her Facebook friends in the application, which triggered the following code:

if(User::current()->hasFacebookAccount()){
    $permissions = Facebook\FacebookAccessor\FacebookActionPrivilege::getPrivilegedURL(Facebook\FacebookAccessor\FacebookActionPrivilege::$ACTION_FIND_FRIENDS_KEY);
    //...
}

However, the following exception was thrown when $permissions is initialized:

Exception 'Facebook\FacebookAuthorizationException' with message '(#412) User has not installed the application' in /www/myproject-prod/master/lib/Facebook/FacebookRequestException.php:134

#0 Facebook\FacebookRequestException::create("{"error":{"message":"(#412) User has not installed",object,integer(400))/www/myproject-prod/master/lib/Facebook/FacebookRequest.php:268
#1 Facebook\FacebookRequest::execute()/www/myproject-prod/master/lib/Facebook/FacebookAccessor/FacebookActionPrivilege.php:44
#2 Facebook\FacebookAccessor\FacebookActionPrivilege::getDistantPrivileges()/www/myproject-prod/master/lib/Facebook/FacebookAccessor/FacebookActionPrivilege.php:147
#3 Facebook\FacebookAccessor\FacebookActionPrivilege::getPrivilegedURL("find_friends")/www/myproject-prod/master/mobile/view/account/find_people.php:142
#4 include_once("/www/myproject-prod/master/mobile/view/account/find_")/www/myproject-prod/master/mobile/user.php:33

The exception is pretty unique, as many users are using the feature without any problems, but a given user's attempt to find Facebook friends yielded this problem. Since this is PHP Facebook SDK, I really do not understand what application should be installed. As far as I know, the user has to allow the Facebook application and grant the necessary privileges. However, the exception message states that an application was not installed. I have absolutely no idea why this error has been thrown, therefore I have no idea how to reproduce it. Any ideas about what might be the problem? Thanks.

Lajos Arpad
  • 64,414
  • 37
  • 100
  • 175

1 Answers1

1

me/friends is no longer a simple list of user's friends and has a much more stringent checks to ensure that only friends who are also using this app show up. When we say app, it can be a web app, a web page that uses FB login or a mobile app.

Some helpful links - https://stackoverflow.com/a/23417628/4263244 https://developers.facebook.com/docs/apps/upgrading#upgrading_v2_0_user_ids

Community
  • 1
  • 1
  • Thank you, Nithya, I will check your answer and get back to you later at all channels – Lajos Arpad Sep 08 '15 at 08:31
  • I am aware of app-level Facebook user ids. I am aware of the fact that find_friends permission has to be requested. I know that the active user has to grant the find_friends permission and if he has a valid facebook user access token, then he will find his or her friends if they have allowed that. I have pasted my code in my original question. However, I have got the error I have described (once from millions of occasions where the error did not occur) and I still do not understand what should have been installed. – Lajos Arpad Sep 08 '15 at 21:55