Since the release of the new FBSDK I'm facing this problem.
Scenario:
1.User logs into my app via FBLogin
2.User declines one of my permissions
My app sets him back to first screen (I don't allow creating an account without providing info)
He tries logging in again
He realizes FBSDK saved his settings he cannot change.
How do I provide the user with the possibility to change it?
My app runs to the point, I am noticing a declined permission and I give my user a notification to change the setting.
But I am clueless what to do at this point.
Firstly, you must not make all permissions as mandatory from Facebook.
Although if needed be, then you can check for the permission granted or not using the following code
if ([[FBSDKAccessToken currentAccessToken] hasGranted:#"publish_actions"]) {
// TODO: publish content.
} else {
FBSDKLoginManager *loginManager = [[FBSDKLoginManager alloc] init];
[loginManager logInWithPublishPermissions:#[#"publish_actions"]
fromViewController:self
handler:^(FBSDKLoginManagerLoginResult *result, NSError *error) {
//TODO: process error or result.
}];
}
To check for Declined Permissions
FBSDKLoginManager *loginManager = [[FBSDKLoginManager alloc] init];
[loginManager logInWithPublishPermissions:#[#"publish_actions"]
fromViewController:self
handler:^(FBSDKLoginManagerLoginResult *result, NSError *error) {
if ([result.declinedPermissions containsObject:#”publish_actions”]) {
// TODO: do not request permissions again immediately. Consider providing a NUX
// describing why the app want this permission.
} else {
// ...
}
}];
Note : If the user declines permissions, you should not immediately ask for them again. Instead your app should continue functioning. You may consider providing a guide or user interface explaining the benefits of granting that permission but should only ask for it again if the user performs an action that needs it.
Ref :
https://developers.facebook.com/docs/facebook-login/ios/permissions
https://developers.facebook.com/docs/ios/errors
I'm trying to log in with Facebook using Parse.com's PFFacebookUtilsV4. I'm using their standard:
PFFacebookUtils.logInInBackgroundWithReadPermissions(["public_profile"])
{ (user, error) in
When I have connected my FB account to my iPhone in my iPhone's settings. I get an error with code 307 (shown below) every time. If I delete the FB/iPhone account connection from my iPhone's settings, the error goes away and the standard authentication happens. I'm testing on my phone. I have the FB app on my phone, but that doesn't seem to affect the error. Just having it connected via my iPhone's settings.
After digging deeper into the stack trace, I found a different error. This solved it iOS 6 Facebook posting procedure ends up with "remote_app_id does not match stored id"
Just in case anyone has any other errors with login with Facebook on parse here's what I learnt
Add this key to your parse server dashboard FACEBOOK_APP_ID (near where you add MASTER_KEY)
You will have to switch up from the following method
[PFFacebookUtils logInWithPermissions:permissionsArray block:^(PFUser *user, NSError *error) {
to first executing the standard Facebook get token
FBSDKLoginManager *login = [[FBSDKLoginManager alloc] init];
[login logInWithReadPermissions: #[#"public_profile"]
fromViewController:self
handler:^(FBSDKLoginManagerLoginResult *result, NSError *error) {
if (error) {
NSLog(#"Process error");
} else if (result.isCancelled) {
NSLog(#"Cancelled");
} else {
NSLog(#"Logged in");
NSString *facebookUserId = [FBSDKAccessToken currentAccessToken].userID;
NSString *accessToken = [FBSDKAccessToken currentAccessToken].tokenString;
NSDate *expirationDate = [FBSDKAccessToken currentAccessToken].expirationDate;
[self loginFacebookUserWithTokenInfo:facebookUserId
accessToken:accessToken
expirationDate:expirationDate];
}
}];
Then you login using the following section of code
[PFFacebookUtils logInWithFacebookId:facebookId
accessToken:accessToken
expirationDate:expirationDate
block:^(PFUser *user, NSError *error) {
if (error) {
NSLog(#"Error!");
} else {
NSLog(#"User logged in through Facebook and parse!");
// do as you please
}
}];
So here's a weird one, in one of my apps i have this FB login code:
FBSDKLoginManager *login = [[FBSDKLoginManager alloc] init];
[login logInWithReadPermissions:#[#"public_profile", #"user_friends ", #"user_likes", #"email", #"user_birthday"]
handler:^(FBSDKLoginManagerLoginResult *result, NSError *error) {
if (error) {
// Process error
NSLog(#"error in opening facebook session - %#", error.localizedDescription);
handler(NO, nil, error);
}else if (result.isCancelled){
// Handle cancellations
handler(NO, nil, error);
}else{
// If you ask for multiple permissions at once, you
// should check if specific permissions missing
FBSDKAccessToken *token = [FBSDKAccessToken currentAccessToken];
handler(YES, token.tokenString, nil);
}
}];
Some of my Beta users have been complaining that the app crashes once they click on the FB login button. I have check my Analytics services and i can see that it is ture.
Now the problem arises here, i have logged in (with my device) into one of my Beta testers FB account to debug this, but for some reason the FB connect works good.
Does any one have any advice or this may have happened to someone?
Thanks
I am integrating FacebookSDK 4.x integration with custom UI and using following method to log-in and also getting email permission from user.
FBSDKLoginManager *login = [[FBSDKLoginManager alloc] init];
[login logInWithReadPermissions:#[#"email"] handler:^(FBSDKLoginManagerLoginResult *result, NSError *error) {
if (error){
NSLog(#"%#",[error localizedDescription]);
}
else if (result.isCancelled){
NSLog(#"Cancled");
}
else
{
if ([result.grantedPermissions containsObject:#"email"])
{
NSLog(#"Granted all permission");
if ([FBSDKAccessToken currentAccessToken])
{
[[[FBSDKGraphRequest alloc] initWithGraphPath:#"me" parameters:nil] startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error)
{
if (!error)
{
NSLog(#"%#",result);
}
}];
}
}
else
{
NSLog(#"Not granted");
}
}
}];
This works great unless the user denies access to "email". I see in the FacebookSDK docs that I can re-request access to the user's email one time. According to the FB docs: as given in this link
If someone has declined a permission for your app, the login dialog won't let your app re-request the permission unless you pass auth_type=rerequest along with your request.
Enabling Re-authentication
During your chosen login flow we showed you how to use the Login
Dialog and OAuth to authenticate someone and request permissions from
them. To re-authenticate, you can use these same steps with additional
parameters to force it:
auth_type: this parameter specifies the requested authentication
features (as a comma-separated list). Valid options are:
https - checks for the presence of a secure Facebook session and asks for re-authentication if it is not present
reauthenticate - asks the person to re-authenticate unconditionally
auth_nonce: includes an app generated alphanumeric nonce which can be used to provide replay protection. See how to check an auth_nonce
for more.
Here is an example using the JavaScript SDK that triggers
re-authentication using an auth_type of reauthenticate:
FB.login(function(response) {
// Original FB.login code }, { auth_type: 'reauthenticate' })
Note that the body of the response contains the auth_type parameter
you specified, for example:
access_token=USER_ACCESS_TOKEN&expires=SECONDS_UNTIL_TOKEN_EXPIRES&auth_type=reauthenticate
How do I pass "auth_type=rerequest" to Facebook's servers via the iOS SDK? Is there a special method for that?
I resolved issue by recalling the method :
[login logInWithReadPermissions:#[#"email"] handler:^(FBSDKLoginManagerLoginResult *result, NSError *error)
I need to pass auth_type: 'rerequest' while requesting again and i get it in the documentation of this method i get the description :
Use this method when asking for read permissions. You should only ask
for permissions when they are needed and explain the value to the
user. You can inspect the result.declinedPermissions to also provide
more information to the user if they decline permissions.
If [FBSDKAccessToken currentAccessToken] is not nil, it will be
treated as a reauthorization for that user and will pass the
"rerequest" flag to the login dialog.
just the problem was i was calling logout Method of FbSDKLoginManager class. This will clear the token and it will takes it as the old permission not re-requested permission.
You actually don't need to pass or call auth_type:rerequest. This is already done by facebook. If you check FBSDKLoginManager, the code does it for you.
-(NSDictionary *)logInParametersWithPermissions:(NSSet *)permissions
{
if ([FBSDKAccessToken currentAccessToken])
{
loginParams[#"auth_type"] = #"rerequest";
}
}
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?
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.
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()
}
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
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())
}
})
}
You can use the following code to solve your problem :
[FBSDKAccessToken refreshCurrentAccessToken:^(FBSDKGraphRequestConnection *connection, id result, NSError *error){}
swift 4.0, 4.2, 5.0
FBSDKLoginManager().logOut()
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.
Swift 4.0 and FBSDKCoreKit 4.33
let loginManager = LoginManager()
if let currentAccessToken = AccessToken.current, currentAccessToken.appId != SDKSettings.appId
{
loginManager.logOut()
}
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:
you get the user info from facebook fail.
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.
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
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/.
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
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/
304 indicates that nothing has changed. Probably you are somehow already authenticated and so getting nil with 304 indicating nothing has changed.