9

I'm using the 3.1 Facebook SDK with iOS 6 Facebook set up in Settings and my app authorized.

This executes flawlessly:

[FBSession openActiveSessionWithReadPermissions:nil allowLoginUI:YES completionHandler:^(FBSession *fbSession, FBSessionState fbState, NSError *error) { ... }

However now when I try to get 'me' information I'm getting an error:

com.facebook.sdk:ParsedJSONResponseKey = {
    body =     {
        error =         {
            code = 190;
            "error_subcode" = 463;
            message = "Error validating access token: Session has expired at unix time 1348704000. The current unix time is 1348706984.";
            type = OAuthException;
        };
    };
    code = 400;
}

If I look at [error code] it's equal to 5. Shouldn't I have a valid access token after just logging in? Do I need to call reauthorize?

UPDATE: Reauthorizing doesn't help. Oddly the accessToken for my activeSession is always coming back the same. This despite calling closeAndClearToken.

Ben Flynn
  • 18,524
  • 20
  • 97
  • 142
  • 2
    To start diagnosing, could you log the access_token you get and paste it into https://developers.facebook.com/tools/debug - is the timestamp right in there? – James Pearce Sep 27 '12 at 07:07
  • Good tip. Now the problem has gone away and I need to recreate it. I'll try your suggestion next time I see it. – Ben Flynn Sep 27 '12 at 16:37
  • I'm having the same problem with iPhone5/iOS6. It seems very sporadic though. Please let me know if you find anything more about this issue and I will do the same. – Chad Sep 27 '12 at 18:11
  • I’m hitting the exact same issue. It looks like the accessToken is cached in NSUserDefaults and is retrieved by FBSession, even though `closeAndClearToken` should nuke it. – cbowns Sep 27 '12 at 19:21
  • Here is the update (3.1.1) that fixes this directly in the SDK: https://github.com/downloads/facebook/facebook-ios-sdk/FacebookSDK-3.1.1.pkg Thanks!! – Jason Clark Oct 03 '12 at 21:49

2 Answers2

4

UPDATE: This issue has been addressed in Facebook iOS SDK 3.1.1.


I synched the code off of github and found that they weren't calling accountStore renewCredentialsForAccount:completion: anywhere. I changed the following code in authorizeUsingSystemAccountStore and it seems to have resolved the issue.

// we will attempt an iOS integrated facebook login
[accountStore requestAccessToAccountsWithType:accountType
                                      options:options
                                   completion:^(BOOL granted, NSError *error) {

                                       // this means the user has not signed-on to Facebook via the OS
                                       BOOL isUntosedDevice = (!granted && error.code == ACErrorAccountNotFound);

                                       dispatch_block_t postReauthorizeBlock = ^{
                                           NSString *oauthToken = nil;
                                           if (granted) {                                                                                                      
                                               NSArray *fbAccounts = [accountStore accountsWithAccountType:accountType];
                                               id account = [fbAccounts objectAtIndex:0];
                                               id credential = [account credential];                                                   
                                               oauthToken = [credential oauthToken];
                                           }

                                           // initial auth case
                                           if (!isReauthorize) {
                                               if (oauthToken) {
                                                   _isFacebookLoginToken = YES;
                                                   _isOSIntegratedFacebookLoginToken = YES;

                                                   // we received a token just now
                                                   self.refreshDate = [NSDate date];

                                                   // set token and date, state transition, and call the handler if there is one
                                                   [self transitionAndCallHandlerWithState:FBSessionStateOpen
                                                                                     error:nil
                                                                                     token:oauthToken
                                                    // BUG: we need a means for fetching the expiration date of the token
                                                                            expirationDate:[NSDate distantFuture]
                                                                               shouldCache:YES
                                                                                 loginType:FBSessionLoginTypeSystemAccount];
                                               } else if (isUntosedDevice) {
                                                   // even when OS integrated auth is possible we use native-app/safari
                                                   // login if the user has not signed on to Facebook via the OS
                                                   [self authorizeWithPermissions:permissions
                                                                  defaultAudience:defaultAudience
                                                                   integratedAuth:NO
                                                                        FBAppAuth:YES
                                                                       safariAuth:YES
                                                                         fallback:YES
                                                                    isReauthorize:NO];
                                               } else {
                                                   // create an error object with additional info regarding failed login
                                                   NSError *err = [FBSession errorLoginFailedWithReason:nil
                                                                                              errorCode:nil
                                                                                             innerError:error];

                                                   // state transition, and call the handler if there is one
                                                   [self transitionAndCallHandlerWithState:FBSessionStateClosedLoginFailed
                                                                                     error:err
                                                                                     token:nil
                                                                            expirationDate:nil
                                                                               shouldCache:NO
                                                                                 loginType:FBSessionLoginTypeNone];
                                               }
                                           } else { // reauth case
                                               if (oauthToken) {
                                                   // union the requested permissions with the already granted permissions
                                                   NSMutableSet *set = [NSMutableSet setWithArray:self.permissions];
                                                   [set addObjectsFromArray:permissions];

                                                   // complete the operation: success
                                                   [self completeReauthorizeWithAccessToken:oauthToken
                                                                             expirationDate:[NSDate distantFuture]
                                                                                permissions:[set allObjects]];
                                               } else {
                                                   // no token in this case implies that the user cancelled the permissions upgrade
                                                   NSError *error = [FBSession errorLoginFailedWithReason:FBErrorReauthorizeFailedReasonUserCancelled
                                                                                                errorCode:nil
                                                                                               innerError:nil];
                                                   // complete the operation: failed
                                                   [self callReauthorizeHandlerAndClearState:error];

                                                   // if we made it this far into the reauth case with an untosed device, then
                                                   // it is time to invalidate the session
                                                   if (isUntosedDevice) {
                                                       [self closeAndClearTokenInformation];
                                                   }
                                               }
                                           }
                                       };



                                       if (granted) {
                                           [accountStore renewCredentialsForAccount:[[accountStore accountsWithAccountType:accountType] lastObject] completion:^(ACAccountCredentialRenewResult renewResult, NSError *error) {
                                               dispatch_async(dispatch_get_main_queue(), postReauthorizeBlock);
                                           }];
                                       } else {
                                           // requestAccessToAccountsWithType:options:completion: completes on an
                                           // arbitrary thread; let's process this back on our main thread
                                           dispatch_async(dispatch_get_main_queue(), postReauthorizeBlock);
                                       }

                                   }];

}
Ben Flynn
  • 18,524
  • 20
  • 97
  • 142
  • We should probably be checking for an expiryDate before renewing (since it takes time to renew) but despite what it says in Apple's docs (http://developer.apple.com/library/ios/#documentation/Accounts/Reference/ACAccountCredentialClassRef/Reference/Reference.html#//apple_ref/doc/c_ref/ACAccountCredential), the expiryDate param doesn't seem to exist. – Ben Flynn Sep 27 '12 at 20:54
  • 2
    Hi Ben, you are right about needed to call renew*. Calling from here works well. The choice of where to make the calls depends generally on the trade-off between consistency in behavior, and extra network roundtrips. In the next SDK update we are considering making the call in the FBRequestConnection logic that handles invalid tokens. – Jason Clark Sep 29 '12 at 16:23
  • @JasonClark That seems like a reasonable choice. I'd suggest also having just a plain old renew call in the FB SDK. That'd be useful if you were just using the SDK for token management rather than a conduit for making graph calls. Thanks for responding! – Ben Flynn Oct 01 '12 at 18:21
  • Hi Ben, your suggestion to add a helper method is a good one, and we will do that for our next update. For now we have done a smaller dot-release that fixes the sync problem implicitly but makes no changes to the public API surface. Here is a link to the SDK update (3.1.1): https://github.com/downloads/facebook/facebook-ios-sdk/FacebookSDK-3.1.1.pkg Thanks!! – Jason Clark Oct 03 '12 at 21:49
0

So it's addressed, but I've been calling /me from our backend to verify since you can't trust the device.

So I make a call to FBSession's + (void)renewSystemAuthorization when the backend comes back with an authorization error.

Ben Trengrove
  • 8,191
  • 3
  • 40
  • 58
hawflakes
  • 1
  • 1
  • 1
    I don't see a renewSystemAuthorization class method in FBSession in 3.1.1. – gerry3 Dec 14 '12 at 22:48
  • Looks like they addressed this issue per this commit for which I am behind on. https://github.com/facebook/facebook-ios-sdk/commit/0b3d28b7bea8b63dd9efca67e1438d72fc78daf5 – hawflakes Dec 17 '12 at 01:08