Anonymous user persists in database after successful log in - ios

Using Parse.com and anonymous users enabled (w/ automatic creation) - this is the scenario:
Start the app - anonymous user #1 gets created on database
Sign up with username & password - anonymous user #1 gets converted to regular user
Delete app - reinstall & start up - anonymous user #2 gets created
This time do not sign up but rather log in - log in successful, but anonymous user #2 persists in cloud.
How do I make anonymous user #2 from step 4 to go away (completely, I don't want junk in the database)?

As long as the user is otherwise logged out and you therefore know that currentUser either contains nil or the anonymous user object, you should be able to store the anonymous user object then delete that user object upon successful login, ex:
PFUser *anonymousUser;
if ([PFUser currentUser] != nil) {
anonymousUser = [PFUser currentUser];
}
[PFUser logInWithUsernameInBackground:emailString password:passwordString block:^(PFUser* user, NSError* error){
if (user) {
if (anonymousUser)
[anonymousUser deleteInBackground];
}
}

Related

Firebase verifying email while being logged in

I have the following logic in my iOS app:
User registers
Firebase sends an email confirmation
Returns to login screen
Now if the user logs in, without verifying the email, then we have a user session and isEmailVerified is false.
I only need to check the isEmailVerified in a certain point in the app.
Also I think signing the user in, checking the field and signing the user out would be bad practise.
I'd need to reauthenticate the user, what is the best way of doing this? How can I, after the user has logged in, switch the status of isEmailVerified?
Thanks
First, you need to have the email and password to create a credential. Your user already provided this on the login page... So the email and password to persistent storage on iOS. In Android, the equivalent would be SharedPreferences.
I do not code in iOS, but this will give you the idea for the logic.
Then, when you get to that point in your app where email verified is called:
if (user.isEmailVerified) == true {
// you do not need to hold the email and password in persistent storage anymore.
// go into your persistent storage and delete the data.
} else {
// get the email and password the user saved in persistent storage.
String email = persistentStorage.getEmail();
String password = persistentStorage.getPassword();
var user = firebase.auth().currentUser;
var credentials = firebase.auth.EmailAuthProvider.credential(email, password);
user.reauthenticate(credentials);
// then, when finished reauthenticating, check whether isEmailVerified() == true;
}

Firebase Phone Auth along with account linking

I am having a problem with phone authentication credentials persistence/expiry.
My scenario is like this:
I have a guest user, that I want to link with a phone number. The flow works perfectly if an account is not registered with that phone number. But if it does exist, then I have to:
SignIn and Unlink account.
Link account.
Sign In.
This requires 3 different credentials. But credentials expire for phone authentication after it gets used once - as per my understanding from the error message :
The SMS code has expired. Please re-send the verification code to try
again.
I do not want to ask user 3 times in a row for verification code on his mobile so new credentials can be generated. Any way to make credentials stick or a way around this problem ?
I can share the code if needed but I do not think it would be of any help.
Here is what you should do:
Initialize the phone auth credential first. Try to link that credential to the guest account always. If it fails with an error "credential already in user", the error userinfo will contain a new credential. This credential can then be used to sign in to the existing phone number account. Here is an example in objective-c.
[[FIRAuth auth].currentUser linkWithCredential:credential
completion:^(FIRUser *_Nullable user,
NSError *_Nullable error) {
if (user) {
// Successfully linked credential.
return;
}
if (error.code == FIRAuthErrorCodePhoneAlreadyInUse) {
// Save guest user data.
// Sign in the user instead if applicable.
FIRAuthCredential *credential = error.userInfo[FIRAuthUpdatedCredentialKey];
[[FIRAuth auth] signInWithCredential:credential
completion:^(FIRUser *_Nullable user,
NSError *_Nullable error) {
// copy guest user data to existing phone number user.
}];
return;
}
// Other errors.
}];
You can then programmatically copy the data of the guest user to the existing user and delete the guest user.
All this can be done efficiently with one SMS sent.

Delete user in iOS using Parse login

I'm implementing parse login in iOS using swift. From a login view controller i get new users to signup via swift using just email and password. This creates a PFUser which correctly saves to Parse backend. The user is then taken to a UserDetails view controller to "complete" their signup by providing more details which gets saved to coredata (im using parse functionality just for the signup). If the user cancels however before "completing" the additional details, I want to delete the PFUser from parse created possibly moments earlier. In the UserDetails view controller under cancelTapped ibaction im running code
if PFUser.currentUser() != nil {
PFUser.currentUser()?.deleteInBackgroundWithBlock({ (deleteSuccessful, error) -> Void in
print("success = \(deleteSuccessful)")
})
//user deleted in background block above but still logged in so now logout
PFUser.logOut()
}
but im getting following parse error [Error]: User cannot be deleted unless they have been authenticated. (Code: 206, Version: 1.8.5) .... i dont know what authentication process is needed and im guessing the problem could be because im trying to delete a user before parse has had a proper time to create the user in the first place .... any help or suggestions appreciated
User ALC permission automatically blocks anyone else from performing a write/delete on your record. This means you must be logged in if you want to update/delete your own record. In your code, you probably log out before your asynchronous delete code gets a chance to run in the background. Instead you need to log the user out after the delete was successful in the completion block of deleteInBackgroundWithBlock function.
if PFUser.currentUser() != nil {
PFUser.currentUser()?.deleteInBackgroundWithBlock({ (deleteSuccessful, error) -> Void in
print("success = \(deleteSuccessful)")
PFUser.logOut()
})
}

Parse error codes 209 and -34018

I have spent several hours learning user management on Parse, thinking it would be easy considering it's been an established service for so long.
There is poor documentation around 'automatic user' and sessions.
I am trying to build an App that allows the user to exist anonymously (using [PFUser enableAutomaticUser]), before they decide to sign up.
Registration Steps:
Gather user details from the UI
Log out current automatic user and wait for success
Upon success create a user object using [PFUser user] and assign values
Call signUpInBackgroundWithBlock on the new user instance
I sometimes get the following errors (yes, only sometimes), when doing the above.
[Error]: PFKeychainStore failed to get object for key 'currentUser', with error: -34018
[Error]: invalid session token (Code: 209, Version: 1.7.0)
I also end up with a dirty database, because I don't know how to delete the automatic user that was previously created. I tried keeping the object id of the old user around and using deleteEventually but that didn't work?
Any advice on how you would go about achieving this would be great.
Take a look at this issue with parse on iOS: https://github.com/ParsePlatform/Parse-SDK-iOS-OSX/issues/437.
I believe this was an issue in iOS and is now fixed
Actually, this is a bug of keychain, you can search it at github.
Github 34018 issues
A few months ago, some apple's staff came our company to give us a course, after course,we asked this question, they also did't give us a solution
I've solved this problem by using the following:
PFUser *user = [PFUser currentUser];
[user refreshInBackgroundWithBlock:^(PFObject *object, NSError *error) {
if (!error) {
NSLog(#"Succesfully refreshed the current user.");
} else {
NSLog(#"Failed to refresh the current user with error %#", error);
}
}];
It seems that [PFUser currentUser] returns an invalid session token which is causing the 209 and -34018 errors. This is only an issue when the current user is an anonymous user.

How do I associate a Parse user with their respective installationId when they log in/sign up?

When a user of my Parse app logs in or signs up, a new User object is created in the database with username, password, and email properties, among others. What I want to do is set the User object's installationId property to be a pointer to the installationId property of that devices respective Installation object, as can be seen in the screenshot.
I've tried looking through PFUser.h to see where it tells the database to set the Users username and email properties, and thought I'd just have it set the installationId property from there as well, but I can't seem to find the place where it does this.
Screenshot:
PFUser.h:
// PFUser.h
// Copyright 2011 Parse, Inc. All rights reserved.
#import <Foundation/Foundation.h>
#import "PFConstants.h"
#import "PFObject.h"
#import "PFSubclassing.h"
#class PFQuery;
/*!
A Parse Framework User Object that is a local representation of a user persisted to the Parse cloud. This class
is a subclass of a PFObject, and retains the same functionality of a PFObject, but also extends it with various
user specific methods, like authentication, signing up, and validation uniqueness.
Many APIs responsible for linking a PFUser with Facebook or Twitter have been deprecated in favor of dedicated
utilities for each social network. See PFFacebookUtils and PFTwitterUtils for more information.
*/
#interface PFUser : PFObject<PFSubclassing>
/*! The name of the PFUser class in the REST API. This is a required
* PFSubclassing method */
+ (NSString *)parseClassName;
/** #name Accessing the Current User */
/*!
Gets the currently logged in user from disk and returns an instance of it.
#result Returns a PFUser that is the currently logged in user. If there is none, returns nil.
*/
+ (instancetype)currentUser;
/// The session token for the PFUser. This is set by the server upon successful authentication.
#property (nonatomic, retain) NSString *sessionToken;
/// Whether the PFUser was just created from a request. This is only set after a Facebook or Twitter login.
#property (readonly, assign) BOOL isNew;
/*!
Whether the user is an authenticated object for the device. An authenticated PFUser is one that is obtained via
a signUp or logIn method. An authenticated object is required in order to save (with altered values) or delete it.
#result Returns whether the user is authenticated.
*/
- (BOOL)isAuthenticated;
/** #name Creating a New User */
/*!
Creates a new PFUser object.
#result Returns a new PFUser object.
*/
+ (PFUser *)user;
/*!
Enables automatic creation of anonymous users. After calling this method, [PFUser currentUser] will always have a value.
The user will only be created on the server once the user has been saved, or once an object with a relation to that user or
an ACL that refers to the user has been saved.
Note: saveEventually will not work if an item being saved has a relation to an automatic user that has never been saved.
*/
+ (void)enableAutomaticUser;
/// The username for the PFUser.
#property (nonatomic, retain) NSString *username;
/**
The password for the PFUser. This will not be filled in from the server with
the password. It is only meant to be set.
*/
#property (nonatomic, retain) NSString *password;
/// The email for the PFUser.
#property (nonatomic, retain) NSString *email;
// The installationId for the PFUser
#property (nonatomic, retain) NSString *installationId;
/*!
Signs up the user. Make sure that password and username are set. This will also enforce that the username isn't already taken.
#result Returns true if the sign up was successful.
*/
- (BOOL)signUp;
/*!
Signs up the user. Make sure that password and username are set. This will also enforce that the username isn't already taken.
#param error Error object to set on error.
#result Returns whether the sign up was successful.
*/
- (BOOL)signUp:(NSError **)error;
/*!
Signs up the user asynchronously. Make sure that password and username are set. This will also enforce that the username isn't already taken.
*/
- (void)signUpInBackground;
/*!
Signs up the user asynchronously. Make sure that password and username are set. This will also enforce that the username isn't already taken.
#param block The block to execute. The block should have the following argument signature: (BOOL succeeded, NSError *error)
*/
- (void)signUpInBackgroundWithBlock:(PFBooleanResultBlock)block;
/*!
Signs up the user asynchronously. Make sure that password and username are set. This will also enforce that the username isn't already taken.
#param target Target object for the selector.
#param selector The selector that will be called when the asynchrounous request is complete. It should have the following signature: (void)callbackWithResult:(NSNumber *)result error:(NSError **)error. error will be nil on success and set if there was an error. [result boolValue] will tell you whether the call succeeded or not.
*/
- (void)signUpInBackgroundWithTarget:(id)target selector:(SEL)selector;
/** #name Logging in */
/*!
Makes a request to login a user with specified credentials. Returns an instance
of the successfully logged in PFUser. This will also cache the user locally so
that calls to currentUser will use the latest logged in user.
#param username The username of the user.
#param password The password of the user.
#result Returns an instance of the PFUser on success. If login failed for either wrong password or wrong username, returns nil.
*/
+ (instancetype)logInWithUsername:(NSString *)username
password:(NSString *)password;
/*!
Makes a request to login a user with specified credentials. Returns an
instance of the successfully logged in PFUser. This will also cache the user
locally so that calls to currentUser will use the latest logged in user.
#param username The username of the user.
#param password The password of the user.
#param error The error object to set on error.
#result Returns an instance of the PFUser on success. If login failed for either wrong password or wrong username, returns nil.
*/
+ (instancetype)logInWithUsername:(NSString *)username
password:(NSString *)password
error:(NSError **)error;
/*!
Makes an asynchronous request to login a user with specified credentials.
Returns an instance of the successfully logged in PFUser. This will also cache
the user locally so that calls to currentUser will use the latest logged in user.
#param username The username of the user.
#param password The password of the user.
*/
+ (void)logInWithUsernameInBackground:(NSString *)username
password:(NSString *)password;
/*!
Makes an asynchronous request to login a user with specified credentials.
Returns an instance of the successfully logged in PFUser. This will also cache
the user locally so that calls to currentUser will use the latest logged in user.
The selector for the callback should look like: myCallback:(PFUser *)user error:(NSError **)error
#param username The username of the user.
#param password The password of the user.
#param target Target object for the selector.
#param selector The selector that will be called when the asynchrounous request is complete.
*/
+ (void)logInWithUsernameInBackground:(NSString *)username
password:(NSString *)password
target:(id)target
selector:(SEL)selector;
/*!
Makes an asynchronous request to log in a user with specified credentials.
Returns an instance of the successfully logged in PFUser. This will also cache
the user locally so that calls to currentUser will use the latest logged in user.
#param username The username of the user.
#param password The password of the user.
#param block The block to execute. The block should have the following argument signature: (PFUser *user, NSError *error)
*/
+ (void)logInWithUsernameInBackground:(NSString *)username
password:(NSString *)password
block:(PFUserResultBlock)block;
/** #name Becoming a user */
/*!
Makes a request to become a user with the given session token. Returns an
instance of the successfully logged in PFUser. This also caches the user locally
so that calls to currentUser will use the latest logged in user.
#param sessionToken The session token for the user.
#result Returns an instance of the PFUser on success. If becoming a user fails due to incorrect token, it returns nil.
*/
+ (instancetype)become:(NSString *)sessionToken;
/*!
Makes a request to become a user with the given session token. Returns an
instance of the successfully logged in PFUser. This will also cache the user
locally so that calls to currentUser will use the latest logged in user.
#param sessionToken The session token for the user.
#param error The error object to set on error.
#result Returns an instance of the PFUser on success. If becoming a user fails due to incorrect token, it returns nil.
*/
+ (instancetype)become:(NSString *)sessionToken
error:(NSError **)error;
/*!
Makes an asynchronous request to become a user with the given session token. Returns an
instance of the successfully logged in PFUser. This also caches the user locally
so that calls to currentUser will use the latest logged in user.
#param sessionToken The session token for the user.
*/
+ (void)becomeInBackground:(NSString *)sessionToken;
/*!
Makes an asynchronous request to become a user with the given session token. Returns an
instance of the successfully logged in PFUser. This also caches the user locally
so that calls to currentUser will use the latest logged in user.
The selector for the callback should look like: myCallback:(PFUser *)user error:(NSError **)error
#param sessionToken The session token for the user.
#param target Target object for the selector.
#param selector The selector that will be called when the asynchrounous request is complete.
*/
+ (void)becomeInBackground:(NSString *)sessionToken
target:(id)target
selector:(SEL)selector;
/*!
Makes an asynchronous request to become a user with the given session token. Returns an
instance of the successfully logged in PFUser. This also caches the user locally
so that calls to currentUser will use the latest logged in user.
The selector for the callback should look like: myCallback:(PFUser *)user error:(NSError **)error
#param sessionToken The session token for the user.
#param block The block to execute. The block should have the following argument signature: (PFUser *user, NSError *error)
*/
+ (void)becomeInBackground:(NSString *)sessionToken
block:(PFUserResultBlock)block;
/** #name Logging Out */
/*!
Logs out the currently logged in user on disk.
*/
+ (void)logOut;
/** #name Requesting a Password Reset */
/*!
Send a password reset request for a specified email. If a user account exists with that email,
an email will be sent to that address with instructions on how to reset their password.
#param email Email of the account to send a reset password request.
#result Returns true if the reset email request is successful. False if no account was found for the email address.
*/
+ (BOOL)requestPasswordResetForEmail:(NSString *)email;
/*!
Send a password reset request for a specified email and sets an error object. If a user
account exists with that email, an email will be sent to that address with instructions
on how to reset their password.
#param email Email of the account to send a reset password request.
#param error Error object to set on error.
#result Returns true if the reset email request is successful. False if no account was found for the email address.
*/
+ (BOOL)requestPasswordResetForEmail:(NSString *)email
error:(NSError **)error;
/*!
Send a password reset request asynchronously for a specified email and sets an
error object. If a user account exists with that email, an email will be sent to
that address with instructions on how to reset their password.
#param email Email of the account to send a reset password request.
*/
+ (void)requestPasswordResetForEmailInBackground:(NSString *)email;
/*!
Send a password reset request asynchronously for a specified email and sets an error object.
If a user account exists with that email, an email will be sent to that address with instructions
on how to reset their password.
#param email Email of the account to send a reset password request.
#param target Target object for the selector.
#param selector The selector that will be called when the asynchronous request is complete. It should have the following signature: (void)callbackWithResult:(NSNumber *)result error:(NSError **)error. error will be nil on success and set if there was an error. [result boolValue] will tell you whether the call succeeded or not.
*/
+ (void)requestPasswordResetForEmailInBackground:(NSString *)email
target:(id)target
selector:(SEL)selector;
/*!
Send a password reset request asynchronously for a specified email.
If a user account exists with that email, an email will be sent to that address with instructions
on how to reset their password.
#param email Email of the account to send a reset password request.
#param block The block to execute. The block should have the following argument signature: (BOOL succeeded, NSError *error)
*/
+ (void)requestPasswordResetForEmailInBackground:(NSString *)email
block:(PFBooleanResultBlock)block;
/** #name Querying for Users */
/*!
Creates a query for PFUser objects.
*/
+ (PFQuery *)query;
#end
Because I don't know why you want to do it, I can't say what method to use is best, but I actually create a custom property of the Installation class called userId, and associate the two that way. That code looks like the following:
PFInstallation *currentInstallation = [PFInstallation currentInstallation];
[currentInstallation setObject:[PFUser currentUser].objectId forKey:#"userId"];
[currentInstallation saveInBackground];
That works for push notifications if that's your goal. If you really are set on that installationId property being part of the user class, then you could just do what I did, then query the Installation class for matching userIds, although that's bit circuitous.

Resources