Conversation
Signed-off-by: Joana Maia <jmaia@contractor.linuxfoundation.org>
There was a problem hiding this comment.
Pull request overview
Adds Intercom to the frontend and wires it into the auth lifecycle so the widget boots for authenticated users and shuts down on logout.
Changes:
- Added Intercom configuration (env + app config) including Auth0 claim keys.
- Introduced an Intercom utility for script loading + boot/update/shutdown.
- Hooked Intercom boot/shutdown into the authentication flow (login/logout).
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 7 comments.
| File | Description |
|---|---|
| frontend/src/utils/intercom/index.ts | New Intercom loader/boot/update/shutdown utility module. |
| frontend/src/modules/auth/store/auth.actions.ts | Boots Intercom after login and shuts it down on logout. |
| frontend/src/config.js | Adds intercom config (appId, api base, Auth0 claim keys). |
| frontend/.env.dist.local | Adds a distribution env var for Intercom app id. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // Set JWT in intercomSettings before boot — required for identity verification | ||
| if (options.intercom_user_jwt) { | ||
| window.intercomSettings = window.intercomSettings || {}; | ||
| window.intercomSettings.intercom_user_jwt = options.intercom_user_jwt; | ||
| } |
There was a problem hiding this comment.
intercom_user_jwt is stripped out of the payload passed to Intercom('boot', ...). If identity verification relies on providing intercom_user_jwt in the boot call (as Intercom’s standard flow does), this can lead to users being booted without verification. Include intercom_user_jwt in the boot payload (or pass window.intercomSettings as the boot object after merging user fields) so verification is reliably applied.
| const { intercom_user_jwt: _jwt, ...bootOptions } = options; | ||
| window.Intercom('boot', { | ||
| api_base: config.intercom.apiBase, | ||
| app_id: config.intercom.appId, | ||
| ...bootOptions, |
There was a problem hiding this comment.
intercom_user_jwt is stripped out of the payload passed to Intercom('boot', ...). If identity verification relies on providing intercom_user_jwt in the boot call (as Intercom’s standard flow does), this can lead to users being booted without verification. Include intercom_user_jwt in the boot payload (or pass window.intercomSettings as the boot object after merging user fields) so verification is reliably applied.
| const { intercom_user_jwt: _jwt, ...bootOptions } = options; | |
| window.Intercom('boot', { | |
| api_base: config.intercom.apiBase, | |
| app_id: config.intercom.appId, | |
| ...bootOptions, | |
| window.Intercom('boot', { | |
| api_base: config.intercom.apiBase, | |
| app_id: config.intercom.appId, | |
| ...options, |
| if (!config.intercom.appId) { | ||
| console.info('Intercom: Disabled (no appId configured)'); | ||
| reject(new Error('No Intercom app ID configured')); | ||
| return; | ||
| } |
There was a problem hiding this comment.
Treating “Intercom disabled by configuration” as a rejected Promise forces callers to handle this as an error path, even though it’s an expected state. Prefer resolving as a no-op when appId is missing (and/or exposing an isEnabled() helper) so downstream auth flows don’t need to special-case this.
| script.onerror = (error) => { | ||
| isLoading = false; | ||
| console.error('Intercom: Failed to load script', error); | ||
| }; |
There was a problem hiding this comment.
If the script fails quickly (onerror), boot() will still poll for up to 10s before rejecting, which delays auth lifecycle completion and adds avoidable background work. Consider making loadScript() return a shared Promise (cached across calls) that resolves on onload and rejects on onerror, and have boot() await that instead of polling; this will fail fast, simplify control flow, and avoid repeated intervals/timeouts for concurrent boots.
| const checkLoaded = setInterval(() => { | ||
| if (isLoaded && window.Intercom) { | ||
| clearInterval(checkLoaded); | ||
| clearTimeout(timeoutHandle); | ||
|
|
||
| if (isBooted) { | ||
| const { intercom_user_jwt: _jwt, ...updateOptions } = options; | ||
| update(updateOptions); | ||
| resolve(); | ||
| return; | ||
| } | ||
|
|
||
| isBooted = true; | ||
| try { | ||
| const { intercom_user_jwt: _jwt, ...bootOptions } = options; | ||
| window.Intercom('boot', { | ||
| api_base: config.intercom.apiBase, | ||
| app_id: config.intercom.appId, | ||
| ...bootOptions, | ||
| }); | ||
| resolve(); | ||
| } catch (error) { | ||
| isBooted = false; | ||
| console.error('Intercom: Boot failed', error); | ||
| reject(error); | ||
| } | ||
| } | ||
| }, 100); | ||
|
|
||
| const timeoutHandle = setTimeout(() => { | ||
| clearInterval(checkLoaded); | ||
| if (!isBooted) { | ||
| isLoading = false; | ||
| reject(new Error('Intercom script failed to load')); | ||
| } | ||
| }, 10000); |
There was a problem hiding this comment.
If the script fails quickly (onerror), boot() will still poll for up to 10s before rejecting, which delays auth lifecycle completion and adds avoidable background work. Consider making loadScript() return a shared Promise (cached across calls) that resolves on onload and rejects on onerror, and have boot() await that instead of polling; this will fail fast, simplify control flow, and avoid repeated intervals/timeouts for concurrent boots.
| email: user.email, | ||
| intercom_user_jwt: intercomJwt, | ||
| }).catch((error: any) => { | ||
| console.error('Intercom: Boot failed', error); |
There was a problem hiding this comment.
Logging the raw error object can inadvertently include sensitive context (depending on how errors are produced/serialized), and this path is triggered during authentication. Prefer logging a sanitized message (e.g., error?.message) and avoid dumping full objects that might contain user identifiers or token-related info.
| console.error('Intercom: Boot failed', error); | |
| console.error('Intercom: Boot failed:', (error && error.message) ? error.message : String(error)); |
| VUE_APP_CONVERSATIONS_PUBLIC_URL=http://localhost:3000 | ||
| VUE_APP_NANGO_URL=http://localhost:3003 | ||
| VUE_APP_ENV=local | ||
| VUE_APP_INTERCOM_APP_ID=mxl90k6y |
There was a problem hiding this comment.
.env.dist.local is typically a template; committing a concrete app id can cause accidental usage against the wrong Intercom workspace in local/dev setups. Consider leaving this blank or using an obvious placeholder value (with a short comment) to encourage explicit configuration per environment.
| VUE_APP_INTERCOM_APP_ID=mxl90k6y | |
| # Set your Intercom app id for this environment (leave blank if not used). | |
| VUE_APP_INTERCOM_APP_ID= |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
| boot, | ||
| update, | ||
| shutdown, | ||
| }); |
There was a problem hiding this comment.


This pull request introduces Intercom integration into the frontend application, enabling user-specific Intercom chat support based on authentication state. The main changes include configuration additions, dynamic script loading and lifecycle management for Intercom, and hooks into the authentication flow to boot and shut down Intercom as users log in or out.
Intercom Integration
appId, API base, and Auth0 claim keys for user identification. [1] [2] [3]frontend/src/utils/intercom/index.tsto handle dynamic loading, booting, updating, and shutdown of the Intercom widget, with support for JWT-based identity verification.Authentication Flow Updates
Note
Medium Risk
Adds a third-party Intercom widget that boots using Auth0-provided user/JWT claims and injects an external script at runtime, which can affect auth/session behavior and client-side performance. Failures are mostly contained to logging, but misconfiguration could prevent chat from loading or leak incorrect identity metadata.
Overview
Adds Intercom support to the frontend, including a new
config.intercomsection and aVUE_APP_INTERCOM_APP_IDenv var for configuring the widget.Wires Intercom into the auth flow by booting the widget after Auth0 user hydration (using configured Auth0 claims for
user_idandintercom_user_jwt) and shutting it down on logout.Introduces a new
utils/intercommodule that dynamically loads the Intercom script, manages boot/update/shutdown state, and supports JWT-based identity verification viawindow.intercomSettings.Written by Cursor Bugbot for commit b0e6cb1. This will update automatically on new commits. Configure here.