In the Parse docs, it says that you can check if a user is linked to a Facebook account, but is there any way to do it vice versa? For example, suppose the user clicks a button to sign in with Facebook, since [PFUser currentUser] is nil (because the user hasn't logged in yet), Parse will create a NEW account for that user since you can't use [PFFacebookUtils isLinkedWithUser: [PFUser currentUser]]. It seems like the only way to link an account is after the user has logged in regularly(i.e. entering their username and password), but that's really inconvenient. What if the user has already created an accounted manually (by filling out a the registration form I provide) and then decides to link their Facebook account? SO is there anyway to check if a Facebook account is linked with a user in general rather than checking it against a specific user?
Ok here is the solution. Basically iterate through every user and see if Facebook account is linked with that. based on that, link or create a new account. Here is code:
PFQuery *userQuery = [PFUser query];
[userQuery findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
NSUInteger count = [objects count];
BOOL linked = NO;
for(NSUInteger i = 0; i < count; i++){
PFUser *checkUser = objects[i];
if([PFFacebookUtils isLinkedWithUser:checkUser]){
linked = YES;
NSLog(#"profile is linked to a user");
break;
}
}
if(linked == YES){
[PFFacebookUtils logInWithPermissions:_permissions block:^(PFUser *user, NSError *error) {
if(!user){
NSLog(#"facebook cancelled");
}
else{
_type = #"twitter";
[self performSegueWithIdentifier:#"showMain" sender:self]; }
}
];
}
else{
CCAlertView *ask = [[CCAlertView alloc]initWithTitle:#"Are you already registered" message:nil];
[ask addButtonWithTitle:#"No" block:^{
[PFFacebookUtils logInWithPermissions:_permissions block:^(PFUser *user, NSError *error) {
if(!user){
NSLog(#"Facebook cancelled");
}
else if(user.isNew){
_type = #"facebook";
[self performSegueWithIdentifier:#"showMain" sender:self]; }
else{
[self performSegueWithIdentifier:#"showMain" sender:self]; }
}];
}];
[ask addButtonWithTitle:#"Yes" block:^{
CCAlertView *log = [[CCAlertView alloc]initWithTitle:#"Enter username and password" message:nil];
[log setAlertViewStyle:UIAlertViewStyleLoginAndPasswordInput];
UITextField *un = [log textFieldAtIndex:0];
UITextField *pw = [log textFieldAtIndex:1];
[log addButtonWithTitle:#"Cancel" block:nil];
[log addButtonWithTitle:#"Enter" block:^{
[PFUser logInWithUsernameInBackground:[un text] password:[pw text] block:^(PFUser *user, NSError *error) {
if(user){
[PFFacebookUtils linkUser:user permissions:_permissions block:^(BOOL succeeded, NSError *error) {
if(succeeded){
NSLog(#"linked");
[self performSegueWithIdentifier:#"showMain" sender:self]; }
else{
NSLog(#"couldnt link");
}
}];
}
else{
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"Oops!" message:[error userInfo] delegate:self cancelButtonTitle:nil otherButtonTitles:#"okay", nil];
[alert show];
}
}];
}];
[log show];
}];
[ask show];
}
}];
Related
I'm using Parse.com + Facebook user and I'm noticing something unusual that has not been happening previously. A user creates an account via Facebook, logs in, logs out, logs in, logs out, and then when they try logging in, that user gets deleted and another user gets created. Why could that be?
Here is my signup/signin code:
-(IBAction)facebookSignIn:(id)sender{
CLGeocoder *geo = [[CLGeocoder alloc] init];
if(![CLLocationManager locationServicesEnabled] || [CLLocationManager authorizationStatus] == kCLAuthorizationStatusDenied ){
UIAlertView *locationAlert = [[UIAlertView alloc]initWithTitle:#"Oops!" message:#"You must have location services enabled for this app to work properly" delegate:self cancelButtonTitle:nil otherButtonTitles:#"Okay", nil];
[locationAlert show];
}else{
[PFFacebookUtils logInWithPermissions:_permissions block:^(PFUser *aUser, NSError *suError) {
if(!aUser){
NSLog(#"not fbook user because %#",[suError description]);
if([[[suError userInfo] objectForKey:#"com.facebook.sdk:ErrorLoginFailedReason"] isEqualToString:#"com.facebook.sdk:SystemLoginDisallowedWithoutError"]){
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"Error" message:#"Looks like you have Facebook authentication disabled! Go to Settings > Facebook > mySwapp and turn the switch ON"delegate:nil cancelButtonTitle:nil otherButtonTitles:#"Okay", nil];
[alert show];
}
else{
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"Error Signing In/Logging In" message:[suError localizedDescription] delegate:nil cancelButtonTitle:nil otherButtonTitles:#"Okay", nil];
[alert show];
}
}
else if(aUser.isNew){
NSLog(#"User is NEW");
[[FBRequest requestForMe] startWithCompletionHandler:
^(FBRequestConnection *connection,
NSDictionary<FBGraphUser> *user,
NSError *fbError) {
if (!fbError) {
NSLog(#"Facebook Request succeeded");
NSString *email = [user objectForKey:#"email"];
[aUser setEmail:email];
PFQuery *g = [PFQuery queryWithClassName:#"Counter"];
PFObject *cool = [g getObjectWithId:#"gpKDgNhwhw"];
[cool incrementKey:#"users"];
[cool saveEventually];
NSString *username = [NSString stringWithFormat:#"blahblah%d",[[cool objectForKey:#"users"] intValue]];
[aUser setUsername:username];
PFInstallation *installation = [PFInstallation currentInstallation];
[installation setObject:aUser forKey:#"user"];
[aUser setObject:#NO forKey:#"text"];
[aUser setObject:#YES forKey:#"snew"];
[aUser setObject:#"All" forKey:#"prefState"];
[aUser setObject:#"All" forKey:#"prefCat"];
[aUser setObject:#YES forKey:#"fnew"];
_type = #"facebook";
NSLog(#"Right before geopoint search....");
[PFGeoPoint geoPointForCurrentLocationInBackground:^(PFGeoPoint *geoPoint, NSError *error) {
if(!error){
NSLog(#"Got current geopoint!");
CLLocation *myLocation = [[CLLocation alloc]initWithLatitude:geoPoint.latitude longitude:geoPoint.longitude];
[geo reverseGeocodeLocation:myLocation completionHandler:^(NSArray *placemarks, NSError *error) {
if(!error){
CLPlacemark *pl = placemarks[0];
NSString *zip = [pl.addressDictionary objectForKey:(NSString *)kABPersonAddressZIPKey];
NSString *city = [pl.addressDictionary objectForKey:(NSString *)kABPersonAddressCityKey];
NSString *state = [pl.addressDictionary objectForKey:(NSString *)kABPersonAddressStateKey];
if(city == nil ||state ==nil){
NSLog(#"city or state is nil");
if(city==nil){
NSLog(#"city is nil");
}
if(state==nil){
NSLog(#"state is nil");
}
}
[aUser setObject:city forKey:#"city"];
[aUser setObject:state forKey:#"state"];
[aUser setObject:zip forKey:#"zip"];
[aUser setObject:geoPoint forKey:#"geopoint"];
[aUser setObject:#NO forKey:#"pref"];
[aUser setObject:#20 forKey:#"radius"];
[aUser setObject:#0 forKey:#"postCount"];
[aUser saveInBackgroundWithBlock:^(BOOL succeeded, NSError *perror) {
if(!perror && succeeded){
[self performSegueWithIdentifier:#"registerMe" sender:self]; }
else{
CCAlertView *errorAlert = [[CCAlertView alloc]initWithTitle:#"Oops!" message:[NSString stringWithFormat:#"%#. If you have already registered, please login regularly and go to the settings tab and switch on \"Link to Facebook\".",[[perror userInfo] objectForKey:#"error"]]];
[errorAlert addButtonWithTitle:#"Okay" block:^{
[aUser deleteInBackground];
}];
[errorAlert show];
}
}];
}
else{
CCAlertView *errorAlert = [[CCAlertView alloc]initWithTitle:#"Error getting Facebook data" message:[[fbError userInfo] objectForKey:#"error"]];
[errorAlert addButtonWithTitle:#"Okay" block:^{
[aUser deleteInBackground];
}];
[errorAlert show];
}
}];
}
else{
CCAlertView *errorAlert = [[CCAlertView alloc]initWithTitle:#"Facebook Sign In/Sign Up" message:[[suError userInfo] objectForKey:#"error"]];
[errorAlert addButtonWithTitle:#"Okay" block:^{
[aUser deleteInBackground];
}];
[errorAlert show];
NSString *ciid = [[PFInstallation currentInstallation] objectId];
[PFCloud callFunctionInBackground:#"logError" withParameters:#{#"installation":ciid,#"message":[suError description],#"place":#"Facebook Sign In/Sign Up"} block:^(id object, NSError *error) {
if(error){
PFObject * errorObj = [PFObject objectWithClassName:#"Error"];
[errorObj setObject:ciid forKey:#"installation"];
[errorObj setObject:[suError description] forKey:#"message"];
[errorObj setObject:#"Facebook Sign In/Sign Up" forKey:#"place"];
[errorObj saveEventually];
}
}];
}
}];
}
}];
}
else{
NSLog(#"User is OLD");
[self performSegueWithIdentifier:#"showMain" sender:self]; }
}];
}
}
and here is my logout code:
- (IBAction)goBackNow:(id)sender {
NSLog(#"gobacknow called");
[PFUser logOut];
[self.navigationController popToRootViewControllerAnimated:YES];
}
You can try to add more stuff to logout method. e.g.
[FBSession.activeSession closeAndClearTokenInformation];
[FBSession.activeSession close];
[FBSession setActiveSession:nil];
PFInstallation *installation = [PFInstallation currentInstallation];
installation[#"user"] = [NSNull null];
[installation saveInBackground];
Also remember to [installation saveInBackground]; in your example
Below is my code for phone number verification. First we query to check if user already exists; if so, I want to log in with that user. If not, I create a new PFUser.
- (IBAction)confirmButtonTapped:(id)sender
{
// Check to see if a valid phone number is entered
if ([self.phoneNumberTextField.text hasPrefix:#"+56"] && self.phoneNumberTextField.text.length == 12){
self.phoneNumber = [self phoneNumberWithoutSpaces:self.phoneNumberTextField.text];
// Check if phone number is already assigned to a Parse user
PFQuery *phoneNumberQuery = [PFUser query];
[phoneNumberQuery whereKey:#"phoneNumber" equalTo:self.phoneNumber];
[phoneNumberQuery findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error){
if (!error){
if (objects.count > 0){
// Move on to main tab bar and log in with user found
PFUser *user = [objects objectAtIndex:0];
[PFUser logInWithUsernameInBackground:user.username password:user.password block:^(PFUser *user, NSError *error){
if (!error){
[[NSUserDefaults standardUserDefaults] setValue:[NSNumber numberWithBool:YES] forKey:#"recoverUser"];
[[NSUserDefaults standardUserDefaults] synchronize];
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:self.phoneNumber, #"phoneNumber", nil];
[PFCloud callFunctionInBackground:#"sendVerificationCode" withParameters:dict block:^(id object, NSError *error){
if (error){
NSLog(#"Error calling cloud function: %#", error.localizedDescription);
}
}];
[self performSegueWithIdentifier:#"confirmNumberSegue" sender:self];
} else {
NSLog(#"Error logging in: %#", error.localizedDescription);
[[[UIAlertView alloc] initWithTitle:#"Connection Failed" message:#"Please try again." delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil] show];
}
}];
} else {
[[NSUserDefaults standardUserDefaults] setValue:[NSNumber numberWithBool:NO] forKey:#"recoverUser"];
[[NSUserDefaults standardUserDefaults] synchronize];
//Create new Parse user
[self createNewUser];
}
} else {
NSLog(#"Error fetching user object: %#", error.localizedDescription);
[[[UIAlertView alloc] initWithTitle:#"Connection Failed" message:#"Please try again." delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil] show];
}
}];
} else {
[[[UIAlertView alloc] initWithTitle:#"Invalid Phone Number" message:#"Please enter a valid phone number." delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil] show];
}
}
I am getting a Parse error when trying to log in (Code: 201) Missing user password.
I have setup a login with Facebook button using the Facebook API a week ago and today I am unable to login or sign-up at all with Facebook. I constantly get the error message: One Take[49749:60b] Uh oh. The user cancelled the Facebook login.
Here is the code that handles signing up/login with Facebook.
- (IBAction)fbButtonPressed:(id)sender
{
NSArray *permissions =#[#"public_profile", #"email", #"user_friends"];
[PFFacebookUtils logInWithPermissions:permissions block:^(PFUser *user, NSError *error) {
if (error) {
NSLog(#"Uh oh. The user cancelled the Facebook login.");
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Log In Error" message:#"Uh oh. The user cancelled the Facebook login." delegate:nil cancelButtonTitle:nil otherButtonTitles:#"Dismiss", nil];
[alert show];
} else {
NSLog(#"User signed up and logged in through Facebook!");
FBRequest *request = [FBRequest requestForMe];
[request startWithCompletionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
if (!error){
NSDictionary *userData = (NSDictionary *)result;
NSLog(#"%#", userData);
NSString *facebookID= userData[#"ID"];
NSURL *pictureURL = [NSURL URLWithString:[NSString stringWithFormat:#"https://graph.facebook.com/%#/picture?type=large&return_ssl_resources=1", facebookID]];
NSMutableDictionary *userProfile = [NSMutableDictionary dictionaryWithCapacity:7];
if (facebookID){
userProfile[#"facebookID"] = facebookID;
}
if (userData[#"name"]) {
user.username = userData[#"name"];
}
if (userData[#"email"]){
user.email = userData[#"email"];
}
/*
if ([pictureURL absoluteString]){
userProfile[#"pictureURL"] = [pictureURL absoluteString];
[user setObject:pictureURL forKey:#"profile_picture"];
[user saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (error){
NSLog(#"ERROR: %# %#", error, [error userInfo]);
}
}];
}
[[PFUser currentUser] saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (error){
NSLog(#"ERROR: %# %#", error, [error userInfo]);
}else{
[self.navigationController popToRootViewControllerAnimated:YES];
}
}];
*/
}
}];
}];
}
The way that I have setup the App is that the HomeViewController is the first screen that is to be displayed and within the viewDidLoad and ViewDidAppear it checks to see if the user is a current user. THe reason for the code being in two places is because if I logout of the app and login again, it will assume the current user is the now logged out user and the app will now display information that should not necessarily be seen by the new user.
Here is the HomeViewController.
- (void)viewDidLoad
{
self.accepted = [NSMutableArray new];
self.currentUser = [PFUser currentUser];
[super viewDidLoad];
if ((self.currentUser = [PFUser currentUser])){
NSLog(#" Current User:%#", self.currentUser.username );
}else if ([PFUser currentUser] && [PFFacebookUtils isLinkedWithUser:[PFUser currentUser]]){
NSLog(#"current User: %# is also linked W/FB!", self.currentUser.username);
}else{
[self performSegueWithIdentifier:#"showLogin" sender:self];
}
backgroundQueue = dispatch_queue_create("Word", NULL);
background2 = dispatch_queue_create("Hello", NULL);
}
-(void)viewWillAppear:(BOOL)animated
{
if (self.images == nil){
self.images = [[NSMutableArray alloc]init];
}
if ((self.currentUser = [PFUser currentUser])){
dispatch_async(backgroundQueue, ^{
PFQuery *messageQUery = [PFQuery queryWithClassName:#"Message"];
[messageQUery whereKey:#"recipientIds" equalTo:[[PFUser currentUser] objectId]];
[messageQUery findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (error){
NSLog(#"ERROR: %#, %#", error, [error userInfo]);
}else{
dispatch_async(dispatch_get_main_queue(), ^{
self.messages = objects;
[self.tableView reloadData];
NSLog(#"%#", self.messages);
});
}
}];
});
PFRelation *friendRelation = [self.currentUser relationForKey:#"friendRelation"];
PFQuery *acceptedQuery = [PFQuery queryWithClassName:#"FriendRequest"];
PFQuery *userQuery = [PFUser query];
//PFQuery *compoudQuery = [PFQuery orQueryWithSubqueries:#[acceptedQuery, userQuery]];
[acceptedQuery whereKey:#"status" equalTo:#"Accepted"];
[acceptedQuery whereKey:#"fromUser" equalTo:self.currentUser.objectId];
[userQuery whereKey:#"objectId" matchesKey:#"toUser" inQuery:acceptedQuery];
[userQuery whereKey:#"objectId" matchesKey:#"objectId" inQuery:acceptedQuery];
[userQuery findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (error){
NSLog(#" ERROR: %# %#", error, [error userInfo]);
}else{
[self.accepted addObjectsFromArray:objects];
NSLog(#"%#" , objects);
[self.tableView reloadData];
for (PFUser *user in self.accepted){
[friendRelation addObject:user];
[self.accepted removeObject:user];
if (self.accepted.count == 0){
[self.currentUser saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (error){
NSLog(#"ERROR: %# %#", error, [error userInfo]);
}
}];
}
}
}
}];
}else{
[self performSegueWithIdentifier:#"showLogin" sender:self];
}
[super viewWillAppear:animated];
}
The homeViewController does more than just check the status of those using the app, but I did display the methods there that can potentially have an impact with the problem I am experiencing.
EDIT: running the Parse FB integration tutorial returns this error:
2014-05-20 23:36:18.852 IntegratingFacebookTutorial[179:60b] Uh oh. An error occurred: Error Domain=com.facebook.sdk Code=2 "The operation couldn’t be completed. (com.facebook.sdk error 2.)" UserInfo=0x165ab770 {com.facebook.sdk:ErrorLoginFailedReason=com.facebook.sdk:SystemLoginCancelled, com.facebook.sdk:ErrorInnerErrorKey=Error Domain=com.apple.accounts Code=7 "The Facebook server could not fulfill this access request: Error validating access token: The session has been invalidated because the user has changed the password." UserInfo=0x165c1700 {NSLocalizedDescription=The Facebook server could not fulfill this access request: Error validating access token: The session has been invalidated because the user has changed the password.}, com.facebook.sdk:ErrorSessionKey=, expirationDate: (null), refreshDate: (null), attemptedRefreshDate: 0001-12-30 00:00:00 +0000, permissions:(null)>}
I have recently had to change my FB name, but not my password.
After logging in with Facebook SDK. I am able to create a new PFUser but the e-mail address of this user does not save in my Databrowser. How can I get this done?
Action for the button:
- (IBAction)didTapFb:(id)sender {
[activityIndicator startAnimating];
PFUser *user = [PFUser currentUser];
if (![PFFacebookUtils isLinkedWithUser:user]) {
[PFFacebookUtils linkUser:user permissions:nil block:^(BOOL succeeded, NSError *error) {
if (succeeded) {
NSLog(#"Woohoo, user logged in with Facebook!");
}
}];
NSArray *permissionsArray = #[ #"user_about_me", #"user_relationships", #"user_birthday", #"user_location", #"email"];
[PFFacebookUtils logInWithPermissions:permissionsArray block:^(PFUser *user, NSError *error) {
if (!user) {
if (!error) {
NSLog(#"Uh oh. The user cancelled the Facebook login.");
} else {
NSLog(#"Uh oh. An error occurred: %#", error);
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:nil message:#"Error en conexión." delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
}
} else if (user.isNew) {
NSLog(#"User with facebook signed up and logged in!");
[self performSegueWithIdentifier:#"MaintoLook" sender:self];
} else {
NSLog(#"User with facebook logged in!");
[self performSegueWithIdentifier:#"MaintoLook" sender:self];
[activityIndicator stopAnimating];
}
}];
}
}
I'm implementing the Parse / Facebook login for iOS and it's working well - I'm logged in with my own FB user and everything is fine.
But now I want to log in to my iOS application from a different FB user - a tester's account. This is what I did:
Went to my FB application on my iPhone, logged out, and logged in again with my Test account.
Went back to my iOS / Parse application and attempted to Login With Facebook.
The result is that I'm logged in with my own account!
I uninstalled the app, and re-run it from xCode, logged in with Facebook - and I'm back in using my own account again.
No matter what I do, I keep going back to my own account. It looks like Parse, or FB, is cacheing my info somehow. How do I un-cache it to get a fresh start?
This is my login method:
- (void)loginWithFB {
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.");
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Log In Error" message:#"Uh oh. The user cancelled the Facebook login." delegate:nil cancelButtonTitle:nil otherButtonTitles:#"Dismiss", nil];
[alert show];
} else {
NSLog(#"Uh oh. An error occurred: %#", error);
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Log In Error" message:[error description] delegate:nil cancelButtonTitle:nil otherButtonTitles:#"Dismiss", nil];
[alert show];
}
} else if (user.isNew) {
NSLog(#"User with facebook signed up and logged in!");
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
appDelegate.window.rootViewController = appDelegate.tabBarController;
[appDelegate.tabBarController setSelectedIndex:3];
[self.navigationController pushViewController:[[MyTrack alloc] init] animated:YES];
} else {
NSLog(#"User with facebook logged in!");
FBRequest *request = [FBRequest requestForMe];
[request startWithCompletionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
if (!error) {
NSString *facebookUsername = [result objectForKey:#"username"];
[PFUser currentUser].username = facebookUsername;
[[PFUser currentUser] saveEventually];
NSLog(#"Welcome Screen I am %#", [[PFUser currentUser] username]);
} else {
NSLog(#"Error getting the FB username %#", [error description]);
}
}];
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
appDelegate.window.rootViewController = appDelegate.tabBarController;
[appDelegate.tabBarController setSelectedIndex:0];
}
}];
[_activityIndicator startAnimating]; // Show loading indicator until login is finished
}
Please try this when you logout
[FBSession.activeSession close];
[FBSession.activeSession closeAndClearTokenInformation];
FBSession.activeSession = nil;