Skip to content

Commit 3fa49f5

Browse files
authored
Anonymous user upgrade federated (#401)
* Add anonymous sign in upgrade * Returns merge error in the correct place. * Adds comments and minor changes * Adds correct error handling for linking errors * Addresses comments Addresses comments Minor enhances Fixes bug where wrong error is returned
1 parent f72d1da commit 3fa49f5

File tree

6 files changed

+139
-56
lines changed

6 files changed

+139
-56
lines changed

FirebaseAuthUI/FUIAuth.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,12 @@ __attribute__((deprecated("Instead use authUI:didSignInWithAuthDataResult:error:
190190
*/
191191
@property(nonatomic, copy, nullable) NSURL *TOSURL;
192192

193+
/** @property shouldAutoUpgradeAnonymousUsers
194+
@brief Whether to enable auto upgrading of anonymous accounts, defaults to NO.
195+
*/
196+
@property(nonatomic, assign, getter=shouldAutoUpgradeAnonymousUsers) BOOL
197+
shouldAutoUpgradeAnonymousUsers;
198+
193199
/** @property delegate
194200
@brief A delegate that receives callbacks or provides custom UI for @c FUIAuth.
195201
*/

FirebaseAuthUI/FUIAuth.m

Lines changed: 68 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -174,37 +174,78 @@ - (void)signInWithProviderUI:(id<FUIAuthProvider>)providerUI
174174
return;
175175
}
176176

177-
[self.auth signInAndRetrieveDataWithCredential:credential
178-
completion:^(FIRAuthDataResult *_Nullable authResult,
179-
NSError *_Nullable error) {
180-
if (error && error.code == FIRAuthErrorCodeAccountExistsWithDifferentCredential) {
181-
NSString *email = error.userInfo[kErrorUserInfoEmailKey];
182-
[self handleAccountLinkingForEmail:email
183-
newCredential:credential
184-
presentingViewController:presentingViewController
185-
singInResult:result];
186-
return;
177+
// Block to complete sign-in
178+
void (^completeSignInBlock)(FIRAuthDataResult *, NSError *) = ^(FIRAuthDataResult *authResult,
179+
NSError *error) {
180+
if (result) {
181+
result(authResult.user, nil);
187182
}
188-
189-
if (error) {
190-
if (result) {
191-
result(nil, error);
192-
}
193-
[self invokeResultCallbackWithAuthDataResult:nil error:error];
183+
// Hide Auth Picker Controller which was presented modally.
184+
if (isAuthPickerShown && presentingViewController.presentingViewController) {
185+
[presentingViewController dismissViewControllerAnimated:YES completion:^{
186+
[self invokeResultCallbackWithAuthDataResult:authResult error:error];
187+
}];
194188
} else {
195-
if (result) {
196-
result(authResult.user, nil);
189+
[self invokeResultCallbackWithAuthDataResult:authResult error:error];
190+
}
191+
};
192+
193+
// Check for the presence of an anonymous user and whether automatic upgrade is enabled.
194+
if (_auth.currentUser.isAnonymous && [FUIAuth defaultAuthUI].shouldAutoUpgradeAnonymousUsers) {
195+
[_auth.currentUser
196+
linkAndRetrieveDataWithCredential:credential
197+
completion:^(FIRAuthDataResult *_Nullable authResult,
198+
NSError * _Nullable error) {
199+
if (error) {
200+
// Check for "credential in use" conflict error and handle appropriately.
201+
if (error.code == FIRAuthErrorCodeCredentialAlreadyInUse) {
202+
NSError *mergeError;
203+
FIRAuthCredential *newCredential = credential;
204+
// Check for and handle special case for Phone Auth Provider.
205+
if (providerUI.providerID == FIRPhoneAuthProviderID) {
206+
// Obtain temporary Phone Auth provider.
207+
newCredential = error.userInfo[FIRAuthUpdatedCredentialKey];
208+
}
209+
NSDictionary *userInfo = @{
210+
FUIAuthCredentialKey : newCredential,
211+
};
212+
mergeError = [NSError errorWithDomain:FUIAuthErrorDomain
213+
code:FUIAuthErrorCodeMergeConflict
214+
userInfo:userInfo];
215+
result(nil, mergeError);
216+
completeSignInBlock(authResult, mergeError);
217+
} else {
218+
if (!isAuthPickerShown || error.code != FUIAuthErrorCodeUserCancelledSignIn) {
219+
[self invokeResultCallbackWithAuthDataResult:nil error:error];
220+
}
221+
if (result) {
222+
result(nil, error);
223+
}
224+
}
197225
}
198-
// Hide Auth Picker Controller which was presented modally.
199-
if (isAuthPickerShown && presentingViewController.presentingViewController) {
200-
[presentingViewController dismissViewControllerAnimated:YES completion:^{
201-
[self invokeResultCallbackWithAuthDataResult:authResult error:nil];
202-
}];
203-
} else {
204-
[self invokeResultCallbackWithAuthDataResult:authResult error:nil];
226+
}];
227+
} else {
228+
[self.auth signInAndRetrieveDataWithCredential:credential
229+
completion:^(FIRAuthDataResult *_Nullable authResult,
230+
NSError *_Nullable error) {
231+
if (error && error.code == FIRAuthErrorCodeAccountExistsWithDifferentCredential) {
232+
NSString *email = error.userInfo[kErrorUserInfoEmailKey];
233+
[self handleAccountLinkingForEmail:email
234+
newCredential:credential
235+
presentingViewController:presentingViewController
236+
singInResult:result];
237+
return;
205238
}
206-
}
207-
}];
239+
if (error) {
240+
if (result) {
241+
result(nil, error);
242+
}
243+
[self invokeResultCallbackWithAuthDataResult:nil error:error];
244+
return;
245+
}
246+
completeSignInBlock(authResult, nil);
247+
}];
248+
}
208249
}];
209250
}
210251

FirebaseAuthUI/FUIAuthErrors.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,12 @@ extern NSString *const FUIAuthErrorDomain;
2828
*/
2929
extern NSString *const FUIAuthErrorUserInfoProviderIDKey;
3030

31+
/** @bar FUIAuthCredentialKey
32+
@brief The key used to obtain the credential stored within the userInfo dictionary of the
33+
error, if availalble.
34+
*/
35+
extern NSString *const FUIAuthCredentialKey;
36+
3137
/** @var FUIAuthErrorCode
3238
@brief Error codes used by FUIAuth.
3339
*/
@@ -50,6 +56,13 @@ typedef NS_ENUM(NSUInteger, FUIAuthErrorCode) {
5056
key @c FUIAuthErrorUserInfoProviderIDKey).
5157
*/
5258
FUIAuthErrorCodeCantFindProvider = 3,
59+
60+
/** @var FUIAuthErrorCodeMergeConflict
61+
@brief Indicates that a merge conflict occurred while trying to automatically upgrade an
62+
anonymous user. The non-anonymous credential can be obtained from the userInfo dictionary
63+
of the corresponding NSError using the @c FUIAuthCredentialKey.
64+
*/
65+
FUIAuthErrorCodeMergeConflict = 4,
5366
};
5467

5568
NS_ASSUME_NONNULL_END

FirebaseAuthUI/FUIAuthErrors.m

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,5 @@
1919
NSString *const FUIAuthErrorDomain = @"FUIAuthErrorDomain";
2020

2121
NSString *const FUIAuthErrorUserInfoProviderIDKey = @"FUIAuthErrorUserInfoProviderIDKey";
22+
23+
NSString *const FUIAuthCredentialKey = @"FUIAuthCredentialKey";

FirebasePhoneAuthUI/FUIPhoneVerificationViewController.m

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,9 @@ - (void)onNext:(NSString *)verificationCode {
187187
result:^(FIRUser *_Nullable user, NSError *_Nullable error) {
188188
[self decrementActivity];
189189
self.navigationItem.rightBarButtonItem.enabled = YES;
190-
if (!error || error.code == FUIAuthErrorCodeUserCancelledSignIn) {
190+
if (!error ||
191+
error.code == FUIAuthErrorCodeUserCancelledSignIn ||
192+
error.code == FUIAuthErrorCodeMergeConflict) {
191193
[self.navigationController dismissViewControllerAnimated:YES completion:nil];
192194
} else {
193195
UIAlertController *alertController = [FUIPhoneAuth alertControllerForError:error

samples/objc/FirebaseUI-demo-objc/Samples/Auth/FUIAuthViewController.m

Lines changed: 47 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -148,8 +148,18 @@ - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPa
148148

149149
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
150150
if (indexPath.section == kSectionsAnonymousSignIn && indexPath.row == 0) {
151-
if (_authUI.auth.currentUser.isAnonymous) {
152-
[self showAlertWithTitlte:@"" message:@"Already signed in anonymously"];
151+
FIRUser *currentUser = self.authUI.auth.currentUser;
152+
if (currentUser.isAnonymous) {
153+
// If the user is anonymous, delete the user to avoid dangling anonymous users.
154+
if (currentUser.isAnonymous) {
155+
[currentUser deleteWithCompletion:^(NSError * _Nullable error) {
156+
if (error) {
157+
[self showAlertWithTitlte:@"Error" message:error.localizedDescription];
158+
return;
159+
}
160+
[self showAlertWithTitlte:@"" message:@"Anonymous user deleted"];
161+
}];
162+
}
153163
[tableView deselectRowAtIndexPath:indexPath animated:NO];
154164
return;
155165
}
@@ -179,12 +189,15 @@ - (void)updateUI:(FIRAuth * _Nonnull) auth withUser:(FIRUser *_Nullable) user {
179189
self.cellUID.textLabel.text = user.uid;
180190

181191
// If the user is anonymous, delete the user to avoid dangling anonymous users.
182-
if (self.authUI.auth.currentUser.isAnonymous) {
183-
self.buttonAuthorization.title = @"Delete Anonymous User";
184-
} else {
192+
if (auth.currentUser.isAnonymous) {
193+
[_anonymousSignIn.textLabel setText:@"Delete Anonymous User"];
194+
}
195+
else {
196+
[_anonymousSignIn.textLabel setText:@"Sign In Anonymously"];
185197
self.buttonAuthorization.title = @"Sign Out";
186198
}
187199
} else {
200+
[_anonymousSignIn.textLabel setText:@"Sign In Anonymously"];
188201
self.cellSignIn.textLabel.text = @"Not signed-in";
189202
self.cellName.textLabel.text = @"";
190203
self.cellEmail.textLabel.text = @"";
@@ -215,8 +228,8 @@ - (IBAction)onAuthUIDelegateChanged:(UISwitch *)sender {
215228
}
216229

217230
- (IBAction)onAuthorization:(id)sender {
218-
if (!self.auth.currentUser) {
219-
231+
if (!_auth.currentUser || _auth.currentUser.isAnonymous) {
232+
FUIAuth.defaultAuthUI.shouldAutoUpgradeAnonymousUsers = YES;
220233
_authUI.providers = [self getListOfIDPs];
221234
_authUI.signInWithEmailHidden = ![self isEmailEnabled];
222235

@@ -247,13 +260,31 @@ - (void)authUI:(FUIAuth *)authUI
247260
if (error) {
248261
if (error.code == FUIAuthErrorCodeUserCancelledSignIn) {
249262
[self showAlertWithTitlte:@"Error" message:error.localizedDescription];
250-
} else {
251-
NSError *detailedError = error.userInfo[NSUnderlyingErrorKey];
252-
if (!detailedError) {
253-
detailedError = error;
254-
}
255-
NSLog(@"ERROR: %@", detailedError.localizedDescription);
263+
return;
264+
}
265+
if (error.code == FUIAuthErrorCodeMergeConflict) {
266+
FIRAuthCredential *credential = error.userInfo[FUIAuthCredentialKey];
267+
NSString *anonymousUserID = authUI.auth.currentUser.uid;
268+
NSString *messsage = [NSString stringWithFormat:@"A merge conflict occurred. The old user ID "
269+
"was: %@. You are now signed in with the following credential type: %@", anonymousUserID,
270+
[credential.provider uppercaseString]];
271+
[self showAlertWithTitlte:@"Merge Conflict" message:messsage];
272+
NSLog(@"%@", messsage);
273+
[[FUIAuth defaultAuthUI].auth
274+
signInAndRetrieveDataWithCredential:credential
275+
completion:^(FIRAuthDataResult *_Nullable authResult,
276+
NSError *_Nullable error) {
277+
if (error) {
278+
NSLog(@"%@",error.description);
279+
}
280+
}];
281+
return;
282+
}
283+
NSError *detailedError = error.userInfo[NSUnderlyingErrorKey];
284+
if (!detailedError) {
285+
detailedError = error;
256286
}
287+
NSLog(@"ERROR: %@", detailedError.localizedDescription);
257288
}
258289
}
259290

@@ -279,21 +310,9 @@ - (NSString *)getAllIdTokens {
279310

280311
- (void)signOut {
281312
NSError *error;
282-
FIRUser *currentUser = self.authUI.auth.currentUser;
283-
// If the user is anonymous, delete the user to avoid dangling anonymous users.
284-
if (currentUser.isAnonymous) {
285-
[currentUser deleteWithCompletion:^(NSError * _Nullable error) {
286-
if (error) {
287-
[self showAlertWithTitlte:@"Error" message:error.localizedDescription];
288-
return;
289-
}
290-
[self showAlertWithTitlte:@"" message:@"Anonymous user deleted"];
291-
}];
292-
} else {
293-
[self.authUI signOutWithError:&error];
294-
if (error) {
295-
[self showAlertWithTitlte:@"Error" message:error.localizedDescription];
296-
}
313+
[self.authUI signOutWithError:&error];
314+
if (error) {
315+
[self showAlertWithTitlte:@"Error" message:error.localizedDescription];
297316
}
298317
}
299318

0 commit comments

Comments
 (0)