Search code examples
iosfacebookfacebook-ios-sdk

Open an FBSession with Existing Token Data on iOS


I have a backend service that will log me in my app and return an extended facebook token from previous login, possibly from another device (so no credentials cached on this device). I want to use this token to start a Facebook Session. I am using something like:

FBSession* session = [[FBSession alloc] initWithPermissions:@[@"publish_actions"]];
FBAccessTokenData* tokenData = 
[FBAccessTokenData createTokenFromString:token
                             permissions:@[@"publish_actions"]
                          expirationDate:nil
                               loginType:FBSessionLoginTypeTestUser
                             refreshDate:nil];


[session openFromAccessTokenData:tokenData completionHandler:nil];

I am here passing 'nil' for clarity, in my code I am handling the completion and loggin the session object returned which appears to be in an Open state with no errors. When I try and use the session I get an error like:

Error Domain=com.facebook.sdk Code=5 "The operation couldn’t be completed. (com.facebook.sdk error 5.)" UserInfo=0xb8b1680 {com.facebook.sdk:HTTPStatusCode=403, com.facebook.sdk:ParsedJSONResponseKey={
    body =     {
        error =         {
            code = 200;
            message = "(#200) The user hasn't authorized the application to perform this action";
            type = OAuthException;
        };
    };
    code = 403;
}, com.facebook.sdk:ErrorSessionKey=<FBSession: 0xa2b62a0, state: FBSessionStateOpen, loginHandler: 0xa29e9b0, appID: 131721883664256, urlSchemeSuffix: , tokenCachingStrategy:<FBSessionTokenCachingStrategy: 0xa273f60>, expirationDate: 4001-01-01 00:00:00 +0000, refreshDate: 2013-07-26 16:21:10 +0000, attemptedRefreshDate: 0001-12-30 00:00:00 +0000, permissions:(
    email,
    "publish_actions"
)>}

Any suggestions...?


Solution

  • I found a solution: (provided you have an existing token)

    First subclass the FBSessionTokenCachingStrategy class as MySessionTokenCachingStrategy and override the method below:

    - (FBAccessTokenData *)fetchFBAccessTokenData 
    {
        NSMutableDictionary *tokenInformationDictionary = [NSMutableDictionary new];
    
        // Expiration date
        tokenInformationDictionary[@"com.facebook.sdk:TokenInformationExpirationDateKey"] = [NSDate dateWithTimeIntervalSinceNow: 3600];
    
        // Refresh date
        tokenInformationDictionary[@"com.facebook.sdk:TokenInformationRefreshDateKey"] = [NSDate date];
    
        // Token key
        tokenInformationDictionary[@"com.facebook.sdk:TokenInformationTokenKey"] = self.token;
    
        // Permissions
        tokenInformationDictionary[@"com.facebook.sdk:TokenInformationPermissionsKey"] = self.permissions;
    
        // Login key
        tokenInformationDictionary[@"com.facebook.sdk:TokenInformationLoginTypeLoginKey"] = @0;
    
        return [FBAccessTokenData createTokenFromDictionary: tokenInformationDictionary];
    }
    

    Then use the class above to create

    MySessionTokenCachingStrategy* tokenCachingStrategy = 
    [[MySessionTokenCachingStrategy alloc] initWithToken:token
                                          andPermissions:@[@"read_stream"]];
    
    
    
    FBSession *session = [[FBSession alloc] initWithAppID: nil
                                              permissions: @[@"read_stream"]]
                                          urlSchemeSuffix: nil
                                       tokenCacheStrategy: tokenCachingStrategy];
    
        if (session.state == FBSessionStateCreatedTokenLoaded)
        {
            // Set the active session
            [FBSession setActiveSession: session];
    
            // Open the session, but do not use iOS6 system acount login
            // if the caching strategy does not store info locally on the
            // device, otherwise you could use:
            // FBSessionLoginBehaviorUseSystemAccountIfPresent
            [session openWithBehavior: FBSessionLoginBehaviorWithFallbackToWebView
                    completionHandler: ^(FBSession *session,
                                         FBSessionState state,
                                         NSError *error) {
                        if (!error)
                        {
                            if (session.isOpen)
                            {
                                successBlock();
                            }
                        }
                        else
                        {
                            failureBlock([error description]);
                        }
                    }];
        }