diff --git a/.changeset/fix-auth-button-cross-contamination.md b/.changeset/fix-auth-button-cross-contamination.md new file mode 100644 index 000000000..64fd6e95f --- /dev/null +++ b/.changeset/fix-auth-button-cross-contamination.md @@ -0,0 +1,5 @@ +--- +"@knocklabs/react-core": patch +--- + +Fix auth button cross-contamination when SlackKit and TeamsKit are rendered simultaneously. The shared `useAuthPostMessageListener` hook now checks whether its own popup is open before processing `authComplete` messages, preventing one integration's OAuth completion from incorrectly updating the other's connection state. diff --git a/packages/react-core/src/modules/core/hooks/useAuthPostMessageListener.ts b/packages/react-core/src/modules/core/hooks/useAuthPostMessageListener.ts index cc3cbfaa5..45c62ff65 100644 --- a/packages/react-core/src/modules/core/hooks/useAuthPostMessageListener.ts +++ b/packages/react-core/src/modules/core/hooks/useAuthPostMessageListener.ts @@ -84,6 +84,11 @@ export function useAuthPostMessageListener( return; } + // Ignore messages when this integration hasn't opened a popup + if (!popupWindowRef.current) { + return; + } + const messageType = getMessageType(event.data); if (messageType === "authComplete") { diff --git a/packages/react-core/test/core/hooks/useAuthPostMessageListener.test.ts b/packages/react-core/test/core/hooks/useAuthPostMessageListener.test.ts index dfaed2a79..994d383e8 100644 --- a/packages/react-core/test/core/hooks/useAuthPostMessageListener.test.ts +++ b/packages/react-core/test/core/hooks/useAuthPostMessageListener.test.ts @@ -132,6 +132,28 @@ describe("useAuthPostMessageListener", () => { expect(popupWindowRef.current).toBeNull(); }); + it("should ignore messages when popupWindowRef is null", () => { + popupWindowRef.current = null; + + renderHook(() => + useAuthPostMessageListener({ + knockHost, + popupWindowRef, + setConnectionStatus, + onAuthenticationComplete, + }), + ); + + const event = new MessageEvent("message", { + data: "authComplete", + origin: knockHost, + }); + window.dispatchEvent(event); + + expect(setConnectionStatus).not.toHaveBeenCalled(); + expect(onAuthenticationComplete).not.toHaveBeenCalled(); + }); + it("should ignore messages from different origins", () => { renderHook(() => useAuthPostMessageListener({