Skip to content

Commit 65bf4f0

Browse files
feat(intercom): Closing intercom session on org change (#112549)
When user changes or org changes we need to call intercom authentication API again. Each user<>org pair represents an intercom user and we don't want the same user in org A has access to their chats on org B.
1 parent 16cbe0f commit 65bf4f0

File tree

3 files changed

+31
-0
lines changed

3 files changed

+31
-0
lines changed

static/app/utils/intercom.tsx

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ interface IntercomJwtResponse {
2424

2525
let hasBooted = false;
2626
let bootPromise: Promise<void> | null = null;
27+
let bootedOrgSlug: string | null = null;
2728

2829
/**
2930
* Initialize Intercom with identity verification.
@@ -69,6 +70,7 @@ async function initIntercom(orgSlug: string): Promise<void> {
6970
});
7071

7172
hasBooted = true;
73+
bootedOrgSlug = orgSlug;
7274
} catch (error) {
7375
// Reset so user can retry on next click
7476
bootPromise = null;
@@ -79,11 +81,34 @@ async function initIntercom(orgSlug: string): Promise<void> {
7981
return bootPromise;
8082
}
8183

84+
/**
85+
* Shutdown Intercom and clear session data.
86+
* Call this when user logs out or switches organizations.
87+
*/
88+
export async function shutdownIntercom(): Promise<void> {
89+
if (!hasBooted) {
90+
return;
91+
}
92+
93+
const {shutdown} = await import('@intercom/messenger-js-sdk');
94+
shutdown();
95+
96+
hasBooted = false;
97+
bootPromise = null;
98+
bootedOrgSlug = null;
99+
}
100+
82101
/**
83102
* Show the Intercom Messenger.
84103
* Lazily initializes Intercom on first call.
104+
* If already booted for a different org, shuts down first and re-initializes.
85105
*/
86106
export async function showIntercom(orgSlug: string): Promise<void> {
107+
// If booted for a different org, shutdown first to re-initialize with new org context
108+
if (hasBooted && bootedOrgSlug !== orgSlug) {
109+
await shutdownIntercom();
110+
}
111+
87112
await initIntercom(orgSlug);
88113
const {show} = await import('@intercom/messenger-js-sdk');
89114
show();

static/app/views/organizationContext.spec.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {OrganizationStore} from 'sentry/stores/organizationStore';
1212
import {ProjectsStore} from 'sentry/stores/projectsStore';
1313
import {TeamStore} from 'sentry/stores/teamStore';
1414
import type {Organization} from 'sentry/types/organization';
15+
import * as intercom from 'sentry/utils/intercom';
1516
import {useOrganization} from 'sentry/utils/useOrganization';
1617

1718
import {OrganizationContextProvider} from './organizationContext';
@@ -114,6 +115,7 @@ describe('OrganizationContext', () => {
114115
const {orgMock, projectMock, teamMock} = setupOrgMocks(anotherOrg);
115116

116117
const switchOrganization = jest.spyOn(orgsActionCreators, 'switchOrganization');
118+
const shutdownIntercom = jest.spyOn(intercom, 'shutdownIntercom');
117119

118120
// re-render with another-org
119121
testRouter.navigate(`/organizations/${anotherOrg.slug}/`);
@@ -123,6 +125,7 @@ describe('OrganizationContext', () => {
123125
expect(projectMock).toHaveBeenCalled();
124126
expect(teamMock).toHaveBeenCalled();
125127
expect(switchOrganization).toHaveBeenCalled();
128+
expect(shutdownIntercom).toHaveBeenCalled();
126129
expect(JSON.stringify(OrganizationStore.getState().organization)).toEqual(
127130
JSON.stringify(anotherOrg)
128131
);

static/app/views/organizationContext.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {TeamStore} from 'sentry/stores/teamStore';
1818
import {useLegacyStore} from 'sentry/stores/useLegacyStore';
1919
import type {Organization} from 'sentry/types/organization';
2020
import type {User} from 'sentry/types/user';
21+
import {shutdownIntercom} from 'sentry/utils/intercom';
2122
import {useParams} from 'sentry/utils/useParams';
2223

2324
interface Props {
@@ -147,6 +148,8 @@ export function OrganizationContextProvider({children}: Props) {
147148
// Also avoid: org1 -> undefined -> org1
148149
if (lastOrgId.current) {
149150
switchOrganization();
151+
// Shutdown Intercom so it re-initializes with new org context on next use
152+
shutdownIntercom();
150153
}
151154

152155
lastOrgId.current = orgSlug;

0 commit comments

Comments
 (0)