diff --git a/src/puter-js/src/modules/notifications/index.js b/src/puter-js/src/modules/notifications/index.js index bf55be234..abf94173c 100644 --- a/src/puter-js/src/modules/notifications/index.js +++ b/src/puter-js/src/modules/notifications/index.js @@ -512,6 +512,9 @@ export class NotificationsModule extends EventListener { let last = null; const note = () => { const token = this.puter.authToken; + // Signed in again with it, so a later change cleans up again. + if (token && token === this.#forgottenToken) + this.#forgottenToken = null; if (!token || token === last?.token) return; const previous = last; last = { token, user: this.#tokenUser(token) }; diff --git a/src/puter-js/src/modules/notifications/webPush.test.js b/src/puter-js/src/modules/notifications/webPush.test.js index 42132dcb8..0f627b37a 100644 --- a/src/puter-js/src/modules/notifications/webPush.test.js +++ b/src/puter-js/src/modules/notifications/webPush.test.js @@ -2415,6 +2415,32 @@ describe('a different user signing in', () => { ); }); + it('ends push for a user who signed out and back in with the same token, once another user signs in', async () => { + const { module, host, subscription } = signedIn({ whoami: undefined }); + server((route) => { + if (route === '/whoami') return { uuid: USERS[host.authToken] }; + if (route === '/push/web/subscriptions') + return { status: 'active', device: { uid: 'd1' } }; + return { removed: true }; + }); + await module._forgetPush('token-a'); + host.setAuthToken(null); + host.setAuthToken('token-a'); + await expect(module.enable()).resolves.toBe(true); + expect(store.get(OPT_IN)).toBe(true); + mockRequest.mockClear(); + subscription.unsubscribe.mockClear(); + + host.setAuthToken('token-b'); + await vi.waitFor(() => expect(deletes()).toHaveLength(1)); + expect(deletes()[0][4]).toMatchObject({ authToken: 'token-a' }); + expect(subscription.unsubscribe).toHaveBeenCalled(); + expect(store.has(OPT_IN)).toBe(false); + + await expect(module.enable()).resolves.toBe(true); + expect(store.get('handlers:puter:opt-in:u-2')).toBe(true); + }); + it('tells users apart by the cached /whoami when the token names none', async () => { const listeners = new Set(); const { host } = signedIn({