Skip to content

Commit 75b8a55

Browse files
committed
Add anonymous sign in upgrade
1 parent f72d1da commit 75b8a55

File tree

6 files changed

+129
-55
lines changed

6 files changed

+129
-55
lines changed

FirebaseAuthUI/FUIAuth.h

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

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

FirebaseAuthUI/FUIAuth.m

Lines changed: 59 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -174,37 +174,70 @@ - (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 *) = ^(FIRAuthDataResult *authResult){
179+
if (result) {
180+
result(authResult.user, nil);
187181
}
188-
189-
if (error) {
190-
if (result) {
191-
result(nil, error);
192-
}
193-
[self invokeResultCallbackWithAuthDataResult:nil error:error];
182+
// Hide Auth Picker Controller which was presented modally.
183+
if (isAuthPickerShown && presentingViewController.presentingViewController) {
184+
[presentingViewController dismissViewControllerAnimated:YES completion:^{
185+
[self invokeResultCallbackWithAuthDataResult:authResult error:nil];
186+
}];
194187
} else {
195-
if (result) {
196-
result(authResult.user, nil);
188+
[self invokeResultCallbackWithAuthDataResult:authResult error:nil];
189+
}
190+
};
191+
192+
// Check for the presence of an anonymous user and whether automatic upgrade is enabled.
193+
if (_auth.currentUser.isAnonymous && [FUIAuth defaultAuthUI].autoUpgradeAnonymousUsers) {
194+
[_auth.currentUser
195+
linkAndRetrieveDataWithCredential:credential
196+
completion:^(FIRAuthDataResult *_Nullable authResult,
197+
NSError * _Nullable error) {
198+
if (error) {
199+
// Handle error cases
200+
NSError *mergeError;
201+
FIRAuthCredential *newCredential = credential;
202+
if (error.code == FIRAuthErrorCodeCredentialAlreadyInUse) {
203+
if (providerUI.providerID == FIRPhoneAuthProviderID) {
204+
newCredential = error.userInfo[FIRAuthUpdatedCredentialKey];
205+
}
206+
NSDictionary *userInfo = @{
207+
FUIAuthCredentialKey : newCredential,
208+
};
209+
mergeError = [NSError errorWithDomain:FUIAuthErrorDomain
210+
code:FUIAuthErrorCodeMergeConflict
211+
userInfo:userInfo];
212+
result(nil, mergeError);
213+
[self invokeResultCallbackWithAuthDataResult:nil error:mergeError];
214+
}
197215
}
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-
}];
216+
completeSignInBlock(authResult);
217+
}];
218+
} else {
219+
[self.auth signInAndRetrieveDataWithCredential:credential
220+
completion:^(FIRAuthDataResult *_Nullable authResult,
221+
NSError *_Nullable error) {
222+
if (error && error.code == FIRAuthErrorCodeAccountExistsWithDifferentCredential) {
223+
NSString *email = error.userInfo[kErrorUserInfoEmailKey];
224+
[self handleAccountLinkingForEmail:email
225+
newCredential:credential
226+
presentingViewController:presentingViewController
227+
singInResult:result];
228+
return;
229+
}
230+
231+
if (error) {
232+
if (result) {
233+
result(nil, error);
234+
}
235+
[self invokeResultCallbackWithAuthDataResult:nil error:error];
203236
} else {
204-
[self invokeResultCallbackWithAuthDataResult:authResult error:nil];
237+
completeSignInBlock(authResult);
205238
}
206-
}
207-
}];
239+
}];
240+
}
208241
}];
209242
}
210243

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.autoUpgradeAnonymousUsers = 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 sig 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)