71

I've just updated facebook sdk v4.0

and according the tutorial of Using Custom Login UIs

-(IBAction)facebookLoginClick:(id)sender {

FBSDKLoginManager *login = [[FBSDKLoginManager alloc] init];

[login logInWithReadPermissions:@[@"email"] handler:^(FBSDKLoginManagerLoginResult *result, NSError *error) {
    if (error) {
        // Process error
    } else if (result.isCancelled) {
        // Handle cancellations
    } else {
        // If you ask for multiple permissions at once, you
        // should check if specific permissions missing
        if ([result.grantedPermissions containsObject:@"email"]) {
            // Do work
        }
    }
}];
}

BUT the result is always nil and error code is 304, am I missing something?

Fattie
  • 27,874
  • 70
  • 431
  • 719
jim
  • 1,035
  • 1
  • 8
  • 16

14 Answers14

213

I had a similar problem.

After initialising FBSDKLoginManager I added a line to flush out the data and the (Facebook)Token:

FBSDKLoginManager *loginmanager= [[FBSDKLoginManager alloc]init];    
[loginmanager logOut];

Thus, exactly as the OP asks, "am I missing something"?

Yes, the following standard example code which is seen everywhere, is simply wrong:

-(IBAction)facebookLoginClick:(id)sender
{
FBSDKLoginManager *login = [[FBSDKLoginManager alloc] init];
---- ONE MAGIC LINE OF CODE IS MISSING HERE ----
[login logInWithReadPermissions:@[@"email"]
   handler:^(FBSDKLoginManagerLoginResult *result, NSError *error)
    {
    if (error) {...}
    else if (result.isCancelled) {...}
    else { // (NB for multiple permissions, check every one)
       if ([result.grantedPermissions containsObject:@"email"])
          { NSLog(@"%@",result.token); }
       }
}];
}

you must do this:

-(IBAction)facebookLoginClick:(id)sender
{
FBSDKLoginManager *login = [[FBSDKLoginManager alloc] init];
[login logOut];   //ESSENTIAL LINE OF CODE
[login logInWithReadPermissions:@[@"email"]
   handler:^(FBSDKLoginManagerLoginResult *result, NSError *error)
    {
    if (error) {...}
    else if (result.isCancelled) {...}
    else { // (NB for multiple permissions, check every one)
       if ([result.grantedPermissions containsObject:@"email"])
          { NSLog(@"%@",result.token); }
       }
}];
}

Otherwise, very simply, the app will not work if the user happens to change FB accounts on the device. (Unless they happen to for some reason re-install the app!)

Once again - the popular sample code above simply does not work (the app goes in to an endless loop) if a user happens to change FB accounts. The logOut call must be made.

starball
  • 20,030
  • 7
  • 43
  • 238
Vineeth Joseph
  • 5,177
  • 3
  • 17
  • 31
  • 1
    This seems a bit excessive. In my case Adrian999's answer below ('turning it off then on again') was the actual issue. – damian Jun 14 '15 at 10:06
  • the correct and awesome answer. THANK YOU for saving me hours. – Fattie Aug 18 '15 at 00:44
  • @JoeBlow Glad to hear that it helped..:) – Vineeth Joseph Aug 18 '15 at 09:59
  • You can have a problem If you try to log out before you log in. If *email* permission is not granted by user for your Facebook app your user will have no option to grant it if you use this approach. So better use the * refreshCurrentAccessToken* approach described below. – zh. Oct 23 '15 at 14:49
  • damn facebook. This is official facebook code snippet in question! – Saqib Saud Mar 03 '16 at 11:15
  • Calling `logOut` means the `rerequest` flag will not be added. Is there any workaround for this? – Code May 28 '16 at 16:37
  • I agree with @damian. I feel like there's a reason why this `standard example code is seen everywhere`. Logging out could screw up the rest of your logic. This happened during my tests when I logged in with user A, and requested additional permissions using user B. If you don't logout, this forces the app to maintain login data for user A. If you logout however, then re-request new permissions with user B, facebook will have you logged in with user B, but your app logic might still think you're user A (say, if you save your facebook userId on Login instead of when adding new permissions) – Merricat Jun 25 '19 at 20:40
8

This happened to me when I changed the Facebook AppID (in the Info.plist file) switching form a test Facebook app to a production Facebook app. If your app sends the Facebook token to a server that generate a JWT for for instance, Facebook SDK still persists the fbToken (FBSDKAccessToken.currentAccessToken()) persisted unless it's told to remove it. That may also happen in production when a user logs out of the app, a log out API request will log the user out and reset your JWT. But even before sending the log out API request, the client will need to tell the FBSDKLoginManager instance to log out.

if let currentAccessToken = FBSDKAccessToken.currentAccessToken() where currentAccessToken.appID != FBSDKSettings.appID()
    {
        loginManager.logOut()
    }
amosel
  • 661
  • 8
  • 10
7

I had this, it occurred when changing the FB app details in Xcode while an iOS app was running. You need to delete the app from the device and then republish with the new FB app setup. Just recompiling is not enough to clear the old FB settings

Adrian999
  • 71
  • 1
  • But this does not help at all in the case where: your user happens to quit the app, and then login (on the browser) as a different FB user, and then launch the app again. – Fattie Aug 18 '15 at 00:45
5

Swift 3.0

func loginWithFacebook
{
        let loginManager = FBSDKLoginManager()
        if let currentAccessToken = FBSDKAccessToken.current(), currentAccessToken.appID != FBSDKSettings.appID()
        {
            loginManager.logOut()
        }

        loginManager.logIn(withReadPermissions: ["public_profile","email"], from: self, handler: { (result, error) in
            if error != nil {
                print("error\(String(describing: error))")
            }
            else if (result?.isCancelled)! {
            }
            else {
                print(FBSDKAccessToken.current())
            }
        })
}
Abhishek Jain
  • 4,557
  • 2
  • 32
  • 31
4

You can use the following code to solve your problem :

[FBSDKAccessToken refreshCurrentAccessToken:^(FBSDKGraphRequestConnection *connection, id result, NSError *error){}
Piyush
  • 1,534
  • 12
  • 32
4

swift 4.0, 4.2, 5.0

FBSDKLoginManager().logOut()
Md. Ibrahim Hassan
  • 5,359
  • 1
  • 25
  • 45
Tanjima Kothiya
  • 5,695
  • 2
  • 8
  • 17
3

According to the docs, 304 FBSDKLoginUserMismatchErrorCode "Indicates a failure to request new permissions because the user has changed".

Make sense in the scenario @Adrian999 mentioned.

Joseph Lin
  • 3,324
  • 1
  • 29
  • 39
3

Swift 4.0 and FBSDKCoreKit 4.33

let loginManager = LoginManager()
if let currentAccessToken = AccessToken.current, currentAccessToken.appId != SDKSettings.appId
{
    loginManager.logOut()
}
alexander.polomodov
  • 5,396
  • 14
  • 39
  • 46
2

Actually, the reason is very simple, you can try following step to reappear this error code easily.

The "Facebook login fail" may have two reasons:

  1. you get the user info from facebook fail.
  2. you get the user info from facebook success,but upload to your own server fail.

The code in FBSDKLoginManager.m is:

- (void)validateReauthentication:(FBSDKAccessToken *)currentToken withResult:(FBSDKLoginManagerLoginResult *)loginResult
{
  FBSDKGraphRequest *requestMe = [[FBSDKGraphRequest alloc] initWithGraphPath:@"me"
                                                                   parameters:nil
                                                                  tokenString:loginResult.token.tokenString
                                                                   HTTPMethod:nil
                                                                        flags:FBSDKGraphRequestFlagDoNotInvalidateTokenOnError | FBSDKGraphRequestFlagDisableErrorRecovery];
  [requestMe startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) {
NSString *actualID = result[@"id"];
  if ([currentToken.userID isEqualToString:actualID]) {
    [FBSDKAccessToken setCurrentAccessToken:loginResult.token];
    [self invokeHandler:loginResult error:nil];
  } else {
    NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
    [FBSDKInternalUtility dictionary:userInfo setObject:error forKey:NSUnderlyingErrorKey];
    NSError *resultError = [NSError errorWithDomain:FBSDKLoginErrorDomain
                                             code:FBSDKLoginUserMismatchErrorCode
                                         userInfo:userInfo];
   [self invokeHandler:nil error:resultError];
  }
}];
}

when currentToken.userID is not equal actualID, the FBSDKLoginUserMismatchErrorCode will throw.

Now, this issue will reappear when user A login facebook fail,but you do not have [FacebookSDKManager logOut] after the fail, the app will cache the accessToken for the user A , and then user A change facebook account to user B, when user B login facebook again,it will reappear this issue.

iCrany
  • 115
  • 10
2

I was also stuck on this issue for a while. then i created object for FBSDKLoginManager in .h file and in viewDidLoad of .m file , initialize it and set logout property.

 _fbLogin= [[FBSDKLoginManager alloc]init];    
 [_fbLogin logOut];

This helped me and hope helps you as well. All the best.

Thanks

Kunal Gupta
  • 2,984
  • 31
  • 34
0

Single Sign On

I've also found that this issue happens if you have an app that does not require single sign on (i.e. an app which multiple users will create content and share from one device).

You can change single sign on in your app settings https://developers.facebook.com/apps/your-app-id/settings/.

Neil Japhtha
  • 875
  • 10
  • 9
0

For me in Objective-C following worked

FBSDKLoginManager *login = [[FBSDKLoginManager alloc] init];
if ([FBSDKAccessToken currentAccessToken]) {
             [login logOut];
        }

and issue was user was trying to login as another user as suggested by @Vineeth Joseph

Chirag Purohit
  • 731
  • 11
  • 20
-2

Check the state of your FBSession:

isOpen
Indicates whether the session is open and ready for use.
@property (readonly) BOOL isOpen;
Declared In: FBSession.h

https://developers.facebook.com/docs/reference/ios/3.0/class/FBSession/

Pablo A.
  • 2,042
  • 1
  • 17
  • 27
  • 1
    You don't have `FBSession` class in v4 anymore, it's deprecated. Instead it being replaced by `FBSDKAccessToken` and `FBSDKLoginManager`. See https://developers.facebook.com/docs/ios/upgrading-4.x#login – Vinh Nguyen Apr 03 '15 at 01:08
  • For an older version. New SDK is released – Vineeth Joseph Jun 11 '15 at 11:48
-4

304 indicates that nothing has changed. Probably you are somehow already authenticated and so getting nil with 304 indicating nothing has changed.

ktv
  • 52
  • 1