Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Session.isOpened() == true but Session.getAccessToken() == ""

In my app, I'm trying to authenticate (login) by passing Facebook session information (token and expiration date) to my server.

My login sequence to the app server is as follows:

  1. Get active facebook session.
  2. If the session is valid, get token and send it on to the server.

And in code:

Session session = Session.getActiveSession();
if(session != null && session.isOpened() && !didLogin) {
    didLogin = true;
    String token = session.getAccessToken();
    Date expires = session.getExpirationDate();
    loginWithFacebookSession(token, expires);
}

I have noticed that suddenly, after a few months of this working just fine, the information being sent to the server is occasionally not valid, specifically the token is an empty string and the expires is an invalid date.

After going a bit through the Facebook SDK (version 3.0.1), I spotted what is probably the basis of my error:

private static final Date MIN_DATE = new Date(Long.MIN_VALUE);
private static final Date ALREADY_EXPIRED_EXPIRATION_TIME = MIN_DATE;
private static final Date DEFAULT_LAST_REFRESH_TIME = new Date();

static AccessToken createEmptyToken(List<String> permissions) {
    return new AccessToken("", ALREADY_EXPIRED_EXPIRATION_TIME, permissions, AccessTokenSource.NONE,
            DEFAULT_LAST_REFRESH_TIME);
}

This means that somewhere along the way, the Facebook SDK is creating an empty token and returning it with a SessionState.Category.OPENED_CATEGORY.

Why is session.isOpened() returning true when in fact there is no accessToken information? Should I be checking a different property? Is this a bug in Facebook's SDK?

EDIT:
Reported this to Facebook at: https://developers.facebook.com/bugs/121924628017965

like image 711
thepoosh Avatar asked Jun 17 '13 06:06

thepoosh


2 Answers

Use this method and check if your hash is correct

public void getHashKeyForFacebook(Activity activity, String packageName){
    try{
        PackageInfo info = activity.getPackageManager().getPackageInfo(packageName,  PackageManager.GET_SIGNATURES);

        for (Signature signature : info.signatures){
                MessageDigest md = MessageDigest.getInstance("SHA");
                md.update(signature.toByteArray());
                Log.d("KeyHash:", Base64.encodeToString(md.digest(), Base64.DEFAULT));
        }
    } catch (Exception ex){

    }
}
like image 58
Douglas Marques Avatar answered Oct 14 '22 04:10

Douglas Marques


Apart from the Facebook SDK being like voodoo magic, there are some thing you are doing that I do not do in my auth flow. I'm assuming here that your code snippet is not in the Session.StatusCallback call function. No matter what, I always open the session for read before accessing the token from the callback. This request just inconveniences the user for a few milliseconds with a loading spinner before it returns. It also helps in case when the user has deleted the permissions from their Facebook settings page.

My auth flow is something like this:

private void startFbAuth() {
    Session session = Session.getActiveSession();
    if (session == null) {
        session = new Session(getApplicationContext());
        Session.setActiveSession(session);

        Session.OpenRequest openReadRequest = new Session.OpenRequest(this);
        openReadRequest.setPermissions(Arrays.asList({ "email" }));
        openReadRequest.setCallback(statusCallback);

        Session.NewPermissionsRequest permissionReadRequest = new Session.NewPermissionsRequest(this, Arrays.asList(EMAIL_PERMISSIONS));
        permissionReadRequest.setCallback(statusCallback);

        if (!session.isOpened() && !session.isClosed()) {
            session.openForRead(openReadRequest);
        } else {
            session.requestNewReadPermissions(permissionReadRequest);
        }
    }

    private Session.StatusCallback statusCallback = new SessionStatusCallback();

    private class SessionStatusCallback implements Session.StatusCallback {
        @Override
        public void call(Session session, SessionState state, Exception exception) {
            if (session.isClosed()) {
                session.closeAndClearTokenInformation();
                Session.setActiveSession(null);
                SessionStore.clearFacebookInformation(Activity.this);
                // Either throw an error or try reconnecting by calling startFbAuth
            }
            if (exception != null) {
                session.closeAndClearTokenInformation();
                Session.setActiveSession(session);
                // Either throw an error or try reconnecting by calling startFbAuth
            } else {
                if (session.isOpened()) {
                    String token = session.getAccessToken();
                    // It shouldn't be null or empty here
                }
            }
        }
    }
like image 26
rarp Avatar answered Oct 14 '22 04:10

rarp