Search code examples
androidfacebookfacebook-android-sdkfacebook-oauth

Why is Facebook OAuth Dialog using outdated permissions list for my app?


I've removed 20+ permissions from Facebook for our app, yet for the life of me I cannot get Facebook OAuth Dialog requests to reflect the new reduced permissions list. It continues to ask for more permissions than I'm asking for, regardless of what I pass in.

I've tried with my accounts that were previously linked on facebook with our app, and I've tried with brand new test accounts (created through the facebook developer site, without pre-authorizing them). I've tried uninstalling Facebook app and our app on my device, I've tried several different devices.

I'm attempting to authorize the user using the Facebook Android SDK (pre-3.0), but I've also tried bypassing it and using our server-side auth flow, and it shows the old list in either case.

I've searched our entire code base, client and server, for someplace permissions might be hiding, but they are nowhere. I've turned on debugging and print statements in the Facebook SDK, and can confirm it is in fact receiving my updated list of permissions to the .authorize() method, and it is attempting to load the mobile url in an FbDialog (when I uninstall the facebook app) using the correct permissions list. However, the permissions it displays include all the ones I've since removed.

I've even tried adding the permissions to our previously blank list of permissions in the Facebook Developer site for the App Center listing, although I assume this has nothing to do with OAuth Dialog.

This should also have nothing to do with the Facebook SDK 3.0+ method of doing only read permissions first, nor with the fact that Facecbook OAuth Dialog breaks up permissions into multiple pages. My problem is it requests more permissions than I want, not less.

Specifically, it is requesting things like:

notes, events, hometown, religious and political views, relationships, relationship interests, chat status, friends' relationships, relationship interests, notes, chat status, events, hometowns, religious and political views

and yet I have removed all of those permissions. I have even tried requesting only the user_photos permission, but it continues to show a long list in the OAuth Dialog.

Has anyone had this problem before? Is there some device cache that isn't removed when uninstalling facebook and your app?

I know there used to be more settings in the Facebook App Developer site, for listing permissions for authenticated referrals and such, is it possible those settings have somehow remained behind the scenes, even though they're no longer accessible?

Here is a complete log from the FbDialog class in Android Facebook SDK (pre-3.0), with me requesting only 'user_photos', and yet being shown the following dialog:

    D/Facebook-WebView( 5622): Webview loading URL: https://m.facebook.com/dialog/oauth?display=touch&client_id=xxxx&scope=user_photos&type=user_agent&redirect_uri=fbconnect%3A%2F%2Fsuccess
D/Facebook-WebView( 5622): Redirect URL: http://m.facebook.com/login.php?skip_api_login=1&api_key=xxxx&signed_next=1&next=https%3A%2F%2Fm.facebook.com%2Fdialog%2Foauth%3Fredirect_uri%3Dfbconnect%253A%252F%252Fsuccess%26display%3Dtouch%26scope%3Duser_photos%26type%3Duser_agent%26client_id%3Dxxxx%26ret%3Dlogin&cancel_uri=fbconnect%3A%2F%2Fsuccess%3Ferror%3Daccess_denied%26error_code%3D200%26error_description%3DPermissions%2Berror%26error_reason%3Duser_denied&display=touch&_rdr
D/Facebook-WebView( 5622): Webview loading URL: http://m.facebook.com/login.php?skip_api_login=1&api_key=xxxx&signed_next=1&next=https%3A%2F%2Fm.facebook.com%2Fdialog%2Foauth%3Fredirect_uri%3Dfbconnect%253A%252F%252Fsuccess%26display%3Dtouch%26scope%3Duser_photos%26type%3Duser_agent%26client_id%3Dxxxx%26ret%3Dlogin&cancel_uri=fbconnect%3A%2F%2Fsuccess%3Ferror%3Daccess_denied%26error_code%3D200%26error_description%3DPermissions%2Berror%26error_reason%3Duser_denied&display=touch&_rdr
D/Facebook-WebView( 5622): Webview onPageFinished: http://m.facebook.com/login.php?skip_api_login=1&api_key=xxxx&signed_next=1&next=https%3A%2F%2Fm.facebook.com%2Fdialog%2Foauth%3Fredirect_uri%3Dfbconnect%253A%252F%252Fsuccess%26display%3Dtouch%26scope%3Duser_photos%26type%3Duser_agent%26client_id%3Dxxxx%26ret%3Dlogin&cancel_uri=fbconnect%3A%2F%2Fsuccess%3Ferror%3Daccess_denied%26error_code%3D200%26error_description%3DPermissions%2Berror%26error_reason%3Duser_denied&display=touch&_rdr
D/Facebook-WebView( 5622): Webview loading URL: http://m.facebook.com/login.php?skip_api_login=1&api_key=xxxx&signed_next=1&next=https%3A%2F%2Fm.facebook.com%2Fdialog%2Foauth%3Fredirect_uri%3Dfbconnect%253A%252F%252Fsuccess%26display%3Dtouch%26scope%3Duser_photos%26type%3Duser_agent%26client_id%3Dxxxx%26ret%3Dlogin&cancel_uri=fbconnect%3A%2F%2Fsuccess%3Ferror%3Daccess_denied%26error_code%3D200%26error_description%3DPermissions%2Berror%26error_reason%3Duser_denied&display=touch&_rdr
D/Facebook-WebView( 5622): Webview onPageFinished: http://m.facebook.com/login.php?skip_api_login=1&api_key=xxxx&signed_next=1&next=https%3A%2F%2Fm.facebook.com%2Fdialog%2Foauth%3Fredirect_uri%3Dfbconnect%253A%252F%252Fsuccess%26display%3Dtouch%26scope%3Duser_photos%26type%3Duser_agent%26client_id%3Dxxxx%26ret%3Dlogin&cancel_uri=fbconnect%3A%2F%2Fsuccess%3Ferror%3Daccess_denied%26error_code%3D200%26error_description%3DPermissions%2Berror%26error_reason%3Duser_denied&display=touch&_rdr
D/Facebook-WebView( 5622): Webview loading URL: https://m.facebook.com/login.php?skip_api_login=1&signed_next=1&next=https%3A%2F%2Fm.facebook.com%2Fdialog%2Foauth%3Fredirect_uri%3Dfbconnect%253A%252F%252Fsuccess%26display%3Dtouch%26scope%3Duser_photos%26type%3Duser_agent%26client_id%3Dxxxx%26ret%3Dlogin&refsrc=http%3A%2F%2Fm.facebook.com%2Flogin.php&app_id=42701128600&refid=9
D/Facebook-WebView( 5622): Redirect URL: https://m.facebook.com/dialog/oauth?redirect_uri=fbconnect%3A%2F%2Fsuccess&display=touch&scope=user_photos&type=user_agent&client_id=xxxx&ret=login&ext=1372379560&hash=AeZmYhdN1rISiaNZ&refid=9&_rdr
D/Facebook-WebView( 5622): Webview loading URL: https://m.facebook.com/dialog/oauth?redirect_uri=fbconnect%3A%2F%2Fsuccess&display=touch&scope=user_photos&type=user_agent&client_id=xxxx&ret=login&ext=1372379560&hash=AeZmYhdN1rISiaNZ&refid=9&_rdr
D/Facebook-WebView( 5622): Webview onPageFinished: https://m.facebook.com/dialog/oauth?redirect_uri=fbconnect%3A%2F%2Fsuccess&display=touch&scope=user_photos&type=user_agent&client_id=xxxx&ret=login&ext=1372379560&hash=AeZmYhdN1rISiaNZ&refid=9&_rdr

permissions dialog

Update (Added code):

Below I list our full permission set, but in the log example above I commented all out except "user_photos". The parts that request Facebook authorize are essentially:

public final class MyFacebook {
private static MyFacebook mInstance = null;
private static Facebook mFBInstance = null;
private String[] mPermissions = null;
private Handler mHandler = null;

public static MyFacebook getInstance() {
    if(mInstance == null) {
        mInstance = new MyFacebook();
    }

    return mInstance;
}

public Facebook getFBInstance() {
    if(mFBInstance == null) {
        mFBInstance = new Facebook("<my facebook app id>");

        SessionStore.restore(mFBInstance, MyApplication.getContext());
        SessionEvents.addAuthListener(new FacebookAuthListener());
        SessionEvents.addLogoutListener(new FacebookLogoutListener());
    }

    return mFBInstance;
}

private MyFacebook () {
    mHandler = new Handler();
}

public String[] getPermissionsList() {
    if(mPermissions == null) {
        int i = 0;
        mPermissions = new String[42];
        mPermissions[i++] = "user_about_me";
        mPermissions[i++] = "user_activities";
        mPermissions[i++] = "user_birthday";
        mPermissions[i++] = "user_education_history";
        mPermissions[i++] = "user_groups";
        mPermissions[i++] = "user_interests";
        mPermissions[i++] = "user_likes";
        mPermissions[i++] = "user_location";
        mPermissions[i++] = "user_photos";
        mPermissions[i++] = "user_status";
        mPermissions[i++] = "user_videos";
        mPermissions[i++] = "user_website";
        mPermissions[i++] = "user_work_history";
        mPermissions[i++] = "email";
        mPermissions[i++] = "read_friendlists";
        mPermissions[i++] = "read_mailbox";
        mPermissions[i++] = "read_requests";
        mPermissions[i++] = "read_stream";
        mPermissions[i++] = "user_checkins";
        mPermissions[i++] = "friends_about_me";
        mPermissions[i++] = "friends_activities";
        mPermissions[i++] = "friends_birthday";
        mPermissions[i++] = "friends_education_history";
        mPermissions[i++] = "friends_groups";
        mPermissions[i++] = "friends_interests";
        mPermissions[i++] = "friends_likes";
        mPermissions[i++] = "friends_location";
        mPermissions[i++] = "friends_photos";
        mPermissions[i++] = "friends_status";
        mPermissions[i++] = "friends_videos";
        mPermissions[i++] = "friends_website";
        mPermissions[i++] = "friends_work_history";
        mPermissions[i++] = "friends_checkins";
        mPermissions[i++] = "publish_stream";
        mPermissions[i++] = "manage_notifications";
        mPermissions[i++] = "publish_actions";
        mPermissions[i++] = "user_actions.music";
        mPermissions[i++] = "user_actions.news";
        mPermissions[i++] = "user_actions.video";
        mPermissions[i++] = "friends_actions.music";
        mPermissions[i++] = "friends_actions.news";
        mPermissions[i++] = "friends_actions.video";
    }

    return mPermissions;
}

public void logout() {
    SessionEvents.onLogoutBegin();
    AsyncFacebookRunner asyncRunner = new AsyncFacebookRunner(getInstance().getFBInstance());
    asyncRunner.logout(MyApplication.getContext(), new LogoutRequestListener());
}


public static class FacebookAuthListener implements AuthListener {
    public void onAuthSucceed() {
        Log.d("FB", "onAuthSucceed: ");
        SessionStore.save(getInstance().getFBInstance(), MyApplication.getContext());
        Log.d("FB", "Finished Saving in onAuthSucceed");
    }

    public void onAuthFail(String error) {
        Log.e("FB", error);
    }
}

public static class FacebookLogoutListener implements LogoutListener {
    public void onLogoutBegin() {
    }

    public void onLogoutFinish() {
        // remove our stored session
        SessionStore.clear(MyApplication.getContext());
    }
}

private class LogoutRequestListener extends BaseRequestListener {
    public void onComplete(String response, final Object state) {
        // callback should be run in the original thread,
        // not the background thread
        mHandler.post(new Runnable() {
            public void run() {
                SessionEvents.onLogoutFinish();
            }
        });
    }
}

}

And the usage:

@JavascriptInterface
    public void clientAddAccount(final int network) {
        AccountManageActivity.this.runOnUiThread(new Runnable(){
            @Override
            public void run() {
                MyFacebook.getInstance().getFBInstance().authorize(ThisActivity.this, MyFacebook.getInstance().getPermissionsList(),
                                  new FBLoginDialogListener());
            }
        });
    }

...

private final class FBLoginDialogListener implements DialogListener {
    public void onComplete(Bundle values) {
        Log.d("LB", "Login Success. onComplete");
        SessionEvents.onLoginSuccess();

        if(MyFacebook.getInstance().getFBInstance().isSessionValid()) {
            // do app stuff
        }
    }

    public void onFacebookError(FacebookError error) {
        Log.d("LB", "onFacebookError: " + error.getMessage());
        SessionEvents.onLoginError(error.getMessage());

        // do app stuff
    }

    public void onError(DialogError error) {
        Log.d("LB", "onError: " + error.getMessage());
        SessionEvents.onLoginError(error.getMessage());

        // do app stuff
    }

    public void onCancel() {
        Log.d("LB", "onCancel");
        SessionEvents.onLoginError("Action Canceled");

        // do app stuff
    }
}

Solution

  • Turns out this was because of legacy settings on Facebook's side related to old methods of whitelisted apps.

    The fix required contacting Facebook to have them remove those non-standard settings on our developer account. This is a rare occurrence and only because we were originally whitelisted by contacting them to request it.