diff --git a/packages/auth/ios/RNFBAuth/RNFBAuthModule.m b/packages/auth/ios/RNFBAuth/RNFBAuthModule.m index 279084be54..cafbc8a4cb 100644 --- a/packages/auth/ios/RNFBAuth/RNFBAuthModule.m +++ b/packages/auth/ios/RNFBAuth/RNFBAuthModule.m @@ -50,6 +50,8 @@ static __strong NSMutableDictionary *authStateHandlers; static __strong NSMutableDictionary *idTokenHandlers; +// Used for caching credentials between method calls. +static __strong NSMutableDictionary *credentials; @implementation RNFBAuthModule #pragma mark - @@ -67,6 +69,7 @@ - (id)init { dispatch_once(&onceToken, ^{ authStateHandlers = [[NSMutableDictionary alloc] init]; idTokenHandlers = [[NSMutableDictionary alloc] init]; + credentials = [[NSMutableDictionary alloc] init]; }); return self; } @@ -88,6 +91,8 @@ - (void)invalidate { [[FIRAuth authWithApp:firebaseApp] removeIDTokenDidChangeListener:[idTokenHandlers valueForKey:key]]; } [idTokenHandlers removeAllObjects]; + + [credentials removeAllObjects]; } #pragma mark - @@ -922,7 +927,10 @@ - (void)invalidate { - (FIRAuthCredential *)getCredentialForProvider:(NSString *)provider token:(NSString *)authToken secret:(NSString *)authTokenSecret { FIRAuthCredential *credential; - if ([provider compare:@"twitter.com" options:NSCaseInsensitiveSearch] == NSOrderedSame) { + // First check if we cached an authToken + if (credentials[authToken] != nil && ![credentials[authToken] isEqual:[NSNull null]]) { + credential = credentials[authToken]; + } else if ([provider compare:@"twitter.com" options:NSCaseInsensitiveSearch] == NSOrderedSame) { credential = [FIRTwitterAuthProvider credentialWithToken:authToken secret:authTokenSecret]; } else if ([provider compare:@"facebook.com" options:NSCaseInsensitiveSearch] == NSOrderedSame) { credential = [FIRFacebookAuthProvider credentialWithAccessToken:authToken]; @@ -979,6 +987,7 @@ - (void)promiseRejectAuthException:(RCTPromiseRejectBlock)reject error:(NSError @"code": [jsError valueForKey:@"code"], @"message": [jsError valueForKey:@"message"], @"nativeErrorMessage": [jsError valueForKey:@"nativeErrorMessage"], + @"authCredential": [jsError valueForKey:@"authCredential"], }]; } @@ -1042,10 +1051,17 @@ - (NSDictionary *)getJSError:(NSError *)error { default:break; } + NSDictionary *authCredentialDict = nil; + if ([error userInfo][FIRAuthErrorUserInfoUpdatedCredentialKey] != nil) { + FIRAuthCredential *authCredential = [error userInfo][FIRAuthErrorUserInfoUpdatedCredentialKey]; + authCredentialDict = [self authCredentialToDict:authCredential]; + } + return @{ @"code": code, @"message": message, @"nativeErrorMessage": nativeErrorMessage, + @"authCredential" : authCredentialDict != nil ? (id) authCredentialDict : [NSNull null], }; } @@ -1183,6 +1199,19 @@ - (NSDictionary *)firebaseUserToDict:(FIRUser *)user { }; } +- (NSDictionary*) authCredentialToDict:(FIRAuthCredential *)authCredential { + NSString *authCredentialHash = [NSString stringWithFormat:@"%@", @([authCredential hash])]; + + // Temporarily store the non-serializable credential for later + credentials[authCredentialHash] = authCredential; + + return @{ + keyProviderId: authCredential.provider, + @"token": authCredentialHash, + @"secret": [NSNull null], + }; +} + - (FIRActionCodeSettings *)buildActionCodeSettings:(NSDictionary *)actionCodeSettings { FIRActionCodeSettings *settings = [[FIRActionCodeSettings alloc] init]; diff --git a/packages/auth/lib/index.d.ts b/packages/auth/lib/index.d.ts index 286111a020..8c317ed0d5 100644 --- a/packages/auth/lib/index.d.ts +++ b/packages/auth/lib/index.d.ts @@ -52,6 +52,16 @@ export namespace FirebaseAuthTypes { import FirebaseModule = ReactNativeFirebase.FirebaseModule; import NativeFirebaseError = ReactNativeFirebase.NativeFirebaseError; + export interface NativeFirebaseAuthError extends NativeFirebaseError { + userInfo: { + /** + * When trying to sign in or link with an AuthCredential which was already associated with an account, + * you might receive an updated credential (depending of provider) which you can use to recover from the error. + */ + authCredential: AuthCredential | null; + }; + } + /** * Interface that represents the credentials returned by an auth provider. Implementations specify the details * about each auth provider's credential requirements. @@ -980,6 +990,7 @@ export namespace FirebaseAuthTypes { * @error auth/wrong-password Thrown if the password used in a auth.EmailAuthProvider.credential is not correct or when the user associated with the email does not have a password. * @error auth/invalid-verification-code Thrown if the credential is a auth.PhoneAuthProvider.credential and the verification code of the credential is not valid. * @error auth/invalid-verification-id Thrown if the credential is a auth.PhoneAuthProvider.credential and the verification ID of the credential is not valid. + * @throws on iOS {@link auth.NativeFirebaseAuthError}, on Android {@link auth.NativeFirebaseError} * @param credential A created {@link auth.AuthCredential}. */ linkWithCredential(credential: AuthCredential): Promise;