From 4c92b4e72e2bf2ed24ff27c5802724fa1a2de1e8 Mon Sep 17 00:00:00 2001 From: Angel Date: Wed, 21 Jun 2023 21:59:10 +0000 Subject: [PATCH 01/23] added authStateReady function --- packages/auth/src/core/auth/auth_impl.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/packages/auth/src/core/auth/auth_impl.ts b/packages/auth/src/core/auth/auth_impl.ts index d7308c03fcc..5fdcd7e0715 100644 --- a/packages/auth/src/core/auth/auth_impl.ts +++ b/packages/auth/src/core/auth/auth_impl.ts @@ -467,6 +467,19 @@ export class AuthImpl implements AuthInternal, _FirebaseService { ); } + authStateReady(): Promise { + return new Promise((resolve, reject) => { + if(this.currentUser){ + resolve(); + }else{ + const unsubscribe = this.onAuthStateChanged(() => { + unsubscribe(); + resolve(); + }, reject); + } + }); + } + toJSON(): object { return { apiKey: this.config.apiKey, From f9c21bbf5ea61d79a98e6c4cf569be027b21a9df Mon Sep 17 00:00:00 2001 From: Angel Date: Wed, 21 Jun 2023 21:59:51 +0000 Subject: [PATCH 02/23] updated Auth interface --- common/api-review/auth.api.md | 1 + 1 file changed, 1 insertion(+) diff --git a/common/api-review/auth.api.md b/common/api-review/auth.api.md index f53b81f8280..74ab8839fe0 100644 --- a/common/api-review/auth.api.md +++ b/common/api-review/auth.api.md @@ -81,6 +81,7 @@ export function applyActionCode(auth: Auth, oobCode: string): Promise; // @public export interface Auth { readonly app: FirebaseApp; + authStateReady(): Promise; beforeAuthStateChanged(callback: (user: User | null) => void | Promise, onAbort?: () => void): Unsubscribe; readonly config: Config; readonly currentUser: User | null; From 4dda61baf15310d568c3b8dc2c20edd8875d032f Mon Sep 17 00:00:00 2001 From: Angel Date: Wed, 21 Jun 2023 22:00:34 +0000 Subject: [PATCH 03/23] added unit tests on authStateReady --- packages/auth/src/core/auth/auth_impl.test.ts | 116 ++++++++++++++++++ 1 file changed, 116 insertions(+) diff --git a/packages/auth/src/core/auth/auth_impl.test.ts b/packages/auth/src/core/auth/auth_impl.test.ts index 5435489c878..8638099f466 100644 --- a/packages/auth/src/core/auth/auth_impl.test.ts +++ b/packages/auth/src/core/auth/auth_impl.test.ts @@ -786,4 +786,120 @@ describe('core/auth/auth_impl', () => { expect(auth._getRecaptchaConfig()).to.eql(cachedRecaptchaConfigOFF); }); }); + + describe('AuthStateReady', () => { + let user: UserInternal; + let onAuthStateChangedCallback: sinon.SinonSpy; + let authStateChangedSpy: sinon.SinonSpy; + + beforeEach(async() => { + user = testUser(auth, 'uid'); + + onAuthStateChangedCallback = sinon.spy(); + auth.onAuthStateChanged(onAuthStateChangedCallback); + authStateChangedSpy = sinon.spy(auth, "onAuthStateChanged"); + + await auth._updateCurrentUser(null); + }); + + //case one: if(this.currentUser) is true --> resolves immediately + it("immediately returns resolved promise if the user is previously logged in", async () => { + + expect(onAuthStateChangedCallback).to.be.calledOnce; + await auth._updateCurrentUser(user); + expect(onAuthStateChangedCallback).to.be.calledTwice; + + await auth.authStateReady().then(() => { + expect(authStateChangedSpy).to.not.have.been.called; + expect(auth.currentUser).to.eq(user); + }).catch(error => { + throw new Error(error); + }); + + expect(onAuthStateChangedCallback).to.be.calledTwice; + }); + + //case two: if(this.currentUser) is false --> calls onAuthStateChanged + it("returns resolved promise once the user is initialized to object of type UserInternal",async () => { + + expect(authStateChangedSpy).to.not.have.been.called; + const promiseVar = auth.authStateReady(); + expect(authStateChangedSpy).to.be.calledOnce; + + await auth._updateCurrentUser(user); + + await promiseVar.then(() => { + // onAuthStateChangedCallback(); + expect(auth.currentUser).to.eq(user); + }).catch(error => { + throw new Error(error); + }); + + expect(authStateChangedSpy).to.be.calledOnce; + // expect(onAuthStateChangedCallback).to.be.calledThrice; + }); + + //case three: if user logged out more than once, promise should still be resolved with currentUser remained as null + it("resolves promise with currentUser remains as null when user logs out more than once", async () => { + + expect(authStateChangedSpy).to.not.have.been.called; + const promiseVar = auth.authStateReady(); + expect(authStateChangedSpy).to.be.calledOnce; + + await auth._updateCurrentUser(null); + + await promiseVar.then(() => { + expect(auth.currentUser).to.eq(null); + }).catch(error => { + throw new Error(error); + }); + + expect(authStateChangedSpy).to.be.calledOnce; + }); + + //case four: user sign in failed, expect promise to resolve and allow currentUser to be null. + it("resolves the promise with currentUser remain null during log in failure", async () => { + + expect(authStateChangedSpy).to.not.have.been.called; + const promiseVar = auth.authStateReady(); + expect(authStateChangedSpy).to.be.calledOnce; + + const auth2 = await testAuth(); + Object.assign(auth2.config, { apiKey: 'not-the-right-auth' }); + const user = testUser(auth2, 'uid'); + await expect(auth.updateCurrentUser(user)).to.be.rejectedWith( + FirebaseError, + 'auth/invalid-user-token' + ); + + await promiseVar.then(() => { + expect(auth.currentUser).to.eq(null); + }).catch(error => { + throw new Error(error); + }); + + expect(authStateChangedSpy).to.be.calledOnce; + }); + + //case five: user sign in delay, promise should be resolved after delay. + it("resolves the promise in a delayed user log in process", async () => { + + setTimeout(async () => { + await auth._updateCurrentUser(user); + }, 5000); + + const promiseVar = auth.authStateReady(); + expect(auth.currentUser).to.eq(null); + expect(authStateChangedSpy).to.be.calledOnce; + + await setTimeout(() => { + promiseVar.then(async () => { + await expect(auth.currentUser).to.eq(user); + }).catch(error => { + throw new Error(error); + }); + }, 10000); + }); + }); + }); From 06d202d28ac059cdef085d345c0a881bd71cb236 Mon Sep 17 00:00:00 2001 From: Angel Date: Wed, 21 Jun 2023 22:02:15 +0000 Subject: [PATCH 04/23] updated demo app --- packages/auth/demo/src/index.js | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/packages/auth/demo/src/index.js b/packages/auth/demo/src/index.js index a3abe1e3f37..7d765b97db6 100644 --- a/packages/auth/demo/src/index.js +++ b/packages/auth/demo/src/index.js @@ -122,7 +122,7 @@ function activeUser() { if (type === 'lastUser') { return lastUser; } else { - return auth.currentUser; + return auth.authStateReady().then(auth.currentUser); } } @@ -172,7 +172,8 @@ function refreshUserData() { // Show enrolled second factors if available for the active user. showMultiFactorStatus(user); // Change color. - if (user === auth.currentUser) { + let myUser = auth.authStateReady().then(auth.currentUser); + if (user === myUser) { $('#user-info').removeClass('last-user'); $('#user-info').addClass('current-user'); } else { @@ -1120,7 +1121,7 @@ function onRefreshToken() { * Signs out the user. */ function onSignOut() { - setLastUser(auth.currentUser); + setLastUser(auth.authStateReady().then(auth.currentUser)); auth.signOut().then(signOut, onAuthError); } @@ -1654,12 +1655,15 @@ function checkDatabaseAuthAccess() { let dbRef; let dbPath; let errMessage; + let myCurUser = auth.authStateReady().then(auth.currentUser); // Run this check only when Database module is available. if ( typeof firebase !== 'undefined' && typeof firebase.database !== 'undefined' ) { - if (lastUser && !auth.currentUser) { + //Angel: subject to race conditions where the user was in the process of logging out when auth.currentUser is checked; + // However, if we use authStateReady here, the promise might never be resolved if the log out process has been completed earlier + if (lastUser && !myCurUser) { dbPath = 'users/' + lastUser.uid; // After sign out, confirm read/write access to users/$user_id blocked. dbRef = firebase.database().ref(dbPath); @@ -1704,8 +1708,8 @@ function checkDatabaseAuthAccess() { ); }); }); - } else if (auth.currentUser) { - dbPath = 'users/' + auth.currentUser.uid; + } else if (myCurUser) { + dbPath = 'users/' + myCurUser.uid; // Confirm read/write access to users/$user_id allowed. dbRef = firebase.database().ref(dbPath); dbRef From 16c022ca2bbb0d6b748681d0fe79bbe82a399f19 Mon Sep 17 00:00:00 2001 From: Angel Date: Wed, 21 Jun 2023 22:19:35 +0000 Subject: [PATCH 05/23] added authStateReady to Auth interface --- packages/auth/src/model/public_types.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/auth/src/model/public_types.ts b/packages/auth/src/model/public_types.ts index 30f202a1b8e..83737b9df39 100644 --- a/packages/auth/src/model/public_types.ts +++ b/packages/auth/src/model/public_types.ts @@ -291,6 +291,11 @@ export interface Auth { error?: ErrorFn, completed?: CompleteFn ): Unsubscribe; + /** + * return a promise that resolves immediately when the initial + * auth state is settled and currentUser is available + */ + authStateReady():Promise; /** The currently signed-in user (or null). */ readonly currentUser: User | null; /** The current emulator configuration (or null). */ From 4e81db4717adaf566cf7abad525006656889ad3e Mon Sep 17 00:00:00 2001 From: Angel Date: Wed, 21 Jun 2023 23:09:36 +0000 Subject: [PATCH 06/23] fixed formatting issue --- packages/auth/demo/src/index.js | 2 +- packages/auth/src/core/auth/auth_impl.test.ts | 91 ++++++++++--------- packages/auth/src/core/auth/auth_impl.ts | 4 +- packages/auth/src/model/public_types.ts | 4 +- 4 files changed, 53 insertions(+), 48 deletions(-) diff --git a/packages/auth/demo/src/index.js b/packages/auth/demo/src/index.js index 7d765b97db6..4af3c00dbf1 100644 --- a/packages/auth/demo/src/index.js +++ b/packages/auth/demo/src/index.js @@ -1661,7 +1661,7 @@ function checkDatabaseAuthAccess() { typeof firebase !== 'undefined' && typeof firebase.database !== 'undefined' ) { - //Angel: subject to race conditions where the user was in the process of logging out when auth.currentUser is checked; + //Angel: subject to race conditions where the user was in the process of logging out when auth.currentUser is checked; // However, if we use authStateReady here, the promise might never be resolved if the log out process has been completed earlier if (lastUser && !myCurUser) { dbPath = 'users/' + lastUser.uid; diff --git a/packages/auth/src/core/auth/auth_impl.test.ts b/packages/auth/src/core/auth/auth_impl.test.ts index 8638099f466..c6480dde1fe 100644 --- a/packages/auth/src/core/auth/auth_impl.test.ts +++ b/packages/auth/src/core/auth/auth_impl.test.ts @@ -792,74 +792,77 @@ describe('core/auth/auth_impl', () => { let onAuthStateChangedCallback: sinon.SinonSpy; let authStateChangedSpy: sinon.SinonSpy; - beforeEach(async() => { + beforeEach(async () => { user = testUser(auth, 'uid'); onAuthStateChangedCallback = sinon.spy(); auth.onAuthStateChanged(onAuthStateChangedCallback); - authStateChangedSpy = sinon.spy(auth, "onAuthStateChanged"); + authStateChangedSpy = sinon.spy(auth, 'onAuthStateChanged'); - await auth._updateCurrentUser(null); + await auth._updateCurrentUser(null); }); //case one: if(this.currentUser) is true --> resolves immediately - it("immediately returns resolved promise if the user is previously logged in", async () => { - + it('immediately returns resolved promise if the user is previously logged in', async () => { expect(onAuthStateChangedCallback).to.be.calledOnce; await auth._updateCurrentUser(user); expect(onAuthStateChangedCallback).to.be.calledTwice; - await auth.authStateReady().then(() => { - expect(authStateChangedSpy).to.not.have.been.called; - expect(auth.currentUser).to.eq(user); - }).catch(error => { - throw new Error(error); - }); + await auth + .authStateReady() + .then(() => { + expect(authStateChangedSpy).to.not.have.been.called; + expect(auth.currentUser).to.eq(user); + }) + .catch(error => { + throw new Error(error); + }); expect(onAuthStateChangedCallback).to.be.calledTwice; }); //case two: if(this.currentUser) is false --> calls onAuthStateChanged - it("returns resolved promise once the user is initialized to object of type UserInternal",async () => { - + it('returns resolved promise once the user is initialized to object of type UserInternal', async () => { expect(authStateChangedSpy).to.not.have.been.called; const promiseVar = auth.authStateReady(); expect(authStateChangedSpy).to.be.calledOnce; await auth._updateCurrentUser(user); - - await promiseVar.then(() => { - // onAuthStateChangedCallback(); - expect(auth.currentUser).to.eq(user); - }).catch(error => { - throw new Error(error); - }); + + await promiseVar + .then(() => { + // onAuthStateChangedCallback(); + expect(auth.currentUser).to.eq(user); + }) + .catch(error => { + throw new Error(error); + }); expect(authStateChangedSpy).to.be.calledOnce; // expect(onAuthStateChangedCallback).to.be.calledThrice; }); //case three: if user logged out more than once, promise should still be resolved with currentUser remained as null - it("resolves promise with currentUser remains as null when user logs out more than once", async () => { - + it('resolves promise with currentUser remains as null when user logs out more than once', async () => { expect(authStateChangedSpy).to.not.have.been.called; const promiseVar = auth.authStateReady(); expect(authStateChangedSpy).to.be.calledOnce; await auth._updateCurrentUser(null); - - await promiseVar.then(() => { - expect(auth.currentUser).to.eq(null); - }).catch(error => { - throw new Error(error); - }); + + await promiseVar + .then(() => { + expect(auth.currentUser).to.eq(null); + }) + .catch(error => { + throw new Error(error); + }); expect(authStateChangedSpy).to.be.calledOnce; }); //case four: user sign in failed, expect promise to resolve and allow currentUser to be null. - it("resolves the promise with currentUser remain null during log in failure", async () => { - + it('resolves the promise with currentUser remain null during log in failure', async () => { expect(authStateChangedSpy).to.not.have.been.called; const promiseVar = auth.authStateReady(); expect(authStateChangedSpy).to.be.calledOnce; @@ -872,18 +875,19 @@ describe('core/auth/auth_impl', () => { 'auth/invalid-user-token' ); - await promiseVar.then(() => { - expect(auth.currentUser).to.eq(null); - }).catch(error => { - throw new Error(error); - }); + await promiseVar + .then(() => { + expect(auth.currentUser).to.eq(null); + }) + .catch(error => { + throw new Error(error); + }); expect(authStateChangedSpy).to.be.calledOnce; }); //case five: user sign in delay, promise should be resolved after delay. - it("resolves the promise in a delayed user log in process", async () => { - + it('resolves the promise in a delayed user log in process', async () => { setTimeout(async () => { await auth._updateCurrentUser(user); }, 5000); @@ -893,13 +897,14 @@ describe('core/auth/auth_impl', () => { expect(authStateChangedSpy).to.be.calledOnce; await setTimeout(() => { - promiseVar.then(async () => { - await expect(auth.currentUser).to.eq(user); - }).catch(error => { - throw new Error(error); - }); + promiseVar + .then(async () => { + await expect(auth.currentUser).to.eq(user); + }) + .catch(error => { + throw new Error(error); + }); }, 10000); }); }); - }); diff --git a/packages/auth/src/core/auth/auth_impl.ts b/packages/auth/src/core/auth/auth_impl.ts index 5fdcd7e0715..be4c3b2d7c7 100644 --- a/packages/auth/src/core/auth/auth_impl.ts +++ b/packages/auth/src/core/auth/auth_impl.ts @@ -469,9 +469,9 @@ export class AuthImpl implements AuthInternal, _FirebaseService { authStateReady(): Promise { return new Promise((resolve, reject) => { - if(this.currentUser){ + if (this.currentUser) { resolve(); - }else{ + } else { const unsubscribe = this.onAuthStateChanged(() => { unsubscribe(); resolve(); diff --git a/packages/auth/src/model/public_types.ts b/packages/auth/src/model/public_types.ts index 83737b9df39..497d2112d21 100644 --- a/packages/auth/src/model/public_types.ts +++ b/packages/auth/src/model/public_types.ts @@ -292,10 +292,10 @@ export interface Auth { completed?: CompleteFn ): Unsubscribe; /** - * return a promise that resolves immediately when the initial + * return a promise that resolves immediately when the initial * auth state is settled and currentUser is available */ - authStateReady():Promise; + authStateReady(): Promise; /** The currently signed-in user (or null). */ readonly currentUser: User | null; /** The current emulator configuration (or null). */ From 3bbc80b04d5fd57de5016061b924098e432cfc2f Mon Sep 17 00:00:00 2001 From: Angel Date: Thu, 22 Jun 2023 08:19:23 +0000 Subject: [PATCH 07/23] updated authStateReady calls in demo app --- packages/auth/demo/src/index.js | 199 +++++++++++++++++--------------- 1 file changed, 107 insertions(+), 92 deletions(-) diff --git a/packages/auth/demo/src/index.js b/packages/auth/demo/src/index.js index 4af3c00dbf1..79501c1b969 100644 --- a/packages/auth/demo/src/index.js +++ b/packages/auth/demo/src/index.js @@ -122,7 +122,11 @@ function activeUser() { if (type === 'lastUser') { return lastUser; } else { - return auth.authStateReady().then(auth.currentUser); + auth.authStateReady().then( () => { + return auth.currentUser; + }).catch(error => { + throw new Error(error); + }); } } @@ -172,14 +176,17 @@ function refreshUserData() { // Show enrolled second factors if available for the active user. showMultiFactorStatus(user); // Change color. - let myUser = auth.authStateReady().then(auth.currentUser); - if (user === myUser) { - $('#user-info').removeClass('last-user'); - $('#user-info').addClass('current-user'); - } else { - $('#user-info').removeClass('current-user'); - $('#user-info').addClass('last-user'); - } + auth.authStateReady().then(() => { + if (user === auth.currentUser) { + $('#user-info').removeClass('last-user'); + $('#user-info').addClass('current-user'); + } else { + $('#user-info').removeClass('current-user'); + $('#user-info').addClass('last-user'); + } + }).catch(error => { + throw new Error(error); + }); } else { $('.profile').slideUp(); $('body').removeClass('user-info-displayed'); @@ -1121,8 +1128,13 @@ function onRefreshToken() { * Signs out the user. */ function onSignOut() { - setLastUser(auth.authStateReady().then(auth.currentUser)); - auth.signOut().then(signOut, onAuthError); + auth.authStateReady().then(() => { + setLastUser(auth.currentUser); + auth.signOut().then(signOut, onAuthError); + }) + .catch(error => { + throw new Error(error); + }); } /** @@ -1655,91 +1667,94 @@ function checkDatabaseAuthAccess() { let dbRef; let dbPath; let errMessage; - let myCurUser = auth.authStateReady().then(auth.currentUser); - // Run this check only when Database module is available. - if ( - typeof firebase !== 'undefined' && - typeof firebase.database !== 'undefined' - ) { - //Angel: subject to race conditions where the user was in the process of logging out when auth.currentUser is checked; - // However, if we use authStateReady here, the promise might never be resolved if the log out process has been completed earlier - if (lastUser && !myCurUser) { - dbPath = 'users/' + lastUser.uid; - // After sign out, confirm read/write access to users/$user_id blocked. - dbRef = firebase.database().ref(dbPath); - dbRef - .set({ - 'test': randomString - }) - .then(() => { - alertError( - 'Error: Unauthenticated write to Database node ' + - dbPath + - ' unexpectedly succeeded!' - ); - }) - .catch(error => { - errMessage = error.message.toLowerCase(); - // Permission denied error should be thrown. - if (errMessage.indexOf('permission_denied') === -1) { - alertError('Error: ' + error.code); - return; - } - dbRef - .once('value') - .then(() => { - alertError( - 'Error: Unauthenticated read to Database node ' + - dbPath + - ' unexpectedly succeeded!' - ); - }) - .catch(error => { - errMessage = error.message.toLowerCase(); - // Permission denied error should be thrown. - if (errMessage.indexOf('permission_denied') === -1) { - alertError('Error: ' + error.code); - return; - } + auth.authStateReady().then(() => { + // Run this check only when Database module is available. + let myCurUser = auth.currentUser; + if ( + typeof firebase !== 'undefined' && + typeof firebase.database !== 'undefined' + ) { + if (lastUser && !myCurUser) { + dbPath = 'users/' + lastUser.uid; + // After sign out, confirm read/write access to users/$user_id blocked. + dbRef = firebase.database().ref(dbPath); + dbRef + .set({ + 'test': randomString + }) + .then(() => { + alertError( + 'Error: Unauthenticated write to Database node ' + + dbPath + + ' unexpectedly succeeded!' + ); + }) + .catch(error => { + errMessage = error.message.toLowerCase(); + // Permission denied error should be thrown. + if (errMessage.indexOf('permission_denied') === -1) { + alertError('Error: ' + error.code); + return; + } + dbRef + .once('value') + .then(() => { + alertError( + 'Error: Unauthenticated read to Database node ' + + dbPath + + ' unexpectedly succeeded!' + ); + }) + .catch(error => { + errMessage = error.message.toLowerCase(); + // Permission denied error should be thrown. + if (errMessage.indexOf('permission_denied') === -1) { + alertError('Error: ' + error.code); + return; + } + log( + 'Unauthenticated read/write to Database node ' + + dbPath + + ' failed as expected!' + ); + }); + }); + } else if (myCurUser) { + dbPath = 'users/' + myCurUser.uid; + // Confirm read/write access to users/$user_id allowed. + dbRef = firebase.database().ref(dbPath); + dbRef + .set({ + 'test': randomString + }) + .then(() => { + return dbRef.once('value'); + }) + .then(snapshot => { + if (snapshot.val().test === randomString) { + // read/write successful. log( - 'Unauthenticated read/write to Database node ' + + 'Authenticated read/write to Database node ' + dbPath + - ' failed as expected!' + ' succeeded!' ); - }); - }); - } else if (myCurUser) { - dbPath = 'users/' + myCurUser.uid; - // Confirm read/write access to users/$user_id allowed. - dbRef = firebase.database().ref(dbPath); - dbRef - .set({ - 'test': randomString - }) - .then(() => { - return dbRef.once('value'); - }) - .then(snapshot => { - if (snapshot.val().test === randomString) { - // read/write successful. - log( - 'Authenticated read/write to Database node ' + - dbPath + - ' succeeded!' - ); - } else { - throw new Error( - 'Authenticated read/write to Database node ' + dbPath + ' failed!' - ); - } - // Clean up: clear that node's content. - return dbRef.remove(); - }) - .catch(error => { - alertError('Error: ' + error.code); - }); + } else { + throw new Error( + 'Authenticated read/write to Database node ' + dbPath + ' failed!' + ); + } + // Clean up: clear that node's content. + return dbRef.remove(); + }) + .catch(error => { + alertError('Error: ' + error.code); + }); + } } - } + }) + .catch(error => { + throw new Error(error); + }); } /** From 0a06aa21c69e41e9f45fc34e028d8d5001830156 Mon Sep 17 00:00:00 2001 From: Angel Date: Thu, 22 Jun 2023 08:20:45 +0000 Subject: [PATCH 08/23] fixed formatting issues --- packages/auth/demo/src/index.js | 224 +++++++++++++++++--------------- 1 file changed, 118 insertions(+), 106 deletions(-) diff --git a/packages/auth/demo/src/index.js b/packages/auth/demo/src/index.js index 79501c1b969..134d699d537 100644 --- a/packages/auth/demo/src/index.js +++ b/packages/auth/demo/src/index.js @@ -122,11 +122,14 @@ function activeUser() { if (type === 'lastUser') { return lastUser; } else { - auth.authStateReady().then( () => { - return auth.currentUser; - }).catch(error => { - throw new Error(error); - }); + auth + .authStateReady() + .then(() => { + return auth.currentUser; + }) + .catch(error => { + throw new Error(error); + }); } } @@ -176,17 +179,20 @@ function refreshUserData() { // Show enrolled second factors if available for the active user. showMultiFactorStatus(user); // Change color. - auth.authStateReady().then(() => { - if (user === auth.currentUser) { - $('#user-info').removeClass('last-user'); - $('#user-info').addClass('current-user'); - } else { - $('#user-info').removeClass('current-user'); - $('#user-info').addClass('last-user'); - } - }).catch(error => { - throw new Error(error); - }); + auth + .authStateReady() + .then(() => { + if (user === auth.currentUser) { + $('#user-info').removeClass('last-user'); + $('#user-info').addClass('current-user'); + } else { + $('#user-info').removeClass('current-user'); + $('#user-info').addClass('last-user'); + } + }) + .catch(error => { + throw new Error(error); + }); } else { $('.profile').slideUp(); $('body').removeClass('user-info-displayed'); @@ -1128,13 +1134,15 @@ function onRefreshToken() { * Signs out the user. */ function onSignOut() { - auth.authStateReady().then(() => { - setLastUser(auth.currentUser); - auth.signOut().then(signOut, onAuthError); - }) - .catch(error => { - throw new Error(error); - }); + auth + .authStateReady() + .then(() => { + setLastUser(auth.currentUser); + auth.signOut().then(signOut, onAuthError); + }) + .catch(error => { + throw new Error(error); + }); } /** @@ -1667,94 +1675,98 @@ function checkDatabaseAuthAccess() { let dbRef; let dbPath; let errMessage; - auth.authStateReady().then(() => { - // Run this check only when Database module is available. - let myCurUser = auth.currentUser; - if ( - typeof firebase !== 'undefined' && - typeof firebase.database !== 'undefined' - ) { - if (lastUser && !myCurUser) { - dbPath = 'users/' + lastUser.uid; - // After sign out, confirm read/write access to users/$user_id blocked. - dbRef = firebase.database().ref(dbPath); - dbRef - .set({ - 'test': randomString - }) - .then(() => { - alertError( - 'Error: Unauthenticated write to Database node ' + - dbPath + - ' unexpectedly succeeded!' - ); - }) - .catch(error => { - errMessage = error.message.toLowerCase(); - // Permission denied error should be thrown. - if (errMessage.indexOf('permission_denied') === -1) { - alertError('Error: ' + error.code); - return; - } - dbRef - .once('value') - .then(() => { - alertError( - 'Error: Unauthenticated read to Database node ' + + auth + .authStateReady() + .then(() => { + // Run this check only when Database module is available. + let myCurUser = auth.currentUser; + if ( + typeof firebase !== 'undefined' && + typeof firebase.database !== 'undefined' + ) { + if (lastUser && !myCurUser) { + dbPath = 'users/' + lastUser.uid; + // After sign out, confirm read/write access to users/$user_id blocked. + dbRef = firebase.database().ref(dbPath); + dbRef + .set({ + 'test': randomString + }) + .then(() => { + alertError( + 'Error: Unauthenticated write to Database node ' + + dbPath + + ' unexpectedly succeeded!' + ); + }) + .catch(error => { + errMessage = error.message.toLowerCase(); + // Permission denied error should be thrown. + if (errMessage.indexOf('permission_denied') === -1) { + alertError('Error: ' + error.code); + return; + } + dbRef + .once('value') + .then(() => { + alertError( + 'Error: Unauthenticated read to Database node ' + + dbPath + + ' unexpectedly succeeded!' + ); + }) + .catch(error => { + errMessage = error.message.toLowerCase(); + // Permission denied error should be thrown. + if (errMessage.indexOf('permission_denied') === -1) { + alertError('Error: ' + error.code); + return; + } + log( + 'Unauthenticated read/write to Database node ' + + dbPath + + ' failed as expected!' + ); + }); + }); + } else if (myCurUser) { + dbPath = 'users/' + myCurUser.uid; + // Confirm read/write access to users/$user_id allowed. + dbRef = firebase.database().ref(dbPath); + dbRef + .set({ + 'test': randomString + }) + .then(() => { + return dbRef.once('value'); + }) + .then(snapshot => { + if (snapshot.val().test === randomString) { + // read/write successful. + log( + 'Authenticated read/write to Database node ' + dbPath + - ' unexpectedly succeeded!' + ' succeeded!' ); - }) - .catch(error => { - errMessage = error.message.toLowerCase(); - // Permission denied error should be thrown. - if (errMessage.indexOf('permission_denied') === -1) { - alertError('Error: ' + error.code); - return; - } - log( - 'Unauthenticated read/write to Database node ' + + } else { + throw new Error( + 'Authenticated read/write to Database node ' + dbPath + - ' failed as expected!' + ' failed!' ); - }); - }); - } else if (myCurUser) { - dbPath = 'users/' + myCurUser.uid; - // Confirm read/write access to users/$user_id allowed. - dbRef = firebase.database().ref(dbPath); - dbRef - .set({ - 'test': randomString - }) - .then(() => { - return dbRef.once('value'); - }) - .then(snapshot => { - if (snapshot.val().test === randomString) { - // read/write successful. - log( - 'Authenticated read/write to Database node ' + - dbPath + - ' succeeded!' - ); - } else { - throw new Error( - 'Authenticated read/write to Database node ' + dbPath + ' failed!' - ); - } - // Clean up: clear that node's content. - return dbRef.remove(); - }) - .catch(error => { - alertError('Error: ' + error.code); - }); + } + // Clean up: clear that node's content. + return dbRef.remove(); + }) + .catch(error => { + alertError('Error: ' + error.code); + }); + } } - } - }) - .catch(error => { - throw new Error(error); - }); + }) + .catch(error => { + throw new Error(error); + }); } /** From bb91d24e2d00f99f02fea51914798c301c92a3e1 Mon Sep 17 00:00:00 2001 From: Angel Date: Wed, 28 Jun 2023 18:52:23 +0000 Subject: [PATCH 09/23] fixed demo app to incorporate authStateReady --- packages/auth/demo/src/index.js | 310 ++++++++++++++++---------------- 1 file changed, 156 insertions(+), 154 deletions(-) diff --git a/packages/auth/demo/src/index.js b/packages/auth/demo/src/index.js index 134d699d537..5bc62046ed0 100644 --- a/packages/auth/demo/src/index.js +++ b/packages/auth/demo/src/index.js @@ -114,7 +114,7 @@ const providersIcons = { }; /** - * Returns the active user (i.e. currentUser or lastUser). + * Returns the promise that waits for user to sign in. * @return {!firebase.User} */ function activeUser() { @@ -122,14 +122,25 @@ function activeUser() { if (type === 'lastUser') { return lastUser; } else { - auth - .authStateReady() - .then(() => { - return auth.currentUser; - }) - .catch(error => { - throw new Error(error); - }); + return auth.currentUser; + } +} + +/** + * Returns the active user after sign in (i.e. currentUser or lastUser). + * @return {!firebase.User} + */ +async function getActiveUserAfterSignIn() { + const type = $('input[name=toggle-user-selection]:checked').val(); + if (type === 'lastUser') { + return lastUser; + } else { + try{ + await auth.authStateReady(); + return auth.currentUser; + }catch (e) { + log(e); + } } } @@ -137,66 +148,63 @@ function activeUser() { * Refreshes the current user data in the UI, displaying a user info box if * a user is signed in, or removing it. */ -function refreshUserData() { - if (activeUser()) { - const user = activeUser(); - $('.profile').show(); - $('body').addClass('user-info-displayed'); - $('div.profile-email,span.profile-email').text(user.email || 'No Email'); - $('div.profile-phone,span.profile-phone').text( - user.phoneNumber || 'No Phone' - ); - $('div.profile-uid,span.profile-uid').text(user.uid); - $('div.profile-name,span.profile-name').text(user.displayName || 'No Name'); - $('input.profile-name').val(user.displayName); - $('input.photo-url').val(user.photoURL); - if (user.photoURL != null) { - let photoURL = user.photoURL; - // Append size to the photo URL for Google hosted images to avoid requesting - // the image with its original resolution (using more bandwidth than needed) - // when it is going to be presented in smaller size. - if ( - photoURL.indexOf('googleusercontent.com') !== -1 || - photoURL.indexOf('ggpht.com') !== -1 - ) { - photoURL = photoURL + '?sz=' + $('img.profile-image').height(); +async function refreshUserData() { + try{ + let user = await getActiveUserAfterSignIn(); + if (user) { + $('.profile').show(); + $('body').addClass('user-info-displayed'); + $('div.profile-email,span.profile-email').text(user.email || 'No Email'); + $('div.profile-phone,span.profile-phone').text( + user.phoneNumber || 'No Phone' + ); + $('div.profile-uid,span.profile-uid').text(user.uid); + $('div.profile-name,span.profile-name').text(user.displayName || 'No Name'); + $('input.profile-name').val(user.displayName); + $('input.photo-url').val(user.photoURL); + if (user.photoURL != null) { + let photoURL = user.photoURL; + // Append size to the photo URL for Google hosted images to avoid requesting + // the image with its original resolution (using more bandwidth than needed) + // when it is going to be presented in smaller size. + if ( + photoURL.indexOf('googleusercontent.com') !== -1 || + photoURL.indexOf('ggpht.com') !== -1 + ) { + photoURL = photoURL + '?sz=' + $('img.profile-image').height(); + } + $('img.profile-image').attr('src', photoURL).show(); + } else { + $('img.profile-image').hide(); } - $('img.profile-image').attr('src', photoURL).show(); - } else { - $('img.profile-image').hide(); - } - $('.profile-email-verified').toggle(user.emailVerified); - $('.profile-email-not-verified').toggle(!user.emailVerified); - $('.profile-anonymous').toggle(user.isAnonymous); - // Display/Hide providers icons. - $('.profile-providers').empty(); - if (user['providerData'] && user['providerData'].length) { - const providersCount = user['providerData'].length; - for (let i = 0; i < providersCount; i++) { - addProviderIcon(user['providerData'][i]['providerId']); + $('.profile-email-verified').toggle(user.emailVerified); + $('.profile-email-not-verified').toggle(!user.emailVerified); + $('.profile-anonymous').toggle(user.isAnonymous); + // Display/Hide providers icons. + $('.profile-providers').empty(); + if (user['providerData'] && user['providerData'].length) { + const providersCount = user['providerData'].length; + for (let i = 0; i < providersCount; i++) { + addProviderIcon(user['providerData'][i]['providerId']); + } } + // Show enrolled second factors if available for the active user. + showMultiFactorStatus(user); + // Change color. + if (user === auth.currentUser) { + $('#user-info').removeClass('last-user'); + $('#user-info').addClass('current-user'); + } else { + $('#user-info').removeClass('current-user'); + $('#user-info').addClass('last-user'); + } + } else { + $('.profile').slideUp(); + $('body').removeClass('user-info-displayed'); + $('input.profile-data').val(''); } - // Show enrolled second factors if available for the active user. - showMultiFactorStatus(user); - // Change color. - auth - .authStateReady() - .then(() => { - if (user === auth.currentUser) { - $('#user-info').removeClass('last-user'); - $('#user-info').addClass('current-user'); - } else { - $('#user-info').removeClass('current-user'); - $('#user-info').addClass('last-user'); - } - }) - .catch(error => { - throw new Error(error); - }); - } else { - $('.profile').slideUp(); - $('body').removeClass('user-info-displayed'); - $('input.profile-data').val(''); + } catch (error) { + log(error); } } @@ -288,6 +296,7 @@ function onAuthSuccess(user) { console.log(user); alertSuccess('User authenticated, id: ' + user.uid); refreshUserData(); + } /** @@ -313,7 +322,7 @@ function onAuthError(error) { function signOut() { log('User successfully signed out.'); alertSuccess('User successfully signed out.'); - refreshUserData(); + refreshUserData() } /** @@ -422,7 +431,7 @@ function onSignInWithEmailLink() { /** * Links a user with an email link. */ -function onLinkWithEmailLink() { +async function onLinkWithEmailLink() { const email = $('#link-with-email-link-email').val(); const link = $('#link-with-email-link-link').val() || undefined; const credential = EmailAuthProvider.credentialWithLink(email, link); @@ -435,7 +444,7 @@ function onLinkWithEmailLink() { /** * Re-authenticate a user with email link credential. */ -function onReauthenticateWithEmailLink() { +async function onReauthenticateWithEmailLink() { if (!activeUser()) { alertError( 'No user logged in. Select the "Last User" tab to reauth the previous user.' @@ -446,6 +455,7 @@ function onReauthenticateWithEmailLink() { const link = $('#link-with-email-link-link').val() || undefined; const credential = EmailAuthProvider.credentialWithLink(email, link); // This will not set auth.currentUser to lastUser if the lastUser is reauthenticated. + reauthenticateWithCredential(activeUser(), credential).then(result => { logAdditionalUserInfo(result); refreshUserData(); @@ -456,7 +466,7 @@ function onReauthenticateWithEmailLink() { /** * Re-authenticate a user with email and password. */ -function onReauthenticateWithEmailAndPassword() { +async function onReauthenticateWithEmailAndPassword() { if (!activeUser()) { alertError( 'No user logged in. Select the "Last User" tab to reauth the previous user.' @@ -470,7 +480,7 @@ function onReauthenticateWithEmailAndPassword() { reauthenticateWithCredential(activeUser(), credential).then(result => { logAdditionalUserInfo(result); refreshUserData(); - alertSuccess('User reauthenticated with email/password!'); + alertSuccess('User reauthenticated with email link!'); }, onAuthError); } @@ -1066,7 +1076,7 @@ function onApplyActionCode() { * or not. */ function getIdToken(forceRefresh) { - if (activeUser() == null) { + if (!activeUser()) { alertError('No user logged in.'); return; } @@ -1091,7 +1101,7 @@ function getIdToken(forceRefresh) { * or not */ function getIdTokenResult(forceRefresh) { - if (activeUser() == null) { + if (!activeUser()) { alertError('No user logged in.'); return; } @@ -1670,31 +1680,43 @@ function populateActionCodes() { * } * This applies when Real-time database service is available. */ -function checkDatabaseAuthAccess() { +async function checkDatabaseAuthAccess() { const randomString = Math.floor(Math.random() * 10000000).toString(); let dbRef; let dbPath; let errMessage; - auth - .authStateReady() - .then(() => { - // Run this check only when Database module is available. - let myCurUser = auth.currentUser; - if ( - typeof firebase !== 'undefined' && - typeof firebase.database !== 'undefined' - ) { - if (lastUser && !myCurUser) { - dbPath = 'users/' + lastUser.uid; - // After sign out, confirm read/write access to users/$user_id blocked. - dbRef = firebase.database().ref(dbPath); + // Run this check only when Database module is available. + if ( + typeof firebase !== 'undefined' && + typeof firebase.database !== 'undefined' + ) { + if (lastUser && !auth.currentUser) { + dbPath = 'users/' + lastUser.uid; + // After sign out, confirm read/write access to users/$user_id blocked. + dbRef = firebase.database().ref(dbPath); + dbRef + .set({ + 'test': randomString + }) + .then(() => { + alertError( + 'Error: Unauthenticated write to Database node ' + + dbPath + + ' unexpectedly succeeded!' + ); + }) + .catch(error => { + errMessage = error.message.toLowerCase(); + // Permission denied error should be thrown. + if (errMessage.indexOf('permission_denied') === -1) { + alertError('Error: ' + error.code); + return; + } dbRef - .set({ - 'test': randomString - }) + .once('value') .then(() => { alertError( - 'Error: Unauthenticated write to Database node ' + + 'Error: Unauthenticated read to Database node ' + dbPath + ' unexpectedly succeeded!' ); @@ -1706,67 +1728,47 @@ function checkDatabaseAuthAccess() { alertError('Error: ' + error.code); return; } - dbRef - .once('value') - .then(() => { - alertError( - 'Error: Unauthenticated read to Database node ' + - dbPath + - ' unexpectedly succeeded!' - ); - }) - .catch(error => { - errMessage = error.message.toLowerCase(); - // Permission denied error should be thrown. - if (errMessage.indexOf('permission_denied') === -1) { - alertError('Error: ' + error.code); - return; - } - log( - 'Unauthenticated read/write to Database node ' + - dbPath + - ' failed as expected!' - ); - }); - }); - } else if (myCurUser) { - dbPath = 'users/' + myCurUser.uid; - // Confirm read/write access to users/$user_id allowed. - dbRef = firebase.database().ref(dbPath); - dbRef - .set({ - 'test': randomString - }) - .then(() => { - return dbRef.once('value'); - }) - .then(snapshot => { - if (snapshot.val().test === randomString) { - // read/write successful. - log( - 'Authenticated read/write to Database node ' + - dbPath + - ' succeeded!' - ); - } else { - throw new Error( - 'Authenticated read/write to Database node ' + - dbPath + - ' failed!' - ); - } - // Clean up: clear that node's content. - return dbRef.remove(); - }) - .catch(error => { - alertError('Error: ' + error.code); + log( + 'Unauthenticated read/write to Database node ' + + dbPath + + ' failed as expected!' + ); }); - } - } - }) - .catch(error => { - throw new Error(error); - }); + }); + } else if (auth.currentUser) { + dbPath = 'users/' + auth.currentUser.uid; + // Confirm read/write access to users/$user_id allowed. + dbRef = firebase.database().ref(dbPath); + dbRef + .set({ + 'test': randomString + }) + .then(() => { + return dbRef.once('value'); + }) + .then(snapshot => { + if (snapshot.val().test === randomString) { + // read/write successful. + log( + 'Authenticated read/write to Database node ' + + dbPath + + ' succeeded!' + ); + } else { + throw new Error( + 'Authenticated read/write to Database node ' + + dbPath + + ' failed!' + ); + } + // Clean up: clear that node's content. + return dbRef.remove(); + }) + .catch(error => { + alertError('Error: ' + error.code); + }); + } + } } /** @@ -1884,7 +1886,7 @@ function initApp() { // Allows to login the user if previously logged in. if (auth.onIdTokenChanged) { - auth.onIdTokenChanged(user => { + auth.onIdTokenChanged(async (user) => { refreshUserData(); if (user) { user.getIdTokenResult(false).then( @@ -1902,14 +1904,14 @@ function initApp() { } if (auth.onAuthStateChanged) { - auth.onAuthStateChanged(user => { + auth.onAuthStateChanged(async (user) => { if (user) { log('user state change detected: ' + user.uid); } else { log('user state change detected: no user'); } // Check Database Auth access. - checkDatabaseAuthAccess(); + await checkDatabaseAuthAccess(); }); } From cad6d23a18d9b9f815b13d510e30a5d3c77564d4 Mon Sep 17 00:00:00 2001 From: Angel Date: Wed, 28 Jun 2023 18:58:06 +0000 Subject: [PATCH 10/23] formatted code --- packages/auth/demo/src/index.js | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/packages/auth/demo/src/index.js b/packages/auth/demo/src/index.js index 5bc62046ed0..b84e9c2046a 100644 --- a/packages/auth/demo/src/index.js +++ b/packages/auth/demo/src/index.js @@ -135,10 +135,10 @@ async function getActiveUserAfterSignIn() { if (type === 'lastUser') { return lastUser; } else { - try{ + try { await auth.authStateReady(); return auth.currentUser; - }catch (e) { + } catch (e) { log(e); } } @@ -149,7 +149,7 @@ async function getActiveUserAfterSignIn() { * a user is signed in, or removing it. */ async function refreshUserData() { - try{ + try { let user = await getActiveUserAfterSignIn(); if (user) { $('.profile').show(); @@ -159,7 +159,9 @@ async function refreshUserData() { user.phoneNumber || 'No Phone' ); $('div.profile-uid,span.profile-uid').text(user.uid); - $('div.profile-name,span.profile-name').text(user.displayName || 'No Name'); + $('div.profile-name,span.profile-name').text( + user.displayName || 'No Name' + ); $('input.profile-name').val(user.displayName); $('input.photo-url').val(user.photoURL); if (user.photoURL != null) { @@ -296,7 +298,6 @@ function onAuthSuccess(user) { console.log(user); alertSuccess('User authenticated, id: ' + user.uid); refreshUserData(); - } /** @@ -322,7 +323,7 @@ function onAuthError(error) { function signOut() { log('User successfully signed out.'); alertSuccess('User successfully signed out.'); - refreshUserData() + refreshUserData(); } /** @@ -1756,9 +1757,7 @@ async function checkDatabaseAuthAccess() { ); } else { throw new Error( - 'Authenticated read/write to Database node ' + - dbPath + - ' failed!' + 'Authenticated read/write to Database node ' + dbPath + ' failed!' ); } // Clean up: clear that node's content. @@ -1886,7 +1885,7 @@ function initApp() { // Allows to login the user if previously logged in. if (auth.onIdTokenChanged) { - auth.onIdTokenChanged(async (user) => { + auth.onIdTokenChanged(async user => { refreshUserData(); if (user) { user.getIdTokenResult(false).then( @@ -1904,7 +1903,7 @@ function initApp() { } if (auth.onAuthStateChanged) { - auth.onAuthStateChanged(async (user) => { + auth.onAuthStateChanged(async user => { if (user) { log('user state change detected: ' + user.uid); } else { From 0ed6c0256192766fa031b5c624727c1343bc4a7e Mon Sep 17 00:00:00 2001 From: Angel Date: Thu, 29 Jun 2023 18:31:17 +0000 Subject: [PATCH 11/23] removed unnecessary async keywords --- packages/auth/demo/src/index.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/auth/demo/src/index.js b/packages/auth/demo/src/index.js index b84e9c2046a..627c22ffe16 100644 --- a/packages/auth/demo/src/index.js +++ b/packages/auth/demo/src/index.js @@ -432,7 +432,7 @@ function onSignInWithEmailLink() { /** * Links a user with an email link. */ -async function onLinkWithEmailLink() { +function onLinkWithEmailLink() { const email = $('#link-with-email-link-email').val(); const link = $('#link-with-email-link-link').val() || undefined; const credential = EmailAuthProvider.credentialWithLink(email, link); @@ -445,7 +445,7 @@ async function onLinkWithEmailLink() { /** * Re-authenticate a user with email link credential. */ -async function onReauthenticateWithEmailLink() { +function onReauthenticateWithEmailLink() { if (!activeUser()) { alertError( 'No user logged in. Select the "Last User" tab to reauth the previous user.' @@ -467,7 +467,7 @@ async function onReauthenticateWithEmailLink() { /** * Re-authenticate a user with email and password. */ -async function onReauthenticateWithEmailAndPassword() { +function onReauthenticateWithEmailAndPassword() { if (!activeUser()) { alertError( 'No user logged in. Select the "Last User" tab to reauth the previous user.' @@ -1681,7 +1681,7 @@ function populateActionCodes() { * } * This applies when Real-time database service is available. */ -async function checkDatabaseAuthAccess() { +function checkDatabaseAuthAccess() { const randomString = Math.floor(Math.random() * 10000000).toString(); let dbRef; let dbPath; @@ -1885,7 +1885,7 @@ function initApp() { // Allows to login the user if previously logged in. if (auth.onIdTokenChanged) { - auth.onIdTokenChanged(async user => { + auth.onIdTokenChanged(user => { refreshUserData(); if (user) { user.getIdTokenResult(false).then( @@ -1903,14 +1903,14 @@ function initApp() { } if (auth.onAuthStateChanged) { - auth.onAuthStateChanged(async user => { + auth.onAuthStateChanged(user => { if (user) { log('user state change detected: ' + user.uid); } else { log('user state change detected: no user'); } // Check Database Auth access. - await checkDatabaseAuthAccess(); + checkDatabaseAuthAccess(); }); } From 5477946062d3f96acfc7a3444353adedc311fb13 Mon Sep 17 00:00:00 2001 From: Angel Date: Thu, 29 Jun 2023 18:34:44 +0000 Subject: [PATCH 12/23] reverted changes in onSignOut --- packages/auth/demo/src/index.js | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/packages/auth/demo/src/index.js b/packages/auth/demo/src/index.js index 627c22ffe16..237f10bb086 100644 --- a/packages/auth/demo/src/index.js +++ b/packages/auth/demo/src/index.js @@ -1145,15 +1145,8 @@ function onRefreshToken() { * Signs out the user. */ function onSignOut() { - auth - .authStateReady() - .then(() => { - setLastUser(auth.currentUser); - auth.signOut().then(signOut, onAuthError); - }) - .catch(error => { - throw new Error(error); - }); + setLastUser(auth.currentUser); + auth.signOut().then(signOut, onAuthError); } /** From 041374d69dfbd0185580b9ef9b3be03d5b254671 Mon Sep 17 00:00:00 2001 From: Angel Date: Thu, 29 Jun 2023 18:37:18 +0000 Subject: [PATCH 13/23] clean up code --- packages/auth/demo/src/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/auth/demo/src/index.js b/packages/auth/demo/src/index.js index 237f10bb086..3efaf95727e 100644 --- a/packages/auth/demo/src/index.js +++ b/packages/auth/demo/src/index.js @@ -481,7 +481,7 @@ function onReauthenticateWithEmailAndPassword() { reauthenticateWithCredential(activeUser(), credential).then(result => { logAdditionalUserInfo(result); refreshUserData(); - alertSuccess('User reauthenticated with email link!'); + alertSuccess('User reauthenticated with email/password'); }, onAuthError); } From 5f0b3d60665c70b1e924aad6f9d17fa31016094f Mon Sep 17 00:00:00 2001 From: Angel Date: Thu, 29 Jun 2023 18:39:44 +0000 Subject: [PATCH 14/23] fixed comments --- packages/auth/demo/src/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/auth/demo/src/index.js b/packages/auth/demo/src/index.js index 3efaf95727e..1bbf1a31f95 100644 --- a/packages/auth/demo/src/index.js +++ b/packages/auth/demo/src/index.js @@ -114,7 +114,7 @@ const providersIcons = { }; /** - * Returns the promise that waits for user to sign in. + * Returns active user (i.e. currentUser or lastUser). * @return {!firebase.User} */ function activeUser() { From 330b374fd391c951210d60c5cbe0bf380bbc8ab9 Mon Sep 17 00:00:00 2001 From: Angel Date: Fri, 30 Jun 2023 00:14:58 +0000 Subject: [PATCH 15/23] resolved code review comments and updated tests --- packages/auth/demo/src/index.js | 5 ++--- packages/auth/src/core/auth/auth_impl.test.ts | 16 +--------------- packages/auth/src/model/public_types.ts | 3 ++- 3 files changed, 5 insertions(+), 19 deletions(-) diff --git a/packages/auth/demo/src/index.js b/packages/auth/demo/src/index.js index 1bbf1a31f95..f936e17abfc 100644 --- a/packages/auth/demo/src/index.js +++ b/packages/auth/demo/src/index.js @@ -130,7 +130,7 @@ function activeUser() { * Returns the active user after sign in (i.e. currentUser or lastUser). * @return {!firebase.User} */ -async function getActiveUserAfterSignIn() { +async function getActiveUserBlocking() { const type = $('input[name=toggle-user-selection]:checked').val(); if (type === 'lastUser') { return lastUser; @@ -150,7 +150,7 @@ async function getActiveUserAfterSignIn() { */ async function refreshUserData() { try { - let user = await getActiveUserAfterSignIn(); + let user = await getActiveUserBlocking(); if (user) { $('.profile').show(); $('body').addClass('user-info-displayed'); @@ -456,7 +456,6 @@ function onReauthenticateWithEmailLink() { const link = $('#link-with-email-link-link').val() || undefined; const credential = EmailAuthProvider.credentialWithLink(email, link); // This will not set auth.currentUser to lastUser if the lastUser is reauthenticated. - reauthenticateWithCredential(activeUser(), credential).then(result => { logAdditionalUserInfo(result); refreshUserData(); diff --git a/packages/auth/src/core/auth/auth_impl.test.ts b/packages/auth/src/core/auth/auth_impl.test.ts index c6480dde1fe..05c1ab7e414 100644 --- a/packages/auth/src/core/auth/auth_impl.test.ts +++ b/packages/auth/src/core/auth/auth_impl.test.ts @@ -789,24 +789,18 @@ describe('core/auth/auth_impl', () => { describe('AuthStateReady', () => { let user: UserInternal; - let onAuthStateChangedCallback: sinon.SinonSpy; let authStateChangedSpy: sinon.SinonSpy; beforeEach(async () => { user = testUser(auth, 'uid'); - onAuthStateChangedCallback = sinon.spy(); - auth.onAuthStateChanged(onAuthStateChangedCallback); authStateChangedSpy = sinon.spy(auth, 'onAuthStateChanged'); await auth._updateCurrentUser(null); }); - //case one: if(this.currentUser) is true --> resolves immediately it('immediately returns resolved promise if the user is previously logged in', async () => { - expect(onAuthStateChangedCallback).to.be.calledOnce; await auth._updateCurrentUser(user); - expect(onAuthStateChangedCallback).to.be.calledTwice; await auth .authStateReady() @@ -817,11 +811,8 @@ describe('core/auth/auth_impl', () => { .catch(error => { throw new Error(error); }); - - expect(onAuthStateChangedCallback).to.be.calledTwice; }); - //case two: if(this.currentUser) is false --> calls onAuthStateChanged it('returns resolved promise once the user is initialized to object of type UserInternal', async () => { expect(authStateChangedSpy).to.not.have.been.called; const promiseVar = auth.authStateReady(); @@ -831,7 +822,6 @@ describe('core/auth/auth_impl', () => { await promiseVar .then(() => { - // onAuthStateChangedCallback(); expect(auth.currentUser).to.eq(user); }) .catch(error => { @@ -839,11 +829,9 @@ describe('core/auth/auth_impl', () => { }); expect(authStateChangedSpy).to.be.calledOnce; - // expect(onAuthStateChangedCallback).to.be.calledThrice; }); - //case three: if user logged out more than once, promise should still be resolved with currentUser remained as null - it('resolves promise with currentUser remains as null when user logs out more than once', async () => { + it('resolves the promise during repeated logout', async () => { expect(authStateChangedSpy).to.not.have.been.called; const promiseVar = auth.authStateReady(); expect(authStateChangedSpy).to.be.calledOnce; @@ -861,7 +849,6 @@ describe('core/auth/auth_impl', () => { expect(authStateChangedSpy).to.be.calledOnce; }); - //case four: user sign in failed, expect promise to resolve and allow currentUser to be null. it('resolves the promise with currentUser remain null during log in failure', async () => { expect(authStateChangedSpy).to.not.have.been.called; const promiseVar = auth.authStateReady(); @@ -886,7 +873,6 @@ describe('core/auth/auth_impl', () => { expect(authStateChangedSpy).to.be.calledOnce; }); - //case five: user sign in delay, promise should be resolved after delay. it('resolves the promise in a delayed user log in process', async () => { setTimeout(async () => { await auth._updateCurrentUser(user); diff --git a/packages/auth/src/model/public_types.ts b/packages/auth/src/model/public_types.ts index 497d2112d21..d927c4798a7 100644 --- a/packages/auth/src/model/public_types.ts +++ b/packages/auth/src/model/public_types.ts @@ -293,7 +293,8 @@ export interface Auth { ): Unsubscribe; /** * return a promise that resolves immediately when the initial - * auth state is settled and currentUser is available + * auth state is settled. The current user might be a valid user, + * or null if there is no user signed in currently. */ authStateReady(): Promise; /** The currently signed-in user (or null). */ From 880e0f7db6d42ca84232ed943a18e904eaedbe04 Mon Sep 17 00:00:00 2001 From: Angel Date: Wed, 5 Jul 2023 16:15:10 +0000 Subject: [PATCH 16/23] changed reference doc --- docs-devsite/auth.auth.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/docs-devsite/auth.auth.md b/docs-devsite/auth.auth.md index 4f49465a094..59c1d3207f5 100644 --- a/docs-devsite/auth.auth.md +++ b/docs-devsite/auth.auth.md @@ -37,6 +37,7 @@ export interface Auth | Method | Description | | --- | --- | +| [authStateReady()](./auth.auth.md#authauthstateready) | return a promise that resolves immediately when the initial auth state is settled. The current user might be a valid user, or null if there is no user signed in currently. | | [beforeAuthStateChanged(callback, onAbort)](./auth.auth.md#authbeforeauthstatechanged) | Adds a blocking callback that runs before an auth state change sets a new user. | | [onAuthStateChanged(nextOrObserver, error, completed)](./auth.auth.md#authonauthstatechanged) | Adds an observer for changes to the user's sign-in state. | | [onIdTokenChanged(nextOrObserver, error, completed)](./auth.auth.md#authonidtokenchanged) | Adds an observer for changes to the signed-in user's ID token. | @@ -144,6 +145,19 @@ const result = await signInWithEmailAndPassword(auth, email, password); ``` +## Auth.authStateReady() + +return a promise that resolves immediately when the initial auth state is settled. The current user might be a valid user, or null if there is no user signed in currently. + +Signature: + +```typescript +authStateReady(): Promise; +``` +Returns: + +Promise<void> + ## Auth.beforeAuthStateChanged() Adds a blocking callback that runs before an auth state change sets a new user. From a34c7b3693946b6b08c7495a0ed5e7890d655d87 Mon Sep 17 00:00:00 2001 From: Angel Date: Wed, 5 Jul 2023 18:22:49 +0000 Subject: [PATCH 17/23] resolved comments from pr --- packages/auth/demo/src/index.js | 3 ++- packages/auth/src/core/auth/auth_impl.test.ts | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/auth/demo/src/index.js b/packages/auth/demo/src/index.js index f936e17abfc..028d3ef3150 100644 --- a/packages/auth/demo/src/index.js +++ b/packages/auth/demo/src/index.js @@ -127,7 +127,8 @@ function activeUser() { } /** - * Returns the active user after sign in (i.e. currentUser or lastUser). + * Blocks until there is a valid user + * then return the valid user (i.e. currentUser or lastUser). * @return {!firebase.User} */ async function getActiveUserBlocking() { diff --git a/packages/auth/src/core/auth/auth_impl.test.ts b/packages/auth/src/core/auth/auth_impl.test.ts index 05c1ab7e414..8b0eb3e1b4b 100644 --- a/packages/auth/src/core/auth/auth_impl.test.ts +++ b/packages/auth/src/core/auth/auth_impl.test.ts @@ -802,6 +802,7 @@ describe('core/auth/auth_impl', () => { it('immediately returns resolved promise if the user is previously logged in', async () => { await auth._updateCurrentUser(user); + // authStateReady calls onAuthStateChanged, if there is no currentUser available await auth .authStateReady() .then(() => { @@ -849,7 +850,7 @@ describe('core/auth/auth_impl', () => { expect(authStateChangedSpy).to.be.calledOnce; }); - it('resolves the promise with currentUser remain null during log in failure', async () => { + it('resolves the promise with currentUser being null during log in failure', async () => { expect(authStateChangedSpy).to.not.have.been.called; const promiseVar = auth.authStateReady(); expect(authStateChangedSpy).to.be.calledOnce; From 464768100eb2c39237da833d3fe5c6dba039b0a3 Mon Sep 17 00:00:00 2001 From: Angel Date: Wed, 5 Jul 2023 19:10:51 +0000 Subject: [PATCH 18/23] added changeset --- .changeset/twelve-actors-enjoy.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/twelve-actors-enjoy.md diff --git a/.changeset/twelve-actors-enjoy.md b/.changeset/twelve-actors-enjoy.md new file mode 100644 index 00000000000..d15229c4c11 --- /dev/null +++ b/.changeset/twelve-actors-enjoy.md @@ -0,0 +1,6 @@ +--- +'@firebase/auth': minor +'firebase': minor +--- + +"firebase":minor; Implemented authStateReady(), which returns a promise that resolves immediately when the initial auth state is settled and currentUser is available. The current user might be a valid user or null if there is no user signed in currently. From a6be192062523d3b476ccf724b5c0d08654ec094 Mon Sep 17 00:00:00 2001 From: AngelAngelXie <78315342+AngelAngelXie@users.noreply.github.com> Date: Wed, 5 Jul 2023 12:12:54 -0700 Subject: [PATCH 19/23] Update twelve-actors-enjoy.md --- .changeset/twelve-actors-enjoy.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/twelve-actors-enjoy.md b/.changeset/twelve-actors-enjoy.md index d15229c4c11..3c1f5498aa0 100644 --- a/.changeset/twelve-actors-enjoy.md +++ b/.changeset/twelve-actors-enjoy.md @@ -3,4 +3,4 @@ 'firebase': minor --- -"firebase":minor; Implemented authStateReady(), which returns a promise that resolves immediately when the initial auth state is settled and currentUser is available. The current user might be a valid user or null if there is no user signed in currently. +Implemented authStateReady(), which returns a promise that resolves immediately when the initial auth state is settled and currentUser is available. The current user might be a valid user or null if there is no user signed in currently. From be970ef1739e702d76d60e469424b1de3ec5ae3a Mon Sep 17 00:00:00 2001 From: Angel Date: Wed, 5 Jul 2023 20:08:12 +0000 Subject: [PATCH 20/23] resolved doc change check failure --- packages/auth/api-extractor.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/auth/api-extractor.json b/packages/auth/api-extractor.json index ca7e1ee6983..325fe12d507 100644 --- a/packages/auth/api-extractor.json +++ b/packages/auth/api-extractor.json @@ -1,6 +1,6 @@ { "extends": "../../config/api-extractor.json", - "mainEntryPointFilePath": "/dist/esm5/index.d.ts", + "mainEntryPointFilePath": "/dist/esm5/index.doc.d.ts", "dtsRollup": { "enabled": true, "untrimmedFilePath": "/dist/.d.ts", From bde08994250d0405309e3a401eb4fefba7d36465 Mon Sep 17 00:00:00 2001 From: Angel Date: Wed, 5 Jul 2023 20:38:41 +0000 Subject: [PATCH 21/23] resolved issues with Doc Change Check --- packages/auth/api-extractor.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/auth/api-extractor.json b/packages/auth/api-extractor.json index 325fe12d507..ca7e1ee6983 100644 --- a/packages/auth/api-extractor.json +++ b/packages/auth/api-extractor.json @@ -1,6 +1,6 @@ { "extends": "../../config/api-extractor.json", - "mainEntryPointFilePath": "/dist/esm5/index.doc.d.ts", + "mainEntryPointFilePath": "/dist/esm5/index.d.ts", "dtsRollup": { "enabled": true, "untrimmedFilePath": "/dist/.d.ts", From 38bd563b8079c06bad7310b1ee1512e6bf72caeb Mon Sep 17 00:00:00 2001 From: Angel Date: Thu, 6 Jul 2023 18:52:01 +0000 Subject: [PATCH 22/23] clarify comments --- .changeset/twelve-actors-enjoy.md | 2 +- docs-devsite/auth.auth.md | 4 ++-- packages/auth/demo/src/index.js | 4 ++-- packages/auth/src/core/auth/auth_impl.test.ts | 3 +-- packages/auth/src/model/public_types.ts | 6 +++--- 5 files changed, 9 insertions(+), 10 deletions(-) diff --git a/.changeset/twelve-actors-enjoy.md b/.changeset/twelve-actors-enjoy.md index 3c1f5498aa0..e599c5bc68a 100644 --- a/.changeset/twelve-actors-enjoy.md +++ b/.changeset/twelve-actors-enjoy.md @@ -3,4 +3,4 @@ 'firebase': minor --- -Implemented authStateReady(), which returns a promise that resolves immediately when the initial auth state is settled and currentUser is available. The current user might be a valid user or null if there is no user signed in currently. +Implemented `authStateReady()`, which returns a promise that resolves immediately when the initial auth state is settled and currentUser is available. When the promise is resolved, the current user might be a valid user or null if there is no user signed in currently. diff --git a/docs-devsite/auth.auth.md b/docs-devsite/auth.auth.md index 59c1d3207f5..8462887d762 100644 --- a/docs-devsite/auth.auth.md +++ b/docs-devsite/auth.auth.md @@ -37,7 +37,7 @@ export interface Auth | Method | Description | | --- | --- | -| [authStateReady()](./auth.auth.md#authauthstateready) | return a promise that resolves immediately when the initial auth state is settled. The current user might be a valid user, or null if there is no user signed in currently. | +| [authStateReady()](./auth.auth.md#authauthstateready) | returns a promise that resolves immediately when the initial auth state is settled. When the promise is resolved, the current user might be a valid user or `null` if users signed out. | | [beforeAuthStateChanged(callback, onAbort)](./auth.auth.md#authbeforeauthstatechanged) | Adds a blocking callback that runs before an auth state change sets a new user. | | [onAuthStateChanged(nextOrObserver, error, completed)](./auth.auth.md#authonauthstatechanged) | Adds an observer for changes to the user's sign-in state. | | [onIdTokenChanged(nextOrObserver, error, completed)](./auth.auth.md#authonidtokenchanged) | Adds an observer for changes to the signed-in user's ID token. | @@ -147,7 +147,7 @@ const result = await signInWithEmailAndPassword(auth, email, password); ## Auth.authStateReady() -return a promise that resolves immediately when the initial auth state is settled. The current user might be a valid user, or null if there is no user signed in currently. +returns a promise that resolves immediately when the initial auth state is settled. When the promise is resolved, the current user might be a valid user or `null` if users signed out. Signature: diff --git a/packages/auth/demo/src/index.js b/packages/auth/demo/src/index.js index 028d3ef3150..8a1037c2f72 100644 --- a/packages/auth/demo/src/index.js +++ b/packages/auth/demo/src/index.js @@ -114,7 +114,7 @@ const providersIcons = { }; /** - * Returns active user (i.e. currentUser or lastUser). + * Returns active user (currentUser or lastUser). * @return {!firebase.User} */ function activeUser() { @@ -128,7 +128,7 @@ function activeUser() { /** * Blocks until there is a valid user - * then return the valid user (i.e. currentUser or lastUser). + * then returns the valid user (currentUser or lastUser). * @return {!firebase.User} */ async function getActiveUserBlocking() { diff --git a/packages/auth/src/core/auth/auth_impl.test.ts b/packages/auth/src/core/auth/auth_impl.test.ts index 8b0eb3e1b4b..55fb6a65a5b 100644 --- a/packages/auth/src/core/auth/auth_impl.test.ts +++ b/packages/auth/src/core/auth/auth_impl.test.ts @@ -802,7 +802,6 @@ describe('core/auth/auth_impl', () => { it('immediately returns resolved promise if the user is previously logged in', async () => { await auth._updateCurrentUser(user); - // authStateReady calls onAuthStateChanged, if there is no currentUser available await auth .authStateReady() .then(() => { @@ -814,7 +813,7 @@ describe('core/auth/auth_impl', () => { }); }); - it('returns resolved promise once the user is initialized to object of type UserInternal', async () => { + it('calls onAuthStateChanged if there is no currentUser available, and returns resolved promise once the user is updated', async () => { expect(authStateChangedSpy).to.not.have.been.called; const promiseVar = auth.authStateReady(); expect(authStateChangedSpy).to.be.calledOnce; diff --git a/packages/auth/src/model/public_types.ts b/packages/auth/src/model/public_types.ts index d927c4798a7..87bd04c2361 100644 --- a/packages/auth/src/model/public_types.ts +++ b/packages/auth/src/model/public_types.ts @@ -292,9 +292,9 @@ export interface Auth { completed?: CompleteFn ): Unsubscribe; /** - * return a promise that resolves immediately when the initial - * auth state is settled. The current user might be a valid user, - * or null if there is no user signed in currently. + * returns a promise that resolves immediately when the initial + * auth state is settled. When the promise resolves, the current user might be a valid user + * or `null` if the user signed out. */ authStateReady(): Promise; /** The currently signed-in user (or null). */ From d87d7adf2964bf54827726e284658010146f985d Mon Sep 17 00:00:00 2001 From: Angel Date: Thu, 6 Jul 2023 19:16:35 +0000 Subject: [PATCH 23/23] fixed doc change check issue --- docs-devsite/auth.auth.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs-devsite/auth.auth.md b/docs-devsite/auth.auth.md index 8462887d762..bea0aae514f 100644 --- a/docs-devsite/auth.auth.md +++ b/docs-devsite/auth.auth.md @@ -37,7 +37,7 @@ export interface Auth | Method | Description | | --- | --- | -| [authStateReady()](./auth.auth.md#authauthstateready) | returns a promise that resolves immediately when the initial auth state is settled. When the promise is resolved, the current user might be a valid user or `null` if users signed out. | +| [authStateReady()](./auth.auth.md#authauthstateready) | returns a promise that resolves immediately when the initial auth state is settled. When the promise resolves, the current user might be a valid user or null if the user signed out. | | [beforeAuthStateChanged(callback, onAbort)](./auth.auth.md#authbeforeauthstatechanged) | Adds a blocking callback that runs before an auth state change sets a new user. | | [onAuthStateChanged(nextOrObserver, error, completed)](./auth.auth.md#authonauthstatechanged) | Adds an observer for changes to the user's sign-in state. | | [onIdTokenChanged(nextOrObserver, error, completed)](./auth.auth.md#authonidtokenchanged) | Adds an observer for changes to the signed-in user's ID token. | @@ -147,7 +147,7 @@ const result = await signInWithEmailAndPassword(auth, email, password); ## Auth.authStateReady() -returns a promise that resolves immediately when the initial auth state is settled. When the promise is resolved, the current user might be a valid user or `null` if users signed out. +returns a promise that resolves immediately when the initial auth state is settled. When the promise resolves, the current user might be a valid user or `null` if the user signed out. Signature: