Cannot Save PFObject after updated ParseFacebookUtilsV4 - ios

I updated the ParseSDK to 1.7.5 and also ParseFacebookUtilsV4 (1.7.5)
Then some our users can not save PFObject like this.
PFObject* post = [PFObject objectWithClassName:#"PostData"];
PFUser* user = [PFUser currentUser];
[post setObject:user forKey: #"user"];
...
[post saveInBackgroundWithBlock: ^(BOOL success, NSError *error){
if ( !success || error != nil ){
....
}
}];
It returns with error "user objects cannot allow writes from other users".
The user is using Facebook login and siged up in previous version's Parse SDK
(before Facebook SDK 4.0).
Even if the user deleted the app and re-installed, it's same error.
And it never happens the user signed up after ParseSDK 1.7.5.
To login with Facebook, I'm doing like this
[PFFacebookUtils logInInBackgroundWithReadPermissions: #[ #"public_profile", #"user_friends", #"email" ] block:^(PFUser *user, NSError *error){
}];
When I commented out adding user to PFObject, it succeeded.
Please give me some advice.
Thanks.
NOTE: For the quick fix, I stopped adding user on client, and use CloudCode
Parse.Cloud.beforeSave( "PostData", function(request, response) {
Parse.Cloud.useMasterKey();
var user = request.user;
var obj = request.object;
obj.set("user", user);
response.success();
});
it works...

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!");
}
}];

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?

Parse: Cannot sign up a user created automatically

I'm using Parse 1.7.1 for iOS and I am trying to sign up a user. I chose to let the user interact with the app before I force the sign up / login process. The problem is that as soon as I call signUp the session object disappears from the server and causes any subsequent calls to return error 209.
Here's some code:
AppDelegate:
[PFUser enableAutomaticUser];
[[PFUser currentUser] incrementKey:#"runCount"];
[[PFUser currentUser] saveInBackground];
When I check the Parse Core console I can see a valid User & Session.
MyController:
PFUser *user = [PFUser currentUser];
if (! user) {
user = [PFUser user];
}
user.email = #"my#email.com";
user.password = #"somepassword";
user.username = #"whatever";
// I also tried Save instead of signUpInBackground... same result
[user signUpInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (succeeded) {
NSLog(#"Success signing up");
}
else {
NSLog(#"Error %#", [error localizedDescription]);
}
}];
When I check the console there is an updated user but there is no Session anymore.
Any other API call
[Error]: invalid session token (Code: 209, Version: 1.7.1)
Despite the fact that the callback returns success as soon as I try to interact with the Parse API I get a 209, invalid session token. This is fair enough, since the session object is not present in the console.
Does anyone know what I am doing wrong?
Note: this a brand new app (not affected by the change made by Parse on the 25th of March)/.
I have raised this as a bug with Facebook and they are fixing it in 1.7.3
Here's the conversation I had with them.
Create button with selector -signUpSelector. And need implement this code:
-(void)signUpSelector
[PFUser becomeInBackground:#"session-token-here" block:^(PFUser *user, NSError *error) {
PFUser *newUser = nil;
if (error) {
newUser = [PFUser user];
} else {
newUser = user;
}
user.email = #"my#email.com";
user.password = #"somepassword";
user.username = #"whatever";
[user signUpInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (succeeded) {
NSLog(#"Success signing up");
}
else {
NSLog(#"Error %#", [error localizedDescription]);
}
}];
}];

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!

Can't write non current user objects by PFUser currentuser

I would like write a PFUser object by the currentUser, i've added the ACL based on the Parse developer guide, but i still get an error:
'User cannot be saved unless they have been authenticated via logIn or signUp'
_ My code:
PFQuery *query = [PFUser query];
[query whereKey:#"username" equalTo:self.bBo];
PFObject *friendData = [query getFirstObject];
PFUser *user = (PFUser *)friendData;
PFACL *userACL = [PFACL ACL];
user.ACL = userACL;
[userACL setWriteAccess:YES forUser:[PFUser currentUser]];
PFRelation *friendRelation = [user relationforKey:#"array"];
[friendRelation addObject:[PFUser currentUser]];
[user saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (error){
NSLog(#"Error %# %#", error, [error userInfo]);
}
}];
I think i did everything correct, so i can't figure out what can be the problem. So if you made earlier something like this or know where is the problem, i would really appreciate any suggestions.
For security reasons, Parse won't allow you to save any changes to a user that is not currently logged in.
If you want to be able to make and save changes to user, you need to use Cloud Code and the Master Key to get around this roadblock.
I have had multiple problems like this before, and every time I've been forced to use a workaround via Cloud Code.
Here's an example of a workaround I did for creating a friends relationship between two users:
[PFCloud callFunction:#"editUser" withParameters:#{#"userId": user.objectId}];
The above code statement is in xcode, and executes the function I have added to my Cloud Code file.
Then, here's what my Cloud Code file looks like:
Parse.Cloud.define('editUser', function(request, response) {
var userId = request.params.userId;
var User = Parse.Object.extend('_User'),
user = new User({ objectId: userId });
var currentUser = request.user;
var relation = user.relation("friendsRelation");
relation.add(currentUser);
Parse.Cloud.useMasterKey();
user.save().then(function(user) {
response.success(user);
}, function(error) {
response.error(error)
});
});
The above code uses the Master Key to save changes to both the currently logged in user, and to the user who's objectId was passed into this function.
Code details:
var relation
This is just a variable I'm creating to hold what the following fucntion returns:
user.relation("friendsRelation");
In the above function, "friendsRelation" is the name of my PFRelation key in Parse.
Now that we have a valid relation object contain in our variable called relation, we execute this function with an argument of our currentUser object.
After that, all that's left is saving everything. I don't program with javascript, but I was still able to come up with the above solution by looking at the Parse Cloud Code docs, and searching around on their support forums.
If you take my template from above, and make some small changes, then you should be able to easily accomplish what you need. You just have to jump through these extra hoops because the Parse SDK doesn't want a situation where someone can login and somehow make changes to another user's account, whether by their own fault or a developer's mistake.
EDIT:
Here is the code to add the relationship for the current user:
PFRelation *friendsRelation = [[PFUser currentUser]relationforKey:#"friendsRelation"];
PFUser *user = [self.parseUsers objectAtIndex:indexPath.row];
[friendsRelation addObject:user];
[currentUser saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (error) {
NSLog(#"%# %#", error, [error userInfo]);
}
}];
And then you can call the Cloud Code method right after:
[PFCloud callFunction:#"editUser" withParameters:#{
#"userId": user.objectId
}];

Resources