Parse - linking a PFUser to an email address and password - ios

OK, I'm using Parse for an app.
I currently have a Facebook login but we will also be offering Twitter and Email/Password login.
I need to make sure that whichever way the user decides to login/sign up they can link everything together so they only have one account.
I know how to link the current user to a Facebook or Twitter account...
[PFFacebookUtils linkUser:[PFUser currentUser] permissions:nil block:^(BOOL succeeded, NSError *error) {
if (succeeded) {
NSLog(#"Woohoo, user logged in with Facebook!");
}
}];
But there doesn't seem to be any information on linking the other way around.
i.e. if the user logs in with Facebook. Then how can I link that account with a email/password combo for log in?
Something like
[[PFUser currentUser] linkWithEmail:#"blah" password:#"blah" block:^(BOOL succeeded, NSError *error) {
if (succeeded) {
NSLog(#"Woohoo!");
}
}];

Related

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

How can two parse apps share PFObjects and link identities via PFUser?

I have an iPad-only app where some users (the ones who buy in-app purchases) have PFUser objects that are linked to their currentInstallation like this:
PFInstallation *currentInstallation = [PFInstallation currentInstallation];
if( [PFUser currentUser] != nil )
currentInstallation[#"user"] = [PFUser currentUser];
The identity behind the PFUser is the Facebook identity:
[PFFacebookUtils initializeFacebook];
[PFFacebookUtils logInWithPermissions:nil block:^(PFUser *user, NSError *error) {
if (!user) {
// Handle error path
failPath();
} else {
// Handle success path
[FBRequestConnection startForMeWithCompletionHandler:^(FBRequestConnection *connection, NSDictionary<FBGraphUser> *user, NSError *error) {
if (error) {
failPath();
} else {
// Save the name on Parse
[PFUser currentUser][kParseUserKeyFacebookName] = user.name;
[PFUser currentUser][kParseUserKeyFacebookID] = user.objectID;
[[PFUser currentUser] saveInBackgroundWithBlock:....
Now I want to release a new app for the iPhone, which acts as a kind of "admin console" for the iPad app. The iPhone app will receive push notifications when things go wrong on the iPad app etc (the iPad is in use by many people).
Question: On the new iPhone app, presuming I can get the user to log in with the same facebook ID, how can I correlate back to the same user's ipad data on parse? For example can I get the ipad app's PFObjects and/or PFUser data in the iphone now?
Concrete example: set a password on the ipad app, log into facebook on the iphone app, enter that same password. How to check it against the ipad app password, from the iphone?

Linking Parse and Facebook causes Facebook session to close

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!

Parse Login users without Facebook details in Data Browser

I am using [PFFacebookUtils loginWithPermissions] to let user log into Facebook in an iOS app. There is no other option for users to log-in. In the completion block I am calling [FBRequestConnection startForMeWithCompletionHandler]. In the completion block of second call I store the data obtained into the [PFUser currentUser] object (like username, email). The data browser in Parse dashboard shows empty data in email and other info received from Facebook for some of the users. I have proper conditional checks to see if the API calls are returning any errors.
This the code. There are many users with no FBID, Email and corrupted Username in Parse databrowser.
[PFFacebookUtils logInWithPermissions:nil block:^(PFUser *user, NSError *error) {
if (!user) {
UIAlertView* alert = [[UIAlertView alloc] initWithTitle:#"Facebook error" message:#"To use you Facebook account with this app, open Settings > Facebook and make sure this app is turned on." delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[alert show];
} else {
[FBRequestConnection startForMeWithCompletionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
if (!error) {
[[PFUser currentUser] setObject:result[#"id"] forKey:kPFUser_FBID];
[[PFUser currentUser] setObject:result[#"name"] forKey:kPFUser_Username];
[[PFUser currentUser] setObject:result[#"email"] forKey:kPFUser_Email];
[[PFUser currentUser] saveInBackground];
}
}];
}
}];
It looks like the block is not entering the second piece of code(i.e it is getting error from FBRequestConnection method.
Please help!
I figured what was the issue here. I was calling
[PFFacebookUtils loginWithPermissions]
with nil permissions and was trying to access email address of the user. Parse was crashing when a null value was being entered in currentUser object.
Just replaced nil with #"email" permission.
Thanks for your help.
Are you remembering to save the current user after you finish setting the properties? That's an easy thing to forget.
UPDATE:
Add an else block to log any possible error returned from the Facebook call.
Also try changing your saveInBackground to saveInBackgroundWithBlock: and check for any errors, if the data isn't showing up in the Parse Data Browser that should always be the first thing you check.

Resources