Linking Parse and Facebook causes Facebook session to close - ios

I am using Parse for my backend on my iOS application, and I am trying to use a FBLoginView to allow users to login with Facebook.
I am also trying to link the user's Facebook account to their Parse account. When I attempt to link the user to their FB by using
if (![PFFacebookUtils isLinkedWithUser:user]) {
[PFFacebookUtils linkUser:user permissions:nil block:^(BOOL succeeded, NSError *error) {
if (succeeded) {
NSLog(#"Woohoo, user logged in with Facebook!");
}
}];
}
I receive an error telling me that the Facebook session is invalid. I have determined that the above code seems to be closing the Facebook session (when I comment out the code, the session does not close) and giving me the error. Does anyone have any experience with this error?

Do your other apps use Parse for the backend?
The user state stored in PFFacebookUtils is different from the user returned from the FBLoginView.
If your code looks like this:
#pragma mark FBLoginViewDelegate {
- (void)loginViewFetchedUserInfo:(FBLoginView *)loginView
user:(id<FBGraphUser>)user {
// link current user
NSLog(#"User: %#", user);
if (![PFFacebookUtils isLinkedWithUser:user]) {
[PFFacebookUtils linkUser:user permissions:nil block:^(BOOL succeeded, NSError *error) {
if (succeeded) {
NSLog(#"Woohoo, user logged in with Facebook!");
}
}];
}
}
Then you are currently linking a Facebook user object with a PFUser, which are incompatible. I would suggest using the data that the FBLoginView returns, and creating a parse user in the conventional way:
#pragma mark FBLoginViewDelegate {
- (void)loginViewFetchedUserInfo:(FBLoginView *)loginView
user:(id<FBGraphUser>)user {
// link current user
NSLog(#"User: %#", user);
NSString *username = user[#"email"];
NSString *password = user[#"id"];
[PFUser logInWithUsernameInBackground:username password:password block:^(PFUser *user, NSError *error) {
NSLog(#"User: %#", user);
if (error.code == 101) {
// sign up
PFUser *user = [PFUser user];
user.username = username;
user.password = password;
[user signUpInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
NSLog(#"Error: %#", error);
}];
}
}];
}
Now [PFUser currentUser] will return a parse user object, that was created based on your Facebook login. If the app is deleted/reinstalled, the next time the user enters the app, the FBLoginView will return a userid that will be used to log in to parse as an existing user.
Edit: A similar issue I've had is with using FBLogin, PFFacebookUtils, and any other recent facebook app logins if my phone has Facebook integrated into iOS settings. It seems to have something to do with the Apple integration of Facebook, and the new Facebook SDKs. It's annoying but I have to currently tell my users to delete their facebook information from the Settings, then they can login, using either PFFacebookUtils, or the process above. I hope this gets resolved soon.

for the first part of your question i'd found a way to login user into my app by just using Facebook with Parse. Actually once the users use this, you can check the data browser of your app and check the auth data filed and the profile filed with all the fb data that i did request within the app.
here is the code i use to login with fb, haven't linked a previous user with a fb account
- (IBAction)loginButtonTouchHandler:(id)sender {
// The permissions requested from the user
NSArray *permissionsArray = #[ #"user_about_me", #"user_relationships", #"user_birthday", #"user_location"];
// Login PFUser using Facebook
[PFFacebookUtils logInWithPermissions:permissionsArray block:^(PFUser *user, NSError *error) {
//[_activityIndicator stopAnimating]; // Hide loading indicator
if (!user) {
if (!error) {
NSLog(#"Uh oh. The user cancelled the Facebook login.");
} else {
NSLog(#"Uh oh. An error occurred: %#", error);
}
} else if (user.isNew) {
NSLog(#"User with facebook signed up and logged in!");
//Go to your next view
} else {
NSLog(#"User with facebook logged in!");
//Go to your next view
}
}];
}
Hope this helps!

Related

Error when using Parse to log into Facebook

I'm trying to implement Facebook login with Parse and I get the following error message:
[Error]: Permission denied for this action. (Code: 119, Version: 1.14.2)
I have already added my application's Facebook Application ID to my Parse application's settings page, linked my app to the Facebook SDK, and added the FacebookAppID and URL Scheme values to my .plist file.
This is the code I'm using to try and login (directly from Parse).
http://parseplatform.github.io/docs/ios/guide/#log-in--sign-up
NSArray *permission = [NSArray arrayWithObjects:#"public_profile", #"email", #"user_friends", nil];
[PFFacebookUtils logInInBackgroundWithReadPermissions:permission block:^(PFUser *user, NSError *error) {
NSLog(#"Error: %#", error.description);
if (user) {
NSLog(#"Uh oh. The user cancelled the Facebook login.");
} else if (user.isNew) {
[self performSegueWithIdentifier:#"signInSuccessful" sender:nil];
NSLog(#"User signed up and logged in through Facebook!");
} else {
[self performSegueWithIdentifier:#"signInSuccessful" sender:nil];
NSLog(#"User logged in through Facebook!");
}
}];
what i found was that the logInInBackgroundWithReadPermissions method just wasn't working for some odd reason. You have to first get the token from Facebook using their standard method, then use the token info to login the user to your parse database. The below is a link to a solution for the problem answered in a related question.
https://stackoverflow.com/a/38936913/2472466

How to merge a user signed up by facebook with an exising user signed up with the same email

I am using parse in an iOS app and there are two ways of signup, either using email/password or Facebook login.
The problem happen when a user sign up using his email/password then logout and try to sign up using his Facebook account which have the same email.
I am using [PFFacebookUtils logInInBackgroundWithReadPermissions:block: to signup with Facebook which creates a new user object in the Users table in Parse
Now I have two records for the same user and I can not update the record that has the Facebook information with the user email because Parse will not allow duplicate emails
So what should be the best solution to solve this problem?
UPDATE
I have used #kRiZ solution to login using plain Facebook code then either create new user or link the user with the Facebook data
- (void)loginWithFacebookWithSuccessCallback:(APISuccessCallback)successCallback
andFailureCallback:(APIFailureCallback)failureCallback {
// Login PFUser using Facebook
NSArray *permissionsArray = #[#"public_profile", #"email"];
FBSDKLoginManager *login = [[FBSDKLoginManager alloc] init];
[login logInWithReadPermissions: permissionsArray handler:^(FBSDKLoginManagerLoginResult *result, NSError *error) {
if (error) {
failureCallback(error);
} else {
FBSDKGraphRequest *request = [[FBSDKGraphRequest alloc] initWithGraphPath:#"me?fields=id,first_name,last_name,email,gender" parameters:nil];
[request startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id fbResult, NSError *error) {
if (!error) {
NSString *email = result[#"email"];
User *existingUser = [self getUserByEmail:email];
if (existingUser == nil) {
[self signUpNewUserWithEmail:email];
} else {
// Need to set the current user to existingUser
}
[self linkCurrentWithAccessToken:result.token
successCallback(#{RESULT:RESULT_OK});
} else {
failureCallback(error);
}
}];
}
}];
}
Now the problem is to assign the [PFUser currentUser] to the existing user in case it is already exists
You can try linking the new Facebook user to an existing user on Parse using the [PFFacebookUtils linkUserInBackground:*... methods.
if (![PFFacebookUtils isLinkedWithUser:user]) {
[PFFacebookUtils linkUserInBackground:user withReadPermissions:nil block:^(BOOL succeeded, NSError *error) {
if (succeeded) {
NSLog(#"Woohoo, user is linked with Facebook!");
}
}];
}
See Parse documentation on linking.
Login via Facebook (do not save user yet)
Query the user table for user having the same email as this new FB user
If found, link, else, save as new user.
UPDATE:
Linking method with FB access token:
[PFFacebookUtils linkUserInBackground:user
withAccessToken:accessToken
block:^(BOOL succeeded, NSError *error) {
if (succeeded) {
NSLog(#"Woohoo, the user is linked with Facebook!");
}
}];

PFUser currentUser returns null after a few days

I'm seeing a strange problem with what appear to be invalid Parse User sessions in my app. I currently use Facebook logins in conjunction with Parse to handle new users in my app.
Using the boilerplate code from Parse tutorials here after two days or so a call to [PFUser currentUser] returns null and my users are asked to login again.
To try to determine if it's related to Parse or Facebook I did the following by attempting to login with a valid Facebook Session when the [PFuser CurrentUser] returns null and it always seems to work.
This makes me think it's not the Facebook session but somehow that the Parse session gets corrupted or returns something strange from time to time. I'm sure I'm not logging out as my logout method is user driven and not called anywhere else in code.
Here's what I'm doing for authorization:
-(void)authorizeAndSaveUser{
//Check to see if we already have a valid PFUser to avoid authenticating again:
if(![PFUser currentUser]){
//*Patch - occasionally the [PFUser CurrentUser] returns null when we have a logged in user.
//My work around for this is to login with the Facebook Token if we have one
if([FBSDKAccessToken currentAccessToken]){ //<- This always succeeds after the user has been created!
// Log In (create/update currentUser) with FBSDKAccessToken
[PFFacebookUtils logInInBackgroundWithAccessToken:[FBSDKAccessToken currentAccessToken]
block:^(PFUser *user, NSError *error) {
if (!user) {
NSLog(#"Uh oh. There was an error logging in with saved FB access token.");
} else {
NSLog(#"Successful login with saved FB access token.");
}
}];
} else { // We have never logged in before so start creating a new user
//Ask for permissions
NSArray *permissionsArray = #[#"public_profile", #"email"];
// Login PFUser using Facebook
[PFFacebookUtils logInInBackgroundWithReadPermissions:permissionsArray block:^(PFUser *user, NSError *error){
if(!error){
if (!user) { //Case where the user cancelled the login
NSLog(#"Uh oh. The user cancelled the Facebook login.");
//The no error case - simply a cancelled login
} else if (user.isNew) { //Net new user
NSLog(#"New User signed up and logged in through Facebook!");
//We have permissions so query Facebook and Write this Data to our App.
[self writeFacebookUserDetailsToParse];
} else { //Returning User
NSLog(#"Existing User logged in through Facebook!");
//We have permissions so query Facebook and Write this Data to our App in order to update profile photos etc.
[self writeFacebookUserDetailsToParse];
}
} else { //The error case
NSLog(#"Uh oh. An error occurred: %#", error);
}
}];
}
}
}
Can anyone see anything obvious or offer any advice? Although not a constant issue it's enough of a pain that every user will encounter a popup to "Re-Login" at least once a week or so...
Thanks in advance,
Andrew
Seems that this was fixed by our friends at Parse using Framework 1.9.0. See the change-log here: https://www.parse.com/docs/downloads
"Fixed currentUser not restoring authentication when loaded from disk."
I'll confirm if I see this again
Andrew

Parse AnyPic Facebook userId is returning nil

I downloaded the Parse example AnyPic. I have started looking into it for some ideas for my new app that will use Parse.
When AnyPic first opens up it requires the user to log in with facebook. I have been able to do some testing on the simulator, but now that I have started on the device I cannot log in through facebook. When I try it stays on the log in screen and just changes the log in button to log out. I have stepped through the code and found that the facebook userID is coming back nil. All the other facebook info seems to be returning values.
NSString *accessToken = [[[FBSession activeSession] accessTokenData] accessToken];
NSDate *expirationDate = [[[FBSession activeSession] accessTokenData] expirationDate];
NSString *facebookUserId = [[[FBSession activeSession] accessTokenData] userID];
if (!accessToken || !facebookUserId) {
NSLog(#"Login failure. FB Access Token or user ID does not exist");
return;
}
The facebokUserId comes back nil, while the others have information. I have tried hard coding the facebook user ID to what the simulator gives when it succeeds. This has not worked. I have also tried substituting my own facebook app IDs into the app to see, and it still not work.
I am new with working with social stuff, is there something I'm missing to set up facebook? Or has anyone worked with this example know how to fix it or be able to skip the facebook login?
I ended up remaking a new facebook app ID. I subbed the new info into parse and the app. I also figured out that the wrong facebook account was logged in on the device. Once I logged into the facebook account that made the new facebook app ID it worked right away.
I was having this same issue when running Anypic on a device and submitted a support ticket with Facebook. They confirmed that it's an issue when signing in with the Facebook application and recommended using PFFacebookUtils logInWithPermissions as a workaround until they're able to look into it further. I changed the handleFacebookSession method within PAPLogInViewController.m to the code below and was finally able to login with a device!
- (void)handleFacebookSession {
if ([PFUser currentUser]) {
if (self.delegate && [self.delegate respondsToSelector:#selector(logInViewControllerDidLogUserIn:)]) {
[self.delegate performSelector:#selector(logInViewControllerDidLogUserIn:) withObject:[PFUser currentUser]];
}
return;
}
NSArray *permissionsArray = #[ #"public_profile",
#"user_friends",
#"email"];
self.hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
// Login PFUser using Facebook
[PFFacebookUtils logInWithPermissions:permissionsArray block:^(PFUser *user, NSError *error) {
if (!user) {
NSString *errorMessage = nil;
if (!error) {
NSLog(#"Uh oh. The user cancelled the Facebook login.");
errorMessage = #"Uh oh. The user cancelled the Facebook login.";
} else {
NSLog(#"Uh oh. An error occurred: %#", error);
errorMessage = [error localizedDescription];
}
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Log In Error"
message:errorMessage
delegate:nil
cancelButtonTitle:nil
otherButtonTitles:#"Dismiss", nil];
[alert show];
} else {
if (user.isNew) {
NSLog(#"User with facebook signed up and logged in!");
} else {
NSLog(#"User with facebook logged in!");
}
if (!error) {
[self.hud removeFromSuperview];
if (self.delegate) {
if ([self.delegate respondsToSelector:#selector(logInViewControllerDidLogUserIn:)]) {
[self.delegate performSelector:#selector(logInViewControllerDidLogUserIn:) withObject:user];
}
}
} else {
[self cancelLogIn:error];
}
}
}];
}

Using Parse, how can you login into facebook and twitter using credentials stored on an iOS device?

In my iOS app, I have it so that the user can login using Facebook or Twitter, and it's logging in just fine. The only issue is that every time the user logs out and logs back in, if they try to use Facebook or Twitter it asks them to enter in their information again. Now I know that iOS devices store the users Facebook and Twitter credentials on the phone after they've logged into their account.
So is there a way to log the user into an application using Twitter or Facebook's stored credentials on the phone, and not have to have the user enter their Facebook or Twitter credentials all the time?
Here's what I have so far.
- (NSError *) loginWithFacebook {
NSArray *permissionsArray = #[#"user_about_me", #"user_relationships", #"user_birthday", #"user_location"];
if (![PFUser currentUser] && // Check if a user is cached
![PFFacebookUtils isLinkedWithUser:[PFUser currentUser]]) // Check if user is linked to Facebook
{
[PFFacebookUtils logInWithPermissions:permissionsArray block:^(PFUser *user, NSError *error) {
// Display some sort of loading indicator
if (!user) {
if (!error) {
NSLog(#"The user cancelled the Facebook login.");
[[DEScreenManager sharedManager] stopActivitySpinner];
}
else {
NSLog(#"An error occured: %#", error);
[[DEScreenManager sharedManager] stopActivitySpinner];
}
}
else
{
NSLog(#"User with facebook signed up and logged in");
[[[[DEScreenManager getMainNavigationController] topViewController] view] setHidden:YES];
[[DEScreenManager sharedManager] gotoNextScreen];
[[DEScreenManager sharedManager] stopActivitySpinner];
// Get the Facebook Profile Picture
[self clearUserImageDefaults];
[self getFacebookProfileInformation];
}
}];
}
return nil;
}
- (NSError *) loginWithTwitter {
[PFTwitterUtils logInWithBlock:^(PFUser *user, NSError *error) {
if (!user) {
NSLog(#"Uh oh. The user cancelled the Twitter login.");
[[DEScreenManager sharedManager] stopActivitySpinner];
return;
}
else
{
[[[[DEScreenManager getMainNavigationController] topViewController] view] setHidden:YES];
[[DEScreenManager sharedManager] gotoNextScreen];
[[DEScreenManager sharedManager] stopActivitySpinner];
[[DEScreenManager sharedManager] stopActivitySpinner];
[self getTwitterProfilePicture : [PFTwitterUtils twitter].userId];
[[PFUser currentUser] setUsername:[PFTwitterUtils twitter].screenName];
[[PFUser currentUser] saveInBackground];
[self clearUserImageDefaults];
}
}];
return nil;
}
Thanks in Advance.
You need to check the facebook / Twitter settings in the device global settings app, if the user is logged in there then those details should be used and the user should not be asked to enter anything. If the user isn't logged in there then how the login process works depends on whether the native app is installed or not, and in these cases the device doesn't auto atically get connected to yhe account the user logs into. So you can't really do exactly what you want, because that might not be what the user actually wants.

Resources