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 0ba32488..06a1e080 100644 --- a/android/src/main/java/io/invertase/firebase/auth/RNFirebaseAuth.java +++ b/android/src/main/java/io/invertase/firebase/auth/RNFirebaseAuth.java @@ -1060,6 +1060,15 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule { @ReactMethod public void reauthenticateWithCredential(String appName, String provider, String authToken, String authSecret, final Promise promise) { + reauthenticate(appName, provider, authToken, authSecret, promise, false); + } + + @ReactMethod + public void reauthenticateAndRetrieveDataWithCredential(String appName, String provider, String authToken, String authSecret, final Promise promise) { + reauthenticate(appName, provider, authToken, authSecret, promise, true); + } + + public void reauthenticate(String appName, String provider, String authToken, String authSecret, final Promise promise, final boolean withData) { FirebaseApp firebaseApp = FirebaseApp.getInstance(appName); final FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp); @@ -1072,13 +1081,17 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule { Log.d(TAG, "reauthenticate"); if (user != null) { - user.reauthenticate(credential) - .addOnCompleteListener(new OnCompleteListener() { + user.reauthenticateAndRetrieveData(credential) + .addOnCompleteListener(new OnCompleteListener() { @Override - public void onComplete(@NonNull Task task) { + public void onComplete(@NonNull Task task) { if (task.isSuccessful()) { Log.d(TAG, "reauthenticate:onComplete:success"); - promiseWithUser(firebaseAuth.getCurrentUser(), promise); + if (withData) { + promiseWithAuthResult(task.getResult(), promise); + } else { + promiseWithUser(task.getResult().getUser(), promise); + } } else { Exception exception = task.getException(); Log.e(TAG, "reauthenticate:onComplete:failure", exception); diff --git a/ios/RNFirebase/auth/RNFirebaseAuth.m b/ios/RNFirebase/auth/RNFirebaseAuth.m index 27f05b15..1ef16d18 100644 --- a/ios/RNFirebase/auth/RNFirebaseAuth.m +++ b/ios/RNFirebase/auth/RNFirebaseAuth.m @@ -940,18 +940,12 @@ RCT_EXPORT_METHOD(unlink: @param RCTPromiseRejectBlock reject @return */ -RCT_EXPORT_METHOD(reauthenticateWithCredential: - (NSString *) appDisplayName - provider: - (NSString *) provider - authToken: - (NSString *) authToken - authSecret: - (NSString *) authSecret - resolver: - (RCTPromiseResolveBlock) resolve - rejecter: - (RCTPromiseRejectBlock) reject) { +RCT_EXPORT_METHOD(reauthenticateWithCredential:(NSString *) appDisplayName + provider:(NSString *) provider + authToken:(NSString *) authToken + authSecret:(NSString *) authSecret + resolver:(RCTPromiseResolveBlock) resolve + rejecter:(RCTPromiseRejectBlock) reject) { FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName]; FIRAuthCredential *credential = [self getCredentialForProvider:provider token:authToken secret:authSecret]; @@ -976,6 +970,45 @@ RCT_EXPORT_METHOD(reauthenticateWithCredential: } } +/** + reauthenticateAndRetrieveDataWithCredential + + @param NSString provider + @param NSString authToken + @param NSString authSecret + @param RCTPromiseResolveBlock resolve + @param RCTPromiseRejectBlock reject + @return + */ +RCT_EXPORT_METHOD(reauthenticateAndRetrieveDataWithCredential:(NSString *) appDisplayName + provider:(NSString *) provider + authToken:(NSString *) authToken + authSecret:(NSString *) authSecret + resolver:(RCTPromiseResolveBlock) resolve + rejecter:(RCTPromiseRejectBlock) reject) { + FIRApp *firApp = [RNFirebaseUtil getApp:appDisplayName]; + + FIRAuthCredential *credential = [self getCredentialForProvider:provider token:authToken secret:authSecret]; + + if (credential == nil) { + return reject(@"auth/invalid-credential", @"The supplied auth credential is malformed, has expired or is not currently supported.", nil); + } + + FIRUser *user = [FIRAuth authWithApp:firApp].currentUser; + + if (user) { + [user reauthenticateAndRetrieveDataWithCredential:credential completion:^(FIRAuthDataResult * _Nullable authResult, NSError * _Nullable error) { + if (error) { + [self promiseRejectAuthException:reject error:error]; + } else { + [self promiseWithAuthResult:resolve rejecter:reject authResult:authResult]; + } + }]; + } else { + [self promiseNoUser:resolve rejecter:reject isError:YES]; + } +} + /** fetchProvidersForEmail diff --git a/tests/src/firebase.js b/tests/src/firebase.js index c98816fd..e67a04fb 100644 --- a/tests/src/firebase.js +++ b/tests/src/firebase.js @@ -54,18 +54,18 @@ console.log('RNApps -->', RNfirebase.apps); // no need for ready checks instances.native .auth() - .signInAnonymously() - .then(user => { - console.log('defaultApp user ->', user.toJSON()); + .signInAnonymouslyAndRetrieveData() + .then(userCredential => { + console.log('defaultApp user ->', userCredential.user.toJSON()); }); // dynamically initialized apps need a ready check instances.another.onReady().then(app => { app .auth() - .signInAnonymously() - .then(user => { - console.log('anotherApp user ->', user.toJSON()); + .signInAnonymouslyAndRetrieveData() + .then(userCredential => { + console.log('anotherApp user ->', userCredential.user.toJSON()); }); }); diff --git a/tests/src/tests/auth/authTests.js b/tests/src/tests/auth/authTests.js index 5d259394..bbaeb8ff 100644 --- a/tests/src/tests/auth/authTests.js +++ b/tests/src/tests/auth/authTests.js @@ -19,7 +19,7 @@ export default (authTests = ({ tryCatch, context, describe, it, firebase }) => { describe('auth()', () => { context('onAuthStateChanged', () => { it('calls callback with the current user and when auth state changes', async () => { - await firebase.native.auth().signInAnonymously(); + await firebase.native.auth().signInAnonymouslyAndRetrieveData(); // Test const callback = sinon.spy(); @@ -54,7 +54,7 @@ export default (authTests = ({ tryCatch, context, describe, it, firebase }) => { }); it('stops listening when unsubscribed', async () => { - await firebase.native.auth().signInAnonymously(); + await firebase.native.auth().signInAnonymouslyAndRetrieveData(); // Test const callback = sinon.spy(); @@ -89,7 +89,7 @@ export default (authTests = ({ tryCatch, context, describe, it, firebase }) => { // Sign back in - await firebase.native.auth().signInAnonymously(); + await firebase.native.auth().signInAnonymouslyAndRetrieveData(); // Assertions @@ -103,7 +103,7 @@ export default (authTests = ({ tryCatch, context, describe, it, firebase }) => { context('onIdTokenChanged', () => { it('calls callback with the current user and when auth state changes', async () => { - await firebase.native.auth().signInAnonymously(); + await firebase.native.auth().signInAnonymouslyAndRetrieveData(); // Test const callback = sinon.spy(); @@ -138,7 +138,7 @@ export default (authTests = ({ tryCatch, context, describe, it, firebase }) => { }); it('stops listening when unsubscribed', async () => { - await firebase.native.auth().signInAnonymously(); + await firebase.native.auth().signInAnonymouslyAndRetrieveData(); // Test const callback = sinon.spy(); @@ -173,7 +173,7 @@ export default (authTests = ({ tryCatch, context, describe, it, firebase }) => { // Sign back in - await firebase.native.auth().signInAnonymously(); + await firebase.native.auth().signInAnonymouslyAndRetrieveData(); // Assertions @@ -187,7 +187,7 @@ export default (authTests = ({ tryCatch, context, describe, it, firebase }) => { context('onUserChanged', () => { it('calls callback with the current user and when auth state changes', async () => { - await firebase.native.auth().signInAnonymously(); + await firebase.native.auth().signInAnonymouslyAndRetrieveData(); // Test const callback = sinon.spy(); @@ -225,7 +225,7 @@ export default (authTests = ({ tryCatch, context, describe, it, firebase }) => { }); it('stops listening when unsubscribed', async () => { - await firebase.native.auth().signInAnonymously(); + await firebase.native.auth().signInAnonymouslyAndRetrieveData(); // Test const callback = sinon.spy(); @@ -263,7 +263,7 @@ export default (authTests = ({ tryCatch, context, describe, it, firebase }) => { // Sign back in - await firebase.native.auth().signInAnonymously(); + await firebase.native.auth().signInAnonymouslyAndRetrieveData(); // Assertions @@ -709,6 +709,10 @@ export default (authTests = ({ tryCatch, context, describe, it, firebase }) => { newUser.isAnonymous.should.equal(false); newUser.providerId.should.equal('firebase'); newUser.should.equal(firebase.native.auth().currentUser); + newUser.metadata.should.be.an.Object(); + should.equal(newUser.phoneNumber, null); + + return newUser.delete(); }; return firebase.native @@ -796,6 +800,8 @@ export default (authTests = ({ tryCatch, context, describe, it, firebase }) => { const { additionalUserInfo } = newUserCredential; additionalUserInfo.should.be.an.Object(); additionalUserInfo.isNewUser.should.equal(true); + + return newUser.delete(); }; return firebase.native @@ -925,6 +931,37 @@ export default (authTests = ({ tryCatch, context, describe, it, firebase }) => { })); }); + context('signOut()', () => { + it('it should reject signOut if no currentUser', () => + new Promise((resolve, reject) => { + if (firebase.native.auth().currentUser) { + return reject( + new Error( + `A user is currently signed in. ${ + firebase.native.auth().currentUser.uid + }` + ) + ); + } + + const successCb = tryCatch(() => { + reject(new Error('No signOut error returned')); + }, reject); + + const failureCb = tryCatch(error => { + error.code.should.equal('auth/no-current-user'); + error.message.should.equal('No user currently signed in.'); + resolve(); + }, reject); + + return firebase.native + .auth() + .signOut() + .then(successCb) + .catch(failureCb); + })); + }); + context('delete()', () => { it('should delete a user', () => { const random = randomString(12, '#aA'); @@ -994,6 +1031,29 @@ export default (authTests = ({ tryCatch, context, describe, it, firebase }) => { }); }); + context('sendPasswordResetEmail()', () => { + it('should not error', async () => { + const random = randomString(12, '#aA'); + const email = `${random}@${random}.com`; + const pass = random; + + await firebase.native + .auth() + .createUserAndRetrieveDataWithEmailAndPassword(email, pass); + + try { + await firebase.native.auth().sendPasswordResetEmail(email); + await firebase.native.auth().currentUser.delete(); + } catch (error) { + // Reject + await firebase.native.auth().currentUser.delete(); + Promise.reject( + new Error('sendPasswordResetEmail() caused an error', error) + ); + } + }); + }); + context('signInWithRedirect', () => { it('should throw an unsupported error', () => { (() => { diff --git a/tests/src/tests/auth/userTests.js b/tests/src/tests/auth/userTests.js index 6b337d12..e71d3332 100644 --- a/tests/src/tests/auth/userTests.js +++ b/tests/src/tests/auth/userTests.js @@ -1,5 +1,3 @@ -import should from 'should'; - const randomString = (length, chars) => { let mask = ''; if (chars.indexOf('a') > -1) mask += 'abcdefghijklmnopqrstuvwxyz'; @@ -13,291 +11,400 @@ const randomString = (length, chars) => { return result; }; -export default (userTests = ({ tryCatch, context, describe, it, firebase }) => { +export default (userTests = ({ context, describe, it, firebase }) => { describe('User', () => { context('getIdToken()', () => { - it('should return a token', () => { + it('should return a token', async () => { const random = randomString(12, '#aA'); const email = `${random}@${random}.com`; const pass = random; - const successCb = newUser => { - newUser.uid.should.be.a.String(); - newUser.email.should.equal(email.toLowerCase()); - newUser.emailVerified.should.equal(false); - newUser.isAnonymous.should.equal(false); - newUser.providerId.should.equal('firebase'); - - return newUser.getIdToken().then(token => { - token.should.be.a.String(); - token.length.should.be.greaterThan(24); - return firebase.native.auth().currentUser.delete(); - }); - }; - - return firebase.native + const newUser = await firebase.native .auth() - .createUserWithEmailAndPassword(email, pass) - .then(successCb); + .createUserWithEmailAndPassword(email, pass); + + // Test + const token = await newUser.getIdToken(); + + // Assertions + token.should.be.a.String(); + token.length.should.be.greaterThan(24); + + // Clean up + await firebase.native.auth().currentUser.delete(); }); }); context('getToken()', () => { - it('should return a token', () => { + it('should return a token', async () => { const random = randomString(12, '#aA'); const email = `${random}@${random}.com`; const pass = random; - const successCb = newUser => { - newUser.uid.should.be.a.String(); - newUser.email.should.equal(email.toLowerCase()); - newUser.emailVerified.should.equal(false); - newUser.isAnonymous.should.equal(false); - newUser.providerId.should.equal('firebase'); - - return newUser.getToken().then(token => { - token.should.be.a.String(); - token.length.should.be.greaterThan(24); - return firebase.native.auth().currentUser.delete(); - }); - }; - - return firebase.native + const newUser = await firebase.native .auth() - .createUserWithEmailAndPassword(email, pass) - .then(successCb); + .createUserWithEmailAndPassword(email, pass); + + // Test + const token = await newUser.getToken(); + + // Assertions + token.should.be.a.String(); + token.length.should.be.greaterThan(24); + + // Clean up + await firebase.native.auth().currentUser.delete(); }); }); context('linkWithCredential()', () => { - it('it should link anonymous account <-> email account', () => { + it('should link anonymous account <-> email account', async () => { const random = randomString(12, '#aA'); const email = `${random}@${random}.com`; const pass = random; - const successCb = currentUser => { - currentUser.should.be.an.Object(); - currentUser.uid.should.be.a.String(); - currentUser.toJSON().should.be.an.Object(); - should.equal(currentUser.toJSON().email, null); - currentUser.isAnonymous.should.equal(true); - currentUser.providerId.should.equal('firebase'); - firebase.native.auth().currentUser.uid.should.be.a.String(); + await firebase.native.auth().signInAnonymouslyAndRetrieveData(); + const currentUser = firebase.native.auth().currentUser; - const credential = firebase.native.auth.EmailAuthProvider.credential( - email, - pass - ); + // Test + const credential = firebase.native.auth.EmailAuthProvider.credential( + email, + pass + ); - return currentUser - .linkWithCredential(credential) - .then(linkedUser => { - linkedUser.should.be.an.Object(); - linkedUser.should.equal(firebase.native.auth().currentUser); - linkedUser.uid.should.be.a.String(); - linkedUser.toJSON().should.be.an.Object(); - // iOS and Android are inconsistent in returning lowercase / mixed case - linkedUser - .toJSON() - .email.toLowerCase() - .should.eql(email.toLowerCase()); - linkedUser.isAnonymous.should.equal(false); - linkedUser.providerId.should.equal('firebase'); - return firebase.native.auth().signOut(); - }) - .catch(error => - firebase.native - .auth() - .signOut() - .then(() => Promise.reject(error)) - ); - }; + const linkedUser = await currentUser.linkWithCredential(credential); - return firebase.native - .auth() - .signInAnonymously() - .then(successCb); + // Assertions + linkedUser.should.be.an.Object(); + linkedUser.should.equal(firebase.native.auth().currentUser); + linkedUser.email.toLowerCase().should.equal(email.toLowerCase()); + linkedUser.isAnonymous.should.equal(false); + linkedUser.providerId.should.equal('firebase'); + linkedUser.providerData.should.be.an.Array(); + linkedUser.providerData.length.should.equal(1); + + // Clean up + await firebase.native.auth().currentUser.delete(); }); - it('it should error on link anon <-> email if email already exists', () => { + it('should error on link anon <-> email if email already exists', async () => { const email = 'test@test.com'; const pass = 'test1234'; - const successCb = currentUser => { - currentUser.should.be.an.Object(); - currentUser.uid.should.be.a.String(); - currentUser.toJSON().should.be.an.Object(); - should.equal(currentUser.toJSON().email, null); - currentUser.isAnonymous.should.equal(true); - currentUser.providerId.should.equal('firebase'); - firebase.native.auth().currentUser.uid.should.be.a.String(); + await firebase.native.auth().signInAnonymouslyAndRetrieveData(); + const currentUser = firebase.native.auth().currentUser; + // Test + try { const credential = firebase.native.auth.EmailAuthProvider.credential( email, pass ); + await currentUser.linkWithCredential(credential); - return currentUser - .linkWithCredential(credential) - .then(() => - firebase.native - .auth() - .signOut() - .then(() => Promise.reject(new Error('Did not error on link'))) - ) - .catch(error => - firebase.native - .auth() - .signOut() - .then(() => { - error.code.should.equal('auth/email-already-in-use'); - error.message.should.equal( - 'The email address is already in use by another account.' - ); - return Promise.resolve(); - }) - ); - }; + // Clean up + await firebase.native.auth().signOut(); - return firebase.native - .auth() - .signInAnonymously() - .then(successCb); + // Reject + Promise.reject(new Error('Did not error on link')); + } catch (error) { + // Assertions + error.code.should.equal('auth/email-already-in-use'); + error.message.should.equal( + 'The email address is already in use by another account.' + ); + + // Clean up + await firebase.native.auth().currentUser.delete(); + } }); }); context('linkAndRetrieveDataWithCredential()', () => { - it('it should link anonymous account <-> email account', () => { + it('should link anonymous account <-> email account', async () => { const random = randomString(12, '#aA'); const email = `${random}@${random}.com`; const pass = random; - const successCb = currentUser => { - currentUser.should.be.an.Object(); - currentUser.uid.should.be.a.String(); - currentUser.toJSON().should.be.an.Object(); - should.equal(currentUser.toJSON().email, null); - currentUser.isAnonymous.should.equal(true); - currentUser.providerId.should.equal('firebase'); - firebase.native.auth().currentUser.uid.should.be.a.String(); + await firebase.native.auth().signInAnonymouslyAndRetrieveData(); + const currentUser = firebase.native.auth().currentUser; - const credential = firebase.native.auth.EmailAuthProvider.credential( - email, - pass - ); + // Test + const credential = firebase.native.auth.EmailAuthProvider.credential( + email, + pass + ); - return currentUser - .linkAndRetrieveDataWithCredential(credential) - .then(linkedUserCredential => { - linkedUserCredential.should.be.an.Object(); - const linkedUser = linkedUserCredential.user; - linkedUser.should.be.an.Object(); - linkedUser.should.equal(firebase.native.auth().currentUser); - linkedUser.uid.should.be.a.String(); - linkedUser.toJSON().should.be.an.Object(); - // iOS and Android are inconsistent in returning lowercase / mixed case - linkedUser - .toJSON() - .email.toLowerCase() - .should.eql(email.toLowerCase()); - linkedUser.isAnonymous.should.equal(false); - linkedUser.providerId.should.equal('firebase'); - // TODO: iOS is incorrect, passes on Android - // const additionalUserInfo = linkedUserCredential.additionalUserInfo; - // additionalUserInfo.should.be.an.Object(); - // additionalUserInfo.isNewUser.should.equal(false); - return firebase.native.auth().signOut(); - }) - .catch(error => - firebase.native - .auth() - .signOut() - .then(() => Promise.reject(error)) - ); - }; + const linkedUserCredential = await currentUser.linkAndRetrieveDataWithCredential( + credential + ); - return firebase.native - .auth() - .signInAnonymously() - .then(successCb); + // Assertions + const linkedUser = linkedUserCredential.user; + linkedUser.should.be.an.Object(); + linkedUser.should.equal(firebase.native.auth().currentUser); + linkedUser.email.toLowerCase().should.equal(email.toLowerCase()); + linkedUser.isAnonymous.should.equal(false); + linkedUser.providerId.should.equal('firebase'); + linkedUser.providerData.should.be.an.Array(); + linkedUser.providerData.length.should.equal(1); + + // Clean up + await firebase.native.auth().currentUser.delete(); }); - it('it should error on link anon <-> email if email already exists', () => { + it('should error on link anon <-> email if email already exists', async () => { const email = 'test@test.com'; const pass = 'test1234'; - const successCb = currentUser => { - currentUser.should.be.an.Object(); - currentUser.uid.should.be.a.String(); - currentUser.toJSON().should.be.an.Object(); - should.equal(currentUser.toJSON().email, null); - currentUser.isAnonymous.should.equal(true); - currentUser.providerId.should.equal('firebase'); - firebase.native.auth().currentUser.uid.should.be.a.String(); + await firebase.native.auth().signInAnonymouslyAndRetrieveData(); + const currentUser = firebase.native.auth().currentUser; + // Test + try { const credential = firebase.native.auth.EmailAuthProvider.credential( email, pass ); + await currentUser.linkAndRetrieveDataWithCredential(credential); - return currentUser - .linkAndRetrieveDataWithCredential(credential) - .then(() => - firebase.native - .auth() - .signOut() - .then(() => Promise.reject(new Error('Did not error on link'))) - ) - .catch(error => - firebase.native - .auth() - .signOut() - .then(() => { - error.code.should.equal('auth/email-already-in-use'); - error.message.should.equal( - 'The email address is already in use by another account.' - ); - return Promise.resolve(); - }) - ); - }; + // Clean up + await firebase.native.auth().signOut(); - return firebase.native - .auth() - .signInAnonymously() - .then(successCb); + // Reject + Promise.reject(new Error('Did not error on link')); + } catch (error) { + // Assertions + error.code.should.equal('auth/email-already-in-use'); + error.message.should.equal( + 'The email address is already in use by another account.' + ); + + // Clean up + await firebase.native.auth().currentUser.delete(); + } }); }); - context('signOut()', () => { - it('it should reject signOut if no currentUser', () => - new Promise((resolve, reject) => { - if (firebase.native.auth().currentUser) { - return reject( - new Error( - `A user is currently signed in. ${ - firebase.native.auth().currentUser.uid - }` - ) - ); - } + context('reauthenticateWithCredential()', () => { + it('should reauthenticate correctly', async () => { + const random = randomString(12, '#aA'); + const email = `${random}@${random}.com`; + const pass = random; - const successCb = tryCatch(() => { - reject(new Error('No signOut error returned')); - }, reject); + await firebase.native + .auth() + .createUserAndRetrieveDataWithEmailAndPassword(email, pass); - const failureCb = tryCatch(error => { - error.code.should.equal('auth/no-current-user'); - error.message.should.equal('No user currently signed in.'); - resolve(); - }, reject); + // Test + const credential = firebase.native.auth.EmailAuthProvider.credential( + email, + pass + ); + await firebase.native + .auth() + .currentUser.reauthenticateWithCredential(credential); - return firebase.native - .auth() - .signOut() - .then(successCb) - .catch(failureCb); - })); + // Assertions + const currentUser = firebase.native.auth().currentUser; + currentUser.email.should.equal(email.toLowerCase()); + + // Clean up + await firebase.native.auth().currentUser.delete(); + }); + }); + + context('reauthenticateAndRetrieveDataWithCredential()', () => { + it('should reauthenticate correctly', async () => { + const random = randomString(12, '#aA'); + const email = `${random}@${random}.com`; + const pass = random; + + await firebase.native + .auth() + .createUserAndRetrieveDataWithEmailAndPassword(email, pass); + + // Test + const credential = firebase.native.auth.EmailAuthProvider.credential( + email, + pass + ); + await firebase.native + .auth() + .currentUser.reauthenticateAndRetrieveDataWithCredential(credential); + + // Assertions + const currentUser = firebase.native.auth().currentUser; + currentUser.email.should.equal(email.toLowerCase()); + + // Clean up + await firebase.native.auth().currentUser.delete(); + }); + }); + + context('reload()', () => { + it('should not error', async () => { + await firebase.native.auth().signInAnonymouslyAndRetrieveData(); + + try { + await firebase.native.auth().currentUser.reload(); + await firebase.native.auth().signOut(); + } catch (error) { + // Reject + await firebase.native.auth().signOut(); + Promise.reject(new Error('reload() caused an error', error)); + } + }); + }); + + context('sendEmailVerification()', () => { + it('should not error', async () => { + const random = randomString(12, '#aA'); + const email = `${random}@${random}.com`; + const pass = random; + + await firebase.native + .auth() + .createUserAndRetrieveDataWithEmailAndPassword(email, pass); + + try { + await firebase.native.auth().currentUser.sendEmailVerification(); + await firebase.native.auth().currentUser.delete(); + } catch (error) { + // Reject + await firebase.native.auth().currentUser.delete(); + Promise.reject( + new Error('sendEmailVerification() caused an error', error) + ); + } + }); + }); + + context('unlink()', () => { + it('should unlink the email address', async () => { + const random = randomString(12, '#aA'); + const email = `${random}@${random}.com`; + const pass = random; + + await firebase.native.auth().signInAnonymouslyAndRetrieveData(); + const currentUser = firebase.native.auth().currentUser; + + const credential = firebase.native.auth.EmailAuthProvider.credential( + email, + pass + ); + await currentUser.linkAndRetrieveDataWithCredential(credential); + + // Test + await currentUser.unlink( + firebase.native.auth.EmailAuthProvider.PROVIDER_ID + ); + + // Assertions + const unlinkedUser = firebase.native.auth().currentUser; + unlinkedUser.providerData.should.be.an.Array(); + unlinkedUser.providerData.length.should.equal(0); + + // Clean up + await firebase.native.auth().currentUser.delete(); + }); + }); + + context('updateEmail()', () => { + it('should update the email address', async () => { + const random = randomString(12, '#aA'); + const random2 = randomString(12, '#aA'); + const email = `${random}@${random}.com`; + const email2 = `${random2}@${random2}.com`; + const pass = random; + + // Setup + await firebase.native + .auth() + .createUserAndRetrieveDataWithEmailAndPassword(email, pass); + firebase.native + .auth() + .currentUser.email.toLowerCase() + .should.equal(email.toLowerCase()); + + // Update user email + await firebase.native.auth().currentUser.updateEmail(email2); + + // Assertions + firebase.native + .auth() + .currentUser.email.toLowerCase() + .should.equal(email2.toLowerCase()); + + // Clean up + await firebase.native.auth().currentUser.delete(); + }); + }); + + context('updatePassword()', () => { + it('should update the password', async () => { + const random = randomString(12, '#aA'); + const random2 = randomString(12, '#aA'); + const email = `${random}@${random}.com`; + const pass = random; + const pass2 = random2; + + // Setup + await firebase.native + .auth() + .createUserAndRetrieveDataWithEmailAndPassword(email, pass); + + // Update user password + await firebase.native.auth().currentUser.updatePassword(pass2); + + // Sign out + await firebase.native.auth().signOut(); + + // Log in with the new password + await firebase.native + .auth() + .signInAndRetrieveDataWithEmailAndPassword(email, pass2); + + // Assertions + firebase.native.auth().currentUser.should.be.an.Object(); + firebase.native + .auth() + .currentUser.email.should.equal(email.toLowerCase()); + + // Clean up + await firebase.native.auth().currentUser.delete(); + }); + }); + + context('updateProfile()', () => { + it('should update the profile', async () => { + const random = randomString(12, '#aA'); + const email = `${random}@${random}.com`; + const pass = random; + const displayName = random; + const photoURL = `http://${random}.com/${random}.jpg`; + + // Setup + await firebase.native + .auth() + .createUserAndRetrieveDataWithEmailAndPassword(email, pass); + + // Update user profile + await firebase.native.auth().currentUser.updateProfile({ + displayName, + photoURL, + }); + + // Assertions + const user = firebase.native.auth().currentUser; + user.should.be.an.Object(); + user.email.should.equal(email.toLowerCase()); + user.displayName.should.equal(displayName); + user.photoURL.should.equal(photoURL); + + // Clean up + await firebase.native.auth().currentUser.delete(); + }); }); context('linkWithPhoneNumber()', () => { @@ -387,9 +494,7 @@ export default (userTests = ({ tryCatch, context, describe, it, firebase }) => { context('refreshToken', () => { it('should throw an unsupported error', async () => { await firebase.native.auth().signInAnonymouslyAndRetrieveData(); - (() => { - firebase.native.auth().currentUser.refreshToken; - }).should.throw( + (() => firebase.native.auth().currentUser.refreshToken).should.throw( 'User.refreshToken is unsupported by the native Firebase SDKs.' ); await firebase.native.auth().signOut();