diff --git a/android/src/main/java/io/invertase/firebase/auth/RNFirebaseAuth.java b/android/src/main/java/io/invertase/firebase/auth/RNFirebaseAuth.java index 393d2ec1..0dd36723 100644 --- a/android/src/main/java/io/invertase/firebase/auth/RNFirebaseAuth.java +++ b/android/src/main/java/io/invertase/firebase/auth/RNFirebaseAuth.java @@ -25,7 +25,9 @@ import com.google.android.gms.tasks.Task; import com.google.firebase.auth.AuthCredential; import com.google.firebase.auth.AuthResult; +import com.google.firebase.auth.FirebaseAuthInvalidCredentialsException; import com.google.firebase.auth.GithubAuthProvider; +import com.google.firebase.auth.ProviderQueryResult; import com.google.firebase.auth.TwitterAuthProvider; import com.google.firebase.auth.UserInfo; import com.google.firebase.auth.UserProfileChangeRequest; @@ -633,6 +635,40 @@ public class RNFirebaseAuth extends ReactContextBaseJavaModule { } } + /** + * fetchProvidersForEmail + * + * @param promise + */ + @ReactMethod + public void fetchProvidersForEmail(final String email, final Promise promise) { + Log.d(TAG, "fetchProvidersForEmail"); + + mAuth.fetchProvidersForEmail(email) + .addOnCompleteListener(new OnCompleteListener() { + @Override + public void onComplete(@NonNull Task task) { + if (task.isSuccessful()) { + Log.d(TAG, "fetchProvidersForEmail:onComplete:success"); + List providers = task.getResult().getProviders(); + WritableArray array = Arguments.createArray(); + + if (providers != null) { + for(String provider : providers) { + array.pushString(provider); + } + } + + promise.resolve(array); + } else { + Exception exception = task.getException(); + Log.d(TAG, "fetchProvidersForEmail:onComplete:failure", exception); + promiseRejectAuthException(promise, exception); + } + } + }); + } + /* ------------------ * INTERNAL HELPERS * ---------------- */ @@ -645,7 +681,7 @@ public class RNFirebaseAuth extends ReactContextBaseJavaModule { */ private void promiseNoUser(Promise promise, Boolean isError) { if (isError) { - promise.reject("auth/no_current_user", "No user currently signed in."); + promise.reject("auth/no-current-user", "No user currently signed in."); } else { promise.resolve(null); } @@ -675,6 +711,7 @@ public class RNFirebaseAuth extends ReactContextBaseJavaModule { private void promiseRejectAuthException(Promise promise, Exception exception) { String code = "UNKNOWN"; String message = exception.getMessage(); + String invalidEmail = "The email address is badly formatted."; try { FirebaseAuthException authException = (FirebaseAuthException) exception; @@ -695,7 +732,7 @@ public class RNFirebaseAuth extends ReactContextBaseJavaModule { message = "The supplied auth credential is malformed or has expired."; break; case "INVALID_EMAIL": - message = "The email address is badly formatted."; + message = invalidEmail; break; case "WRONG_PASSWORD": message = "The password is invalid or the user does not have a password."; @@ -737,6 +774,11 @@ public class RNFirebaseAuth extends ReactContextBaseJavaModule { } } + if (code.equals("UNKNOWN") && exception instanceof FirebaseAuthInvalidCredentialsException) { + code = "INVALID_EMAIL"; + message = invalidEmail; + } + code = "auth/" + code.toLowerCase().replace("error_", "").replace('_', '-'); promise.reject(code, message, exception); } diff --git a/ios/RNFirebase/RNFirebaseAuth.m b/ios/RNFirebase/RNFirebaseAuth.m index 96f8da0b..c8e82f1e 100644 --- a/ios/RNFirebase/RNFirebaseAuth.m +++ b/ios/RNFirebase/RNFirebaseAuth.m @@ -433,6 +433,27 @@ RCT_EXPORT_METHOD(reauthenticate:(NSString *)provider authToken:(NSString *)auth } } +/** + fetchProvidersForEmail + + @param NSString email + @param RCTPromiseResolveBlock resolve + @param RCTPromiseRejectBlock reject + @return + */ +RCT_EXPORT_METHOD(fetchProvidersForEmail:(NSString *)email resolver:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject) { + [[FIRAuth auth] fetchProvidersForEmail:email completion:^(NSArray *_Nullable providers, NSError *_Nullable error) { + if (error) { + [self promiseRejectAuthException:reject error:error]; + } else if (!providers) { + NSMutableArray *emptyResponse = [[NSMutableArray alloc] init]; + resolve(emptyResponse); + } else { + resolve(providers); + } + }]; +} + /** getCredentialForProvider @@ -472,7 +493,7 @@ RCT_EXPORT_METHOD(reauthenticate:(NSString *)provider authToken:(NSString *)auth */ - (void) promiseNoUser:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject isError:(BOOL) isError { if (isError) { - reject(@"auth/no_current_user", @"No user currently signed in.", nil); + reject(@"auth/no-current-user", @"No user currently signed in.", nil); } else { resolve([NSNull null]); } diff --git a/lib/modules/auth/index.js b/lib/modules/auth/index.js index 52b7a4af..5b58a40c 100644 --- a/lib/modules/auth/index.js +++ b/lib/modules/auth/index.js @@ -156,6 +156,14 @@ export default class Auth extends Base { return this._interceptUserValue(FirebaseAuth.getCurrentUser()); } + /** + * Returns a list of authentication providers that can be used to sign in a given user (identified by its main email address). + * @return {Promise} + */ + fetchProvidersForEmail(email: string): Promise { + return FirebaseAuth.fetchProvidersForEmail(email); + } + /** * Get the currently signed in user * @return {Promise} diff --git a/lib/modules/auth/providers/Email.js b/lib/modules/auth/providers/Email.js index 92f4d68f..484dfbe3 100644 --- a/lib/modules/auth/providers/Email.js +++ b/lib/modules/auth/providers/Email.js @@ -4,6 +4,7 @@ export default { token: email, secret: password, provider: 'password', + providerId: 'password', }; }, }; diff --git a/lib/modules/auth/providers/Facebook.js b/lib/modules/auth/providers/Facebook.js index ffe032bb..55420cde 100644 --- a/lib/modules/auth/providers/Facebook.js +++ b/lib/modules/auth/providers/Facebook.js @@ -4,6 +4,7 @@ export default { token, secret: '', provider: 'facebook', + providerId: 'facebook', }; }, }; diff --git a/lib/modules/auth/providers/Github.js b/lib/modules/auth/providers/Github.js index 96a72c28..d93bab34 100644 --- a/lib/modules/auth/providers/Github.js +++ b/lib/modules/auth/providers/Github.js @@ -4,6 +4,7 @@ export default { token, secret: '', provider: 'github', + providerId: 'github', }; }, }; diff --git a/lib/modules/auth/providers/Google.js b/lib/modules/auth/providers/Google.js index 2e7177ed..157bdff4 100644 --- a/lib/modules/auth/providers/Google.js +++ b/lib/modules/auth/providers/Google.js @@ -4,6 +4,7 @@ export default { token, secret, provider: 'google', + providerId: 'google', }; }, }; diff --git a/lib/modules/auth/providers/Twitter.js b/lib/modules/auth/providers/Twitter.js index 1a769089..36d5f25a 100644 --- a/lib/modules/auth/providers/Twitter.js +++ b/lib/modules/auth/providers/Twitter.js @@ -4,6 +4,7 @@ export default { token, secret, provider: 'twitter', + providerId: 'twitter', }; }, }; diff --git a/lib/modules/auth/user.js b/lib/modules/auth/user.js index 89afc0d7..4aa99645 100644 --- a/lib/modules/auth/user.js +++ b/lib/modules/auth/user.js @@ -113,7 +113,7 @@ export default class User { * * @param credential */ - link(credential: CredentialType) { + linkWithCredential(credential: CredentialType) { return this._auth._interceptUserValue(FirebaseAuth.link(credential.provider, credential.token, credential.secret)); } @@ -121,11 +121,10 @@ export default class User { * Re-authenticate a user with a third-party authentication provider * @return {Promise} A promise resolved upon completion */ - reauthenticate(credential: CredentialType) { + reauthenticateWithCredential(credential: CredentialType) { return this._auth._interceptUserValue(FirebaseAuth.reauthenticate(credential.provider, credential.token, credential.secret)); } - /** * Reload the current user * @return {Promise} @@ -136,9 +135,19 @@ export default class User { /** * get the token of current user + * @deprecated Deprecated getToken in favor of getIdToken. * @return {Promise} */ getToken(forceRefresh: Boolean = false): Promise { + console.warn('Deprecated firebase.User.prototype.getToken in favor of firebase.User.prototype.getIdToken.'); + return FirebaseAuth.getToken(forceRefresh); + } + + /** + * get the token of current user + * @return {Promise} + */ + getIdToken(forceRefresh: Boolean = false): Promise { return FirebaseAuth.getToken(forceRefresh); } diff --git a/tests/src/tests/auth/authTests.js b/tests/src/tests/auth/authTests.js index c9870069..f5f77414 100644 --- a/tests/src/tests/auth/authTests.js +++ b/tests/src/tests/auth/authTests.js @@ -49,7 +49,7 @@ function authTests({ tryCatch, describe, it, firebase }) { const credential = firebase.native.auth.EmailAuthProvider.credential(email, pass); return currentUser - .link(credential) + .linkWithCredential(credential) .then((linkedUser) => { linkedUser.should.be.an.Object(); linkedUser.uid.should.be.a.String(); @@ -84,7 +84,7 @@ function authTests({ tryCatch, describe, it, firebase }) { const credential = firebase.native.auth.EmailAuthProvider.credential(email, pass); return currentUser - .link(credential) + .linkWithCredential(credential) .then(() => { return firebase.native.auth().signOut().then(() => { return Promise.reject(new Error('Did not error on link')); @@ -149,6 +149,7 @@ function authTests({ tryCatch, describe, it, firebase }) { }; const failureCb = (error) => { + console.log('ERROR', error) error.code.should.equal('auth/wrong-password'); error.message.should.equal('The password is invalid or the user does not have a password.'); return Promise.resolve(); @@ -246,6 +247,56 @@ function authTests({ tryCatch, describe, it, firebase }) { }); }); + describe('Email - Providers', () => { + it('it should return password provider for an email address', () => { + return new Promise((resolve, reject) => { + const successCb = tryCatch((providers) => { + providers.should.be.a.Array(); + providers.should.containEql('password'); + resolve(); + }, reject); + + const failureCb = tryCatch(() => { + reject(new Error('Should not have an error.')); + }, reject); + + return firebase.native.auth().fetchProvidersForEmail('test@test.com').then(successCb).catch(failureCb); + }); + }); + + it('it should return an empty array for a not found email', () => { + return new Promise((resolve, reject) => { + const successCb = tryCatch((providers) => { + providers.should.be.a.Array(); + providers.should.be.empty(); + resolve(); + }, reject); + + const failureCb = tryCatch(() => { + reject(new Error('Should not have an error.')); + }, reject); + + return firebase.native.auth().fetchProvidersForEmail('test@i-do-not-exist.com').then(successCb).catch(failureCb); + }); + }); + + it('it should return an error for a bad email address', () => { + return new Promise((resolve, reject) => { + const successCb = tryCatch(() => { + reject(new Error('Should not have successfully resolved.')); + }, reject); + + const failureCb = tryCatch((error) => { + error.code.should.equal('auth/invalid-email'); + error.message.should.equal('The email address is badly formatted.'); + resolve(); + }, reject); + + return firebase.native.auth().fetchProvidersForEmail('foobar').then(successCb).catch(failureCb); + }); + }); + }); + describe('Misc', () => { it('it should delete a user', () => { const random = randomString(12, '#aA'); @@ -264,7 +315,7 @@ function authTests({ tryCatch, describe, it, firebase }) { return firebase.native.auth().createUserWithEmailAndPassword(email, pass).then(successCb); }); - it('it should return a token via getToken', () => { + it('it should return a token via getIdToken', () => { const random = randomString(12, '#aA'); const email = `${random}@${random}.com`; const pass = random; @@ -276,7 +327,7 @@ function authTests({ tryCatch, describe, it, firebase }) { newUser.isAnonymous.should.equal(false); newUser.providerId.should.equal('firebase'); - return newUser.getToken().then((token) => { + return newUser.getIdToken().then((token) => { token.should.be.a.String(); token.length.should.be.greaterThan(24); return firebase.native.auth().currentUser.delete(); @@ -297,7 +348,7 @@ function authTests({ tryCatch, describe, it, firebase }) { }, reject); const failureCb = tryCatch((error) => { - error.code.should.equal('auth/no_current_user'); + error.code.should.equal('auth/no-current-user'); error.message.should.equal('No user currently signed in.'); resolve(); }, reject);