diff --git a/.changeset/happy-badgers-leave.md b/.changeset/happy-badgers-leave.md new file mode 100644 index 00000000000..8e182c5a138 --- /dev/null +++ b/.changeset/happy-badgers-leave.md @@ -0,0 +1,7 @@ +--- +"@firebase/firestore": patch +--- + +Fixed an AppCheck issue that caused Firestore listeners to stop working and +receive a "Permission Denied" error. This issue only occurred for AppCheck users +that set their expiration time to under an hour. diff --git a/packages/firestore/src/core/firestore_client.ts b/packages/firestore/src/core/firestore_client.ts index dabdc35edf3..858b40929ad 100644 --- a/packages/firestore/src/core/firestore_client.ts +++ b/packages/firestore/src/core/firestore_client.ts @@ -99,6 +99,10 @@ export class FirestoreClient { private readonly clientId = AutoId.newId(); private authCredentialListener: CredentialChangeListener = () => Promise.resolve(); + private appCheckCredentialListener: ( + appCheckToken: string, + user: User + ) => Promise = () => Promise.resolve(); offlineComponents?: OfflineComponentProvider; onlineComponents?: OnlineComponentProvider; @@ -122,8 +126,10 @@ export class FirestoreClient { await this.authCredentialListener(user); this.user = user; }); - // Register an empty credentials change listener to activate token refresh. - this.appCheckCredentials.start(asyncQueue, () => Promise.resolve()); + this.appCheckCredentials.start(asyncQueue, newAppCheckToken => { + logDebug(LOG_TAG, 'Received new app check token=', newAppCheckToken); + return this.appCheckCredentialListener(newAppCheckToken, this.user); + }); } async getConfiguration(): Promise { @@ -142,6 +148,12 @@ export class FirestoreClient { this.authCredentialListener = listener; } + setAppCheckTokenChangeListener( + listener: (appCheckToken: string, user: User) => Promise + ): void { + this.appCheckCredentialListener = listener; + } + /** * Checks that the client has not been terminated. Ensures that other methods on * this class cannot be called after the client is terminated. @@ -234,6 +246,9 @@ export async function setOnlineComponentProvider( client.setCredentialChangeListener(user => remoteStoreHandleCredentialChange(onlineComponentProvider.remoteStore, user) ); + client.setAppCheckTokenChangeListener((_, user) => + remoteStoreHandleCredentialChange(onlineComponentProvider.remoteStore, user) + ); client.onlineComponents = onlineComponentProvider; }