Handle invalid accessToken with FBSession openActiveSessionWithReadPermissions in Facebook iOS 3.1.1 SDK

ozzotto picture ozzotto · Oct 5, 2012 · Viewed 14.5k times · Source

Before anything, I have read both this and this questions to solve the problem below and before asking.

My problem is that when the accessToken gets expired (either because the expiration date passes, or manually by deleting the app from my Facebook's App Center) the following code:

if ([[FBSession activeSession] isOpen]) {
        //do something
    }
else {
        [FBSession openActiveSessionWithReadPermissions:nil allowLoginUI:YES completionHandler:^(FBSession *session, FBSessionState status, NSError *error) {
            if(FB_ISSESSIONOPENWITHSTATE(status)) {
                //do something
            }
          }
       }];
     }

gets in the else block with FBSession.activeSession open but when the 'do something' is executed the accessToken is invalid so the request gets Error: HTTP status code: 400. When I try to do the whole procedure twice immediately the FBSession asks for permission (either UIAlertView for iOS6 integrated facebook, Facebook App or Facebook website in Safari) and the rest runs smoothly.

My concern is why I have to do everything twice to work well and why Facebook SDK cannot detect in the first time that the activeSession and accessToken are invalid.

Thank you all in advance!

Answer

Chris Pan picture Chris Pan · Oct 9, 2012

The questions you linked are relevant, especially Facebook SDK 3.1 - Error validating access token which explains the problem where the Facebook account on the device is out of sync with the server (I.e., if you deleted the app from App Center). As mentioned there, in 3.1.1 the SDK will call to renew the device token only when it gets the invalid response from the server. This is a trade off in convenience for less round-trips to the server.

Assuming your code block is executed on applicationDidFinishLaunching or something similar, it will go to the else block because the app starts with a new session. When it calls openActiveSessionWithReadPermissions, the iOS 6 device thinks the token is valid and will let the state go to Open, so then your "do something" gets executed. Only then does the SDK get the invalid response from the server and invalidate the device token. As a result, the next time the procedure is called, it will prompt the user appropriately to authorize again.

This is intentional. For now, you can consider a automatic retry in your application if the error code describes an invalid token. For example, see the Scrumptious sample postOpenGraph retry code. In your case, it may look closer to something like (I used requestForMe as the "do something" for demonstration purposes):

else {
    [FBSessionopenActiveSessionWithReadPermissions:nil allowLoginUI:YES completionHandler:^(FBSession *session, FBSessionState status, NSError *error) {
        if(FB_ISSESSIONOPENWITHSTATE(status)) {
            //do something
            [[FBRequest requestForMe] startWithCompletionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
                if (!error){
                    NSLog(@"success on first try");
                } else if ([[error userInfo][FBErrorParsedJSONResponseKey][@"body"][@"error"][@"code"] compare:@190] == NSOrderedSame) {
                    //requestForMe failed due to error validating access token (code 190), so retry login
                    [FBSession openActiveSessionWithReadPermissions:nil allowLoginUI:YES completionHandler:^(FBSession *session, FBSessionState status, NSError *error) {
                        if (!error){
                            //do something again, or consider recursive call with a max retry count.
                            NSLog(@"success on retry");
                        }
                    }];
                }
            }];
        }
    }];
}