From 20c9f756dd738e0381bf985dcf64e23b812698f5 Mon Sep 17 00:00:00 2001 From: Nick Hibberd Date: Fri, 1 Aug 2025 17:27:35 -0700 Subject: [PATCH 1/3] feat: add support for auth_tokens in boot and update methods MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Users can now pass authentication tokens to Intercom for secure data operations. The authTokens property accepts an object with any string key-value pairs. Example usage: boot({ email: 'john.doe@example.com', userId: '9876', authTokens: { security_token: 'abc...' // JWT } }) 🤖 Generated with Claude Code Co-Authored-By: Claude --- .changeset/add-auth-tokens-support.md | 18 +++++ examples/auth-tokens-example.tsx | 37 +++++++++ packages/react-use-intercom/README.md | 17 +++++ packages/react-use-intercom/src/mappers.ts | 1 + packages/react-use-intercom/src/types.ts | 19 ++++- .../test/authTokens.test.tsx | 75 +++++++++++++++++++ 6 files changed, 166 insertions(+), 1 deletion(-) create mode 100644 .changeset/add-auth-tokens-support.md create mode 100644 examples/auth-tokens-example.tsx create mode 100644 packages/react-use-intercom/test/authTokens.test.tsx diff --git a/.changeset/add-auth-tokens-support.md b/.changeset/add-auth-tokens-support.md new file mode 100644 index 00000000..0219e1b0 --- /dev/null +++ b/.changeset/add-auth-tokens-support.md @@ -0,0 +1,18 @@ +--- +"react-use-intercom": minor +--- + +Add support for auth_tokens in boot and update methods + +Users can now pass authentication tokens to Intercom for secure data operations. The `authTokens` property accepts an object with any string key-value pairs. + +Example usage: +```js +boot({ + email: 'john.doe@example.com', + userId: '9876', + authTokens: { + security_token: 'abc...' // JWT + } +}) +``` \ No newline at end of file diff --git a/examples/auth-tokens-example.tsx b/examples/auth-tokens-example.tsx new file mode 100644 index 00000000..0f996f94 --- /dev/null +++ b/examples/auth-tokens-example.tsx @@ -0,0 +1,37 @@ +import React from 'react'; +import { IntercomProvider, useIntercom } from 'react-use-intercom'; + +function MyApp() { + const { boot } = useIntercom(); + + const handleLogin = () => { + // After successful login, boot Intercom with auth tokens + boot({ + email: 'john.doe@example.com', + createdAt: 1234567890, + name: 'John Doe', + userId: '9876', + authTokens: { + security_token: 'abc...', // Your JWT token + // You can add any other tokens as key-value pairs + api_token: 'xyz...', + custom_token: '123...' + } + }); + }; + + return ( +
+

Intercom Auth Tokens Example

+ +
+ ); +} + +export default function App() { + return ( + + + + ); +} \ No newline at end of file diff --git a/packages/react-use-intercom/README.md b/packages/react-use-intercom/README.md index 1b329535..c8d82c32 100644 --- a/packages/react-use-intercom/README.md +++ b/packages/react-use-intercom/README.md @@ -255,6 +255,23 @@ All the Intercom default attributes/props are camel cased (`appId` instead of `a }) ``` + #### Authentication tokens + For secure data operations, you can pass authentication tokens to Intercom using the `authTokens` property. This accepts an object with any string key-value pairs. + + ```ts + const { boot } = useIntercom(); + + boot({ + email: 'john.doe@example.com', + userId: '9876', + authTokens: { + security_token: 'abc...', // JWT token + api_token: 'xyz...', + // Any other tokens as key-value pairs + } +}) + ``` + ## Playground Small playground to showcase the functionalities of `react-use-intercom`. diff --git a/packages/react-use-intercom/src/mappers.ts b/packages/react-use-intercom/src/mappers.ts index a7047027..1aafbd13 100644 --- a/packages/react-use-intercom/src/mappers.ts +++ b/packages/react-use-intercom/src/mappers.ts @@ -75,6 +75,7 @@ export const mapDataAttributesToRawDataAttributes = ( mapDataAttributesCompanyToRawDataAttributesCompany, ), intercom_user_jwt: attributes.intercomUserJwt, + auth_tokens: attributes.authTokens, ...attributes.customAttributes, }); diff --git a/packages/react-use-intercom/src/types.ts b/packages/react-use-intercom/src/types.ts index 927d2829..8e746548 100644 --- a/packages/react-use-intercom/src/types.ts +++ b/packages/react-use-intercom/src/types.ts @@ -117,6 +117,8 @@ export type DataAttributesAvatar = { imageUrl?: string; }; +export type AuthTokens = Record; + export type RawDataAttributes = { email?: string; user_id?: string; @@ -137,6 +139,7 @@ export type RawDataAttributes = { companies?: RawDataAttributesCompany[]; intercom_user_jwt?: string; customAttributes?: Record; + auth_tokens?: AuthTokens; }; export type DataAttributes = { @@ -236,9 +239,23 @@ export type DataAttributes = { * ``` * * @see {@link https://www.intercom.com/help/en/articles/179-send-custom-user-attributes-to-intercom} - * @remarks The key is the attribute name. The value is a placeholder for the data you’ll track + * @remarks The key is the attribute name. The value is a placeholder for the data you'll track */ customAttributes?: Record; + /** + * Authentication tokens for secure data operations + * Can contain any key-value pairs where both key and value are strings + * + * @example + * ``` + * authTokens: { + * security_token: 'abc...' // JWT + * } + * ``` + * + * @see {@link https://www.intercom.com/help/en/articles/6615543-setting-up-data-connectors-authentication#h_5343ec2d2c} + */ + authTokens?: AuthTokens; }; export type IntercomMethod = diff --git a/packages/react-use-intercom/test/authTokens.test.tsx b/packages/react-use-intercom/test/authTokens.test.tsx new file mode 100644 index 00000000..657fb007 --- /dev/null +++ b/packages/react-use-intercom/test/authTokens.test.tsx @@ -0,0 +1,75 @@ +import * as React from 'react'; +import { act, renderHook } from '@testing-library/react'; + +import { IntercomProvider, useIntercom } from '../src'; + +describe('auth_tokens', () => { + it('should pass auth_tokens during boot', () => { + const appId = 'app123'; + const mockIntercom = jest.fn(); + (window as any).Intercom = mockIntercom; + (window as any).intercomSettings = undefined; + + const wrapper = ({ children }: { children: React.ReactNode }) => ( + {children} + ); + + const { result } = renderHook(() => useIntercom(), { wrapper }); + + act(() => { + result.current.boot({ + email: 'john.doe@example.com', + createdAt: 1234567890, + name: 'John Doe', + userId: '9876', + authTokens: { + security_token: 'abc123', + another_token: 'xyz789', + }, + }); + }); + + expect(mockIntercom).toHaveBeenCalledWith('boot', { + app_id: appId, + email: 'john.doe@example.com', + created_at: 1234567890, + name: 'John Doe', + user_id: '9876', + auth_tokens: { + security_token: 'abc123', + another_token: 'xyz789', + }, + }); + }); + + it('should pass auth_tokens during update', () => { + const appId = 'app123'; + const mockIntercom = jest.fn(); + (window as any).Intercom = mockIntercom; + (window as any).intercomSettings = { app_id: appId }; + + const wrapper = ({ children }: { children: React.ReactNode }) => ( + {children} + ); + + const { result } = renderHook(() => useIntercom(), { wrapper }); + + act(() => { + result.current.update({ + authTokens: { + security_token: 'updated_token', + }, + }); + }); + + // Find the update call (not the boot or event handler calls) + const updateCalls = mockIntercom.mock.calls.filter(call => call[0] === 'update'); + const lastUpdateCall = updateCalls[updateCalls.length - 1]; + expect(lastUpdateCall).toBeDefined(); + expect(lastUpdateCall[1]).toMatchObject({ + auth_tokens: { + security_token: 'updated_token', + }, + }); + }); +}); \ No newline at end of file From 7ff5a4e28ae7b051e602e3aa3124d1927f471c93 Mon Sep 17 00:00:00 2001 From: Nick Hibberd Date: Fri, 1 Aug 2025 17:45:53 -0700 Subject: [PATCH 2/3] fix: sort imports in authTokens test file --- packages/react-use-intercom/test/authTokens.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-use-intercom/test/authTokens.test.tsx b/packages/react-use-intercom/test/authTokens.test.tsx index 657fb007..d19047de 100644 --- a/packages/react-use-intercom/test/authTokens.test.tsx +++ b/packages/react-use-intercom/test/authTokens.test.tsx @@ -1,5 +1,5 @@ -import * as React from 'react'; import { act, renderHook } from '@testing-library/react'; +import * as React from 'react'; import { IntercomProvider, useIntercom } from '../src'; From 4289f55f85e3bc2ae9610c9eb885a8c0d5550170 Mon Sep 17 00:00:00 2001 From: devrnt Date: Thu, 30 Jul 2026 19:50:01 +0200 Subject: [PATCH 3/3] Add setAuthTokens method for runtime auth token refresh Layer the setAuthTokens method (Intercom('setAuthTokens')) on top of the existing auth_tokens attribute so per-user Data Connector tokens can be refreshed at runtime without a full update, matching the vanilla Intercom SDK. Wire it through the provider, types, README and tests, clarify how authTokens relates to intercomUserJwt, and drop the `as any` casts in the test in favour of the typed window globals. Co-Authored-By: Claude Opus 4.8 --- .changeset/add-auth-tokens-support.md | 4 +- packages/react-use-intercom/README.md | 15 ++++++- packages/react-use-intercom/src/provider.tsx | 12 ++++++ packages/react-use-intercom/src/types.ts | 17 +++++++- .../test/authTokens.test.tsx | 42 +++++++++++++++---- 5 files changed, 78 insertions(+), 12 deletions(-) diff --git a/.changeset/add-auth-tokens-support.md b/.changeset/add-auth-tokens-support.md index 0219e1b0..0c8af282 100644 --- a/.changeset/add-auth-tokens-support.md +++ b/.changeset/add-auth-tokens-support.md @@ -2,9 +2,9 @@ "react-use-intercom": minor --- -Add support for auth_tokens in boot and update methods +Add support for auth_tokens in boot and update methods, plus a setAuthTokens method -Users can now pass authentication tokens to Intercom for secure data operations. The `authTokens` property accepts an object with any string key-value pairs. +Users can now pass authentication tokens to Intercom for secure data operations. The `authTokens` property accepts an object with any string key-value pairs, and the `setAuthTokens` method refreshes them at runtime without a full update. Example usage: ```js diff --git a/packages/react-use-intercom/README.md b/packages/react-use-intercom/README.md index c8d82c32..fedd6cbd 100644 --- a/packages/react-use-intercom/README.md +++ b/packages/react-use-intercom/README.md @@ -151,6 +151,7 @@ Used to retrieve all methods bundled with Intercom. These are based on the offic | showSpace | (spaceName: IntercomSpace) => void | Opens the Messenger with the specified space | showTicket | (ticketId: number) => void | Opens the Messenger with the specified ticket by `ticketId` | showConversation | (conversationId: number) => void | Opens the Messenger with the specified conversation by `conversationId` +| setAuthTokens | (authTokens: AuthTokens) => void | sets/refreshes the per-user Data Connector auth tokens at runtime without a full `update` #### Example ```ts @@ -184,7 +185,8 @@ const HomePage = () => { startSurvey, showSpace, showTicket, - showConversation + showConversation, + setAuthTokens, } = useIntercom(); const bootWithProps = () => boot({ name: 'Russo' }); @@ -204,6 +206,8 @@ const HomePage = () => { const handleShowSpace = () => showSpace('tasks'); const handleShowTicket = () => showTicket(123); const handleShowConversation = () => showConversation(123); + const handleSetAuthTokens = () => + setAuthTokens({ security_token: 'your-jwt' }); return ( <> @@ -232,6 +236,7 @@ const HomePage = () => { + ); }; @@ -272,6 +277,14 @@ All the Intercom default attributes/props are camel cased (`appId` instead of `a }) ``` +This is distinct from `intercomUserJwt`, which is for Messenger identity verification. To refresh a token during a session without sending a full `update`, use the `setAuthTokens` method: + +```ts +const { setAuthTokens } = useIntercom(); + +setAuthTokens({ security_token: 'refreshed-jwt' }); +``` + ## Playground Small playground to showcase the functionalities of `react-use-intercom`. diff --git a/packages/react-use-intercom/src/provider.tsx b/packages/react-use-intercom/src/provider.tsx index efe0dd8c..f4931f11 100644 --- a/packages/react-use-intercom/src/provider.tsx +++ b/packages/react-use-intercom/src/provider.tsx @@ -6,6 +6,7 @@ import initialize from './initialize'; import * as logger from './logger'; import { mapIntercomPropsToRawIntercomProps } from './mappers'; import { + AuthTokens, IntercomContextValues, IntercomProps, IntercomProviderProps, @@ -291,6 +292,15 @@ export const IntercomProvider: React.FC< [ensureIntercom], ); + const setAuthTokens = React.useCallback( + (authTokens: AuthTokens) => { + ensureIntercom('setAuthTokens', () => { + IntercomAPI('setAuthTokens', authTokens); + }); + }, + [ensureIntercom], + ); + const providerValue = React.useMemo(() => { return { boot, @@ -312,6 +322,7 @@ export const IntercomProvider: React.FC< showNews, showTicket, showConversation, + setAuthTokens, }; }, [ boot, @@ -333,6 +344,7 @@ export const IntercomProvider: React.FC< showNews, showTicket, showConversation, + setAuthTokens, ]); return ( diff --git a/packages/react-use-intercom/src/types.ts b/packages/react-use-intercom/src/types.ts index 8e746548..963c1446 100644 --- a/packages/react-use-intercom/src/types.ts +++ b/packages/react-use-intercom/src/types.ts @@ -254,6 +254,9 @@ export type DataAttributes = { * ``` * * @see {@link https://www.intercom.com/help/en/articles/6615543-setting-up-data-connectors-authentication#h_5343ec2d2c} + * + * @remarks Distinct from `intercomUserJwt` (Messenger identity verification). To refresh tokens at + * runtime without a full `update`, use the `setAuthTokens` method. */ authTokens?: AuthTokens; }; @@ -279,7 +282,8 @@ export type IntercomMethod = | 'showSpace' | 'showNews' | 'showTicket' - | 'showConversation'; + | 'showConversation' + | 'setAuthTokens'; export type RawIntercomProps = RawMessengerAttributes & RawDataAttributes; @@ -493,6 +497,17 @@ export type IntercomContextValues = { * @see {@link https://developers.intercom.com/installing-intercom/web/methods/#intercomshowconversation-conversationid} */ showConversation: (conversationId: number) => void; + /** + * Sets or refreshes the per-user authentication tokens used for Data Connector requests, + * without sending a full `update`. + * + * @remarks Use this to refresh a short-lived token during a session. For the initial tokens, + * pass `authTokens` to `boot`/`update` instead. + * @see {@link https://www.intercom.com/help/en/articles/6615543-setting-up-data-connectors-authentication} + * + * @param authTokens object of token name → token (JWT) pairs + */ + setAuthTokens: (authTokens: AuthTokens) => void; }; export type IntercomProviderProps = { diff --git a/packages/react-use-intercom/test/authTokens.test.tsx b/packages/react-use-intercom/test/authTokens.test.tsx index d19047de..8b5f92bc 100644 --- a/packages/react-use-intercom/test/authTokens.test.tsx +++ b/packages/react-use-intercom/test/authTokens.test.tsx @@ -7,8 +7,8 @@ describe('auth_tokens', () => { it('should pass auth_tokens during boot', () => { const appId = 'app123'; const mockIntercom = jest.fn(); - (window as any).Intercom = mockIntercom; - (window as any).intercomSettings = undefined; + window.Intercom = mockIntercom; + window.intercomSettings = undefined; const wrapper = ({ children }: { children: React.ReactNode }) => ( {children} @@ -45,11 +45,13 @@ describe('auth_tokens', () => { it('should pass auth_tokens during update', () => { const appId = 'app123'; const mockIntercom = jest.fn(); - (window as any).Intercom = mockIntercom; - (window as any).intercomSettings = { app_id: appId }; + window.Intercom = mockIntercom; + window.intercomSettings = { app_id: appId }; const wrapper = ({ children }: { children: React.ReactNode }) => ( - {children} + + {children} + ); const { result } = renderHook(() => useIntercom(), { wrapper }); @@ -62,8 +64,9 @@ describe('auth_tokens', () => { }); }); - // Find the update call (not the boot or event handler calls) - const updateCalls = mockIntercom.mock.calls.filter(call => call[0] === 'update'); + const updateCalls = mockIntercom.mock.calls.filter( + (call) => call[0] === 'update', + ); const lastUpdateCall = updateCalls[updateCalls.length - 1]; expect(lastUpdateCall).toBeDefined(); expect(lastUpdateCall[1]).toMatchObject({ @@ -72,4 +75,27 @@ describe('auth_tokens', () => { }, }); }); -}); \ No newline at end of file + + it('should pass auth_tokens through setAuthTokens', () => { + const appId = 'app123'; + const mockIntercom = jest.fn(); + window.Intercom = mockIntercom; + window.intercomSettings = { app_id: appId }; + + const wrapper = ({ children }: { children: React.ReactNode }) => ( + + {children} + + ); + + const { result } = renderHook(() => useIntercom(), { wrapper }); + + act(() => { + result.current.setAuthTokens({ security_token: 'refreshed_token' }); + }); + + expect(mockIntercom).toHaveBeenCalledWith('setAuthTokens', { + security_token: 'refreshed_token', + }); + }); +});