iOS 6 Facebook Login not refreshing access token

2019-02-19 03:12发布

问题:

Nothing I'm reading (many FB dev articles and SO question) is helping, so I thought I'd post here. I'm trying to get this all to work with iOS 6 and Facebook SDK 3.1.1.

I've got a pretty basic setup: My iOS app authenticates with Facebook, I pass the access_token to my server and the user is now logged into my app. The only other thing they can do related to facebook is post something to their wall. I'm using the legacy Dialog window so the user can add their own comments.

Everything works perfectly on a new install for the first hour or so. If the user is logged in via the iOS Settings app, they're presented with a quick iOS confirmation box, I get their token, send it to my server and we're all good.

If I paste the token I receive into Facebook's Access Token debugger, it shows that it will expire in an hour.

If I wait that hour and come back to the app afterwards, anything I try to do gives me this error:

{"error_code":190,"
  error_msg":"Error validating access token: 
              Session has expired at unix time 1351497600. 
              The current unix time is 1351525180. }
  1. What do I need to do so things just work? I've read that in SDK 3.1.1 FBSession will just take care of it, but that doesn't seem like the case. My access token is expiring, and once it does, iOS/FB SDK won't give me back a refreshed one.
  2. Even logging out (which calls [FBSession.activeSession closeAndClearTokenInformation]) doesn't fix this. The only thing I've been able to do is "Reset Content and Settings" in the iOS Simulator Preferences, re-enter my FB credentials in Settings.app, and rebuild my app. Only then can I get another token which will expire in an hour.

Random notes:

  1. When my app resumes, I'm calling [FBSession.activeSession handleDidBecomeActive] and [self.facebook extendAccessTokenIfNeeded]
  2. According to the SO question linked to above, the SDK should automatically be calling FBSession#renewSystemAuthorization. I've put an NSLog in there, and I never see that method getting called.

UPDATE #1:

I mentioned that I've read that the SDK will handle invalid/expired tokens. It seems like that should happen in FBRequestConnection#completeWithResults:orError:. See inline comments for notes about the code path.

- (void)completeWithResults:(NSArray *)results
                orError:(NSError *)error
{
    int count = [self.requests count];
    for (int i = 0; i < count; i++) {
        FBRequestMetadata *metadata = [self.requests objectAtIndex:i];
        id result = error ? nil : [results objectAtIndex:i];
        NSError *itemError = error ? error : [self errorFromResult:result];

        id body = nil;
        if (!itemError && [result isKindOfClass:[NSDictionary class]]) {
            NSDictionary *resultDictionary = (NSDictionary *)result;
            body = [FBGraphObject graphObjectWrappingDictionary:[resultDictionary objectForKey:@"body"]];
        }

        ...

        // *******************************************
        // My code actually falls into this block, proving that I do in fact have an invalid session
        // *******************************************
        if ([self isInvalidSessionError:itemError
                        resultIndex:error == itemError ? i : 0]) {
            [metadata.request.session closeAndClearTokenInformation:itemError];

            // *******************************************
            // Unfortunately metadata.request.session is nil, so this condition is never
            // run, so renewySystemAuthorization is never called
            // *******************************************
            if (metadata.request.session.loginType == FBSessionLoginTypeSystemAccount){
                [FBSession renewSystemAuthorization];
        }
    }

    ...
}

回答1:

This isn't the type of fix I was after, but things look to be working as expected, so at this point I just need to ship.

First things first, it turns out that old versions of our app were requesting offline_access. I did not know that. I went in to my FB app's Settings > Advanced, and enabled "Remove offline_access permission." I am now getting back a token that lasts two months (check out the facbeook token debugger).

Now that I'm getting back the correct kind of token, I still have a problem with getting back a valid token when it has been deactivated/expired. Since I'm now getting back a 2 month token, I can't wait for that to expire, so my plan was to trigger an OAuthException by going into my account's settings and deauthorizing it.

In my appDidBecomeActive, I put [FBRequestConnection startForMeWithCompletionHandler:nil] to simply trigger a silent request to FB, and when that fails because of an invalid session/token, the code flow runs through the correct path and [FBSession renewSystemAuthorization] ends up getting called, which is ultimately what I was needing. What this allows iOS to do is the next time I go to Log in to my app through FB, iOS prompts me again, understanding I don't have a valid token anymore. Calling [FBSession renewSystemAuthorization] effectively tells iOS that the next time my app requests access, hit facebook and give me back a new/refreshed token.

For the most part everything is working as expected now.