From 6c7c9ec1f712e26f4f55013cbf3ebb07ea70a625 Mon Sep 17 00:00:00 2001 From: Zsika Phillip Date: Wed, 14 Feb 2018 15:14:44 -0800 Subject: [PATCH 1/3] Adds anonymous upgrade for facebook email conflict --- FirebaseAuthUI/FUIAuth.m | 94 ++++++++++++++++++++++-- FirebaseAuthUI/FUIAuthProvider.h | 7 +- FirebaseFacebookAuthUI/FUIFacebookAuth.m | 15 ++++ 3 files changed, 110 insertions(+), 6 deletions(-) diff --git a/FirebaseAuthUI/FUIAuth.m b/FirebaseAuthUI/FUIAuth.m index 448299fb7b4..462599afbd1 100644 --- a/FirebaseAuthUI/FUIAuth.m +++ b/FirebaseAuthUI/FUIAuth.m @@ -19,12 +19,14 @@ #import #import +#import #import #import #import "FUIAuthBaseViewController_Internal.h" #import "FUIAuthErrors.h" #import "FUIAuthPickerViewController.h" #import "FUIAuthStrings.h" +#import "FUIGoogleAuth.h" #import "FUIEmailEntryViewController.h" #import "FUIPasswordVerificationViewController.h" @@ -173,7 +175,6 @@ - (void)signInWithProviderUI:(id)providerUI } return; } - // Block to complete sign-in void (^completeSignInBlock)(FIRAuthDataResult *, NSError *) = ^(FIRAuthDataResult *authResult, NSError *error) { @@ -200,7 +201,6 @@ - (void)signInWithProviderUI:(id)providerUI if (error) { // Check for "credential in use" conflict error and handle appropriately. if (error.code == FIRAuthErrorCodeCredentialAlreadyInUse) { - NSError *mergeError; FIRAuthCredential *newCredential = credential; // Check for and handle special case for Phone Auth Provider. if (providerUI.providerID == FIRPhoneAuthProviderID) { @@ -210,11 +210,44 @@ - (void)signInWithProviderUI:(id)providerUI NSDictionary *userInfo = @{ FUIAuthCredentialKey : newCredential, }; - mergeError = [NSError errorWithDomain:FUIAuthErrorDomain - code:FUIAuthErrorCodeMergeConflict - userInfo:userInfo]; + NSError *mergeError = [NSError errorWithDomain:FUIAuthErrorDomain + code:FUIAuthErrorCodeMergeConflict + userInfo:userInfo]; result(nil, mergeError); completeSignInBlock(authResult, mergeError); + } else if (error.code == FIRAuthErrorCodeEmailAlreadyInUse) { + if ([providerUI respondsToSelector:@selector(email)]) { + // Link federated providers + [self signInWithEmailHint:[providerUI email] + presentingViewController:presentingViewController + completion:^(FIRAuthDataResult *_Nullable authResult, + NSError *_Nullable error) { + if (error) { + result(nil, error); + completeSignInBlock(nil, error); + return; + } + [authResult.user linkAndRetrieveDataWithCredential:credential + completion:^(FIRAuthDataResult + *_Nullable authResult, + NSError *_Nullable error) { + if (error) { + result(nil, error); + completeSignInBlock(nil, error); + return; + } + FIRAuthCredential *newCredential = credential; + NSDictionary *userInfo = @{ + FUIAuthCredentialKey : newCredential, + }; + NSError *mergeError = [NSError errorWithDomain:FUIAuthErrorDomain + code:FUIAuthErrorCodeMergeConflict + userInfo:userInfo]; + result(nil, mergeError); + completeSignInBlock(authResult, mergeError); + }]; + }]; + } } else { if (!isAuthPickerShown || error.code != FUIAuthErrorCodeUserCancelledSignIn) { [self invokeResultCallbackWithAuthDataResult:nil error:error]; @@ -250,6 +283,57 @@ - (void)signInWithProviderUI:(id)providerUI }]; } +- (void)signInWithEmailHint:(NSString *)emailHint + presentingViewController:(UIViewController *)presentingViewController + completion:(FIRAuthDataResultCallback)completion { + NSString *kTempApp = @"tempApp"; + FIROptions *options = [FIROptions defaultOptions]; + if (![FIRApp appNamed:kTempApp]) { + [FIRApp configureWithName:kTempApp options:options]; + } + FIRApp *tempApp = [FIRApp appNamed:kTempApp]; + FIRAuth *auth = [FIRAuth authWithApp:tempApp]; + + [self.auth fetchProvidersForEmail:emailHint completion:^(NSArray *_Nullable providers, + NSError *_Nullable error) { + NSString *federatedProviderID = [self federatedAuthProviderFromProviders:providers]; + // Google case + if ([federatedProviderID isEqualToString:FIRGoogleAuthProviderID]) { + id googleProviderUI; + for (id provider in self.providers) { + if ([provider.providerID isEqualToString:FIRGoogleAuthProviderID]) { + googleProviderUI = provider; + break; + } + } + [googleProviderUI signOut]; + [googleProviderUI signInWithDefaultValue:emailHint + presentingViewController:presentingViewController + completion:^(FIRAuthCredential *_Nullable credential, + NSError *_Nullable error, + FIRAuthResultCallback _Nullable result) { + if (error) { + completion(nil, error); + return; + } + + [auth signInAndRetrieveDataWithCredential:credential completion:completion]; + }]; + } + }]; +} + +- (nullable NSString *)federatedAuthProviderFromProviders:(NSArray *) providers { + NSSet *providerSet = + [[NSSet alloc] initWithArray:@[ FIRFacebookAuthProviderID, FIRGoogleAuthProviderID ]]; + for (NSString *provider in providers) { + if ( [providerSet containsObject:provider]) { + return provider; + } + } + return nil; +} + - (void)handleAccountLinkingForEmail:(NSString *)email newCredential:(FIRAuthCredential *)newCredential presentingViewController:(UIViewController *)presentingViewController diff --git a/FirebaseAuthUI/FUIAuthProvider.h b/FirebaseAuthUI/FUIAuthProvider.h index f992730b7b7..493a55f67ee 100644 --- a/FirebaseAuthUI/FUIAuthProvider.h +++ b/FirebaseAuthUI/FUIAuthProvider.h @@ -124,10 +124,15 @@ __attribute__((deprecated("This is deprecated API and will be removed in a futur @optional; /** @property idToken - @brief User Id Token obtained during sign in. Not all providers can return, thus it's optional + @brief User Id Token obtained during sign in. Not all providers can return, thus it's optional. */ @property(nonatomic, copy, readonly) NSString *idToken; +/** @fn email + @brief The email address associated with this provider, if any. + */ +- (NSString *)email; + /** @fn handleOpenURL: @brief May be used to help complete a sign-in flow which requires a callback from Safari. @param URL The URL which may be handled by the auth provider if an URL is expected. diff --git a/FirebaseFacebookAuthUI/FUIFacebookAuth.m b/FirebaseFacebookAuthUI/FUIFacebookAuth.m index 3d75d6b3ad8..86dd5b1deee 100644 --- a/FirebaseFacebookAuthUI/FUIFacebookAuth.m +++ b/FirebaseFacebookAuthUI/FUIFacebookAuth.m @@ -61,6 +61,11 @@ @implementation FUIFacebookAuth { @brief The presenting view controller for interactive sign-in. */ UIViewController *_presentingViewController; + + /** @var _email + @brief The email address associated with this account. + */ + NSString *_email; } - (instancetype)initWithPermissions:(NSArray *)permissions { @@ -141,12 +146,22 @@ - (void)signInWithDefaultValue:(nullable NSString *)defaultValue NSError *newError = [FUIAuthErrorUtils userCancelledSignInError]; [self completeSignInFlowWithAccessToken:nil error:newError]; } else { + // Retrieve email. + [[[FBSDKGraphRequest alloc] initWithGraphPath:@"me" parameters:@{ @"fields" : @"email" }] + startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id result, + NSError *error) { + self->_email = result[@"email"]; + }]; [self completeSignInFlowWithAccessToken:result.token.tokenString error:nil]; } }]; } +- (NSString *)email { + return _email; +} + - (void)signOut { [_loginManager logOut]; } From 49afd7fad8299a81ebf8263699db67bd90529fc3 Mon Sep 17 00:00:00 2001 From: Zsika Phillip Date: Wed, 14 Feb 2018 20:51:30 -0800 Subject: [PATCH 2/3] Addresses comments --- FirebaseAuthUI/FUIAuth.m | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/FirebaseAuthUI/FUIAuth.m b/FirebaseAuthUI/FUIAuth.m index 462599afbd1..72a4cbd1c5a 100644 --- a/FirebaseAuthUI/FUIAuth.m +++ b/FirebaseAuthUI/FUIAuth.m @@ -213,7 +213,6 @@ - (void)signInWithProviderUI:(id)providerUI NSError *mergeError = [NSError errorWithDomain:FUIAuthErrorDomain code:FUIAuthErrorCodeMergeConflict userInfo:userInfo]; - result(nil, mergeError); completeSignInBlock(authResult, mergeError); } else if (error.code == FIRAuthErrorCodeEmailAlreadyInUse) { if ([providerUI respondsToSelector:@selector(email)]) { @@ -223,7 +222,6 @@ - (void)signInWithProviderUI:(id)providerUI completion:^(FIRAuthDataResult *_Nullable authResult, NSError *_Nullable error) { if (error) { - result(nil, error); completeSignInBlock(nil, error); return; } @@ -232,7 +230,6 @@ - (void)signInWithProviderUI:(id)providerUI *_Nullable authResult, NSError *_Nullable error) { if (error) { - result(nil, error); completeSignInBlock(nil, error); return; } @@ -243,7 +240,6 @@ - (void)signInWithProviderUI:(id)providerUI NSError *mergeError = [NSError errorWithDomain:FUIAuthErrorDomain code:FUIAuthErrorCodeMergeConflict userInfo:userInfo]; - result(nil, mergeError); completeSignInBlock(authResult, mergeError); }]; }]; @@ -325,7 +321,7 @@ - (void)signInWithEmailHint:(NSString *)emailHint - (nullable NSString *)federatedAuthProviderFromProviders:(NSArray *) providers { NSSet *providerSet = - [[NSSet alloc] initWithArray:@[ FIRFacebookAuthProviderID, FIRGoogleAuthProviderID ]]; + [NSSet setWithArray:@[ FIRFacebookAuthProviderID, FIRGoogleAuthProviderID ]]; for (NSString *provider in providers) { if ( [providerSet containsObject:provider]) { return provider; From 0b6061d09713526b097e524366e961e8fbcbb8a8 Mon Sep 17 00:00:00 2001 From: Zsika Phillip Date: Thu, 15 Feb 2018 09:26:22 -0800 Subject: [PATCH 3/3] Addresses comments. --- FirebaseAuthUI/FUIAuth.m | 3 +++ 1 file changed, 3 insertions(+) diff --git a/FirebaseAuthUI/FUIAuth.m b/FirebaseAuthUI/FUIAuth.m index 72a4cbd1c5a..09319e2efcf 100644 --- a/FirebaseAuthUI/FUIAuth.m +++ b/FirebaseAuthUI/FUIAuth.m @@ -284,10 +284,13 @@ - (void)signInWithEmailHint:(NSString *)emailHint completion:(FIRAuthDataResultCallback)completion { NSString *kTempApp = @"tempApp"; FIROptions *options = [FIROptions defaultOptions]; + // Create an new app instance in order to create a new auth instance. if (![FIRApp appNamed:kTempApp]) { [FIRApp configureWithName:kTempApp options:options]; } FIRApp *tempApp = [FIRApp appNamed:kTempApp]; + // Create a new auth instance in order to perform a successful sign-in without losing the + // currently signed in user on the default auth instance. FIRAuth *auth = [FIRAuth authWithApp:tempApp]; [self.auth fetchProvidersForEmail:emailHint completion:^(NSArray *_Nullable providers,