Skip to content

Commit 216b199

Browse files
authored
fix(search): clarify source settings and permission updates (#7715)
* fix(search): clarify source settings and permission updates * fix(search): clarify recovery when Search is disabled
1 parent 2aa642e commit 216b199

16 files changed

Lines changed: 195 additions & 48 deletions

File tree

apps/docs/content/docs/platform/self-hosting/integrations-oauth.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ Keep **Expire user authorization tokens** enabled so Sim receives the refresh to
203203

204204
Complete the installation through [the GitHub Search source setup](/search/github#add-a-repository).
205205

206-
If you replace a deployment's GitHub App, an organization admin must first open **Settings → Sources → Update configurations**. When Search is disabled, this action is under **Settings → Connected accounts → Providers**. This applies the deployment's current App configuration to the existing providers while preserving their saved identities. Accounts whose App configuration changed must reconnect. Then reconnect personal GitHub accounts and connect an installation of the new App. Reconnecting alone cannot update the organization's saved App configuration.
206+
If you replace a deployment's GitHub App, an organization admin selects **Settings → Sources → More → Refresh connection settings**. When Search is disabled, use **Connected accounts → Providers → Update configurations** in organization settings. This applies the deployment's current App configuration to all existing providers while preserving their option IDs. Accounts whose App configuration changed must reconnect. Then reconnect personal GitHub accounts and connect an installation of the new App. Reconnecting alone cannot update the organization's saved App configuration.
207207

208208
</Step>
209209
</Steps>

apps/docs/content/docs/search/confluence.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,6 @@ Configure one shared Confluence OAuth integration for your deployment. This powe
196196
2. Under **Authorization → OAuth 2.0 (3LO)**, add `https://<your-sim-domain>/api/auth/oauth2/callback/confluence` to **Callback URLs**, keep existing callbacks used by the deployment, and save.
197197
3. Under **Permissions**, add the Confluence API and configure the full `confluence` scope list for your release in [Sim's OAuth configuration](https://github.com/simstudioai/sim/blob/staging/apps/sim/lib/oauth/oauth.ts), including `read:group:confluence`. Also add **User Identity API** with `read:me`. Sim requests `offline_access` for refresh tokens. The service-account read scopes above do not replace the broader shared OAuth scope set.
198198
4. Enable sharing under **Distribution**. Set `CONFLUENCE_CLIENT_ID` and `CONFLUENCE_CLIENT_SECRET` from the app's **Settings**, verify `NEXT_PUBLIC_APP_URL`, and restart Sim.
199-
5. Start authorization from **Integrations** and select the configured site. After changing the deployment's OAuth client or requested scopes, an organization admin selects **Settings → Sources → Update configurations**, then affected teammates reconnect.
199+
5. Start authorization from **Integrations** and select the configured site. After changing the deployment's OAuth client or requested scopes, an organization admin selects **Settings → Sources → More → Refresh connection settings**, then affected teammates reconnect.
200200

201201
A callback mismatch needs a corrected callback URL; a connection that works only for the app owner needs sharing enabled. See Atlassian's [OAuth configuration guide](https://developer.atlassian.com/cloud/confluence/oauth-2-3lo-apps/) and Sim's [deployment reference](/platform/self-hosting/integrations-oauth).

apps/docs/content/docs/search/github.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ This is an installation plus personal authorization flow. GitHub Search does not
9696
| Identity verification fails | Verify the email used by your Sim account in GitHub's email settings, then reconnect. A public profile email alone is insufficient. |
9797
| Authorization fails after installation | Return to Sim and start **Connect** there. Do not enable authorization during installation. |
9898
| Account authorization did not complete | Start the connection again from Sim. If it repeats, contact your organization admin or Sim support. For self-hosted Sim, check the [App callback and credentials](/platform/self-hosting/integrations-oauth#github-search). |
99-
| Update GitHub using Update configurations in organization settings before connecting this source | An organization admin must select **Settings → Sources → Update configurations**, then reconnect GitHub. |
99+
| GitHub asks for a provider configuration update | An organization admin selects **Settings → Sources → More → Refresh connection settings**, then affected users reconnect GitHub. |
100100
| Indexed files no longer appear | Confirm your own repository access, App repository selection, and connection status. Installation-indexed content is also withheld when GitHub cannot verify current access; retry once GitHub is available. |
101101
| Sync is incomplete | Review the source status. Very large Git trees, file size limits, and unreadable files can limit indexing. |
102102
| Empty repository returns an error | Add an initial commit, then sync again. GitHub does not return a file tree for an uninitialized repository. |

apps/docs/content/docs/search/gmail.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ Users do not need to create Google Cloud credentials. The deployment operator co
9797
1. In [Google Cloud Console](https://console.cloud.google.com/), select your project. Open **APIs & Services → Library**, find **Gmail API**, and enable it.
9898
2. Open **Google Auth platform → Branding**. Select **Get started** if needed, then enter the app name, support email, and contact email. Under **Audience**, use **Internal** only for an app limited to your Google Workspace organization; otherwise use **External** and add test users while testing. Review the app's permissions under **Data Access → Add or remove scopes**, using the current Sim scopes below. Follow Google's [consent and verification guidance](https://developers.google.com/workspace/guides/configure-oauth-consent) for your audience.
9999
3. Open **Google Auth platform → Clients → Create client**. Choose **Web application**, give the client a name, and add the URI below under **Authorized redirect URIs**. If this instance already has a Google client, add this URI to that client instead. See [Google's credential setup](https://developers.google.com/workspace/guides/create-credentials#web-application).
100-
4. Save the client ID and secret as `GOOGLE_CLIENT_ID` and `GOOGLE_CLIENT_SECRET`. Set `NEXT_PUBLIC_APP_URL` to the same Sim origin used in the callback, then restart Sim. See [Integrations & OAuth](/platform/self-hosting/integrations-oauth). If you change an existing deployment's OAuth client or scopes, an organization admin selects **Settings → Sources → Update configurations**, then affected teammates reconnect.
100+
4. Save the client ID and secret as `GOOGLE_CLIENT_ID` and `GOOGLE_CLIENT_SECRET`. Set `NEXT_PUBLIC_APP_URL` to the same Sim origin used in the callback, then restart Sim. See [Integrations & OAuth](/platform/self-hosting/integrations-oauth). If you change an existing deployment's OAuth client or scopes, an organization admin selects **Settings → Sources → More → Refresh connection settings**, then affected teammates reconnect.
101101

102102
```text
103103
https://<your-sim-domain>/api/auth/oauth2/callback/google-email

apps/docs/content/docs/search/google-calendar.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ The deployment operator configures Google OAuth once; teammates then use the nor
9494
1. In [Google Cloud Console](https://console.cloud.google.com/), select your project. Open **APIs & Services → Library**, find **Google Calendar API**, and enable it.
9595
2. Open **Google Auth platform → Branding** and configure the app name and contact details. Under **Audience**, choose **Internal** for your Google Workspace organization only, or **External** for other users. Add test users while an external app is testing. Review **Data Access → Add or remove scopes** using the current Sim scopes below. See Google's [consent and verification guidance](https://developers.google.com/workspace/guides/configure-oauth-consent).
9696
3. Open **Google Auth platform → Clients → Create client**, choose **Web application**, and add the URI below under **Authorized redirect URIs**. Add it to the existing Google client if the instance already uses one. See [Google's credential setup](https://developers.google.com/workspace/guides/create-credentials#web-application).
97-
4. Save the client ID and secret as `GOOGLE_CLIENT_ID` and `GOOGLE_CLIENT_SECRET`. Set `NEXT_PUBLIC_APP_URL` to the same Sim origin used in the callback, then restart Sim. See [Integrations & OAuth](/platform/self-hosting/integrations-oauth). If you change an existing deployment's OAuth client or scopes, an organization admin selects **Settings → Sources → Update configurations**, then affected teammates reconnect.
97+
4. Save the client ID and secret as `GOOGLE_CLIENT_ID` and `GOOGLE_CLIENT_SECRET`. Set `NEXT_PUBLIC_APP_URL` to the same Sim origin used in the callback, then restart Sim. See [Integrations & OAuth](/platform/self-hosting/integrations-oauth). If you change an existing deployment's OAuth client or scopes, an organization admin selects **Settings → Sources → More → Refresh connection settings**, then affected teammates reconnect.
9898

9999
```text
100100
https://<your-sim-domain>/api/auth/oauth2/callback/google-calendar

apps/docs/content/docs/search/google-drive.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ https://<your-sim-domain>/api/auth/oauth2/callback/google-drive
151151

152152
This Google Cloud example uses one client for all three services. Replace `https://sim.example.com` with your Sim origin and add only the callbacks for services you enable.
153153

154-
Save the client ID and secret as `GOOGLE_CLIENT_ID` and `GOOGLE_CLIENT_SECRET`. Set `NEXT_PUBLIC_APP_URL` to the same Sim origin used in the callback, then restart Sim. See [Integrations & OAuth](/platform/self-hosting/integrations-oauth). If you change an existing deployment's OAuth client or scopes, an organization admin selects **Settings → Sources → Update configurations**, then affected teammates reconnect.
154+
Save the client ID and secret as `GOOGLE_CLIENT_ID` and `GOOGLE_CLIENT_SECRET`. Set `NEXT_PUBLIC_APP_URL` to the same Sim origin used in the callback, then restart Sim. See [Integrations & OAuth](/platform/self-hosting/integrations-oauth). If you change an existing deployment's OAuth client or scopes, an organization admin selects **Settings → Sources → More → Refresh connection settings**, then affected teammates reconnect.
155155

156156
The current Sim Drive OAuth connection uses these scopes:
157157

apps/docs/content/docs/search/index.mdx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ Search brings your connected sources into one place. An organization admin allow
1818

1919
As an organization admin, open **Settings → Sources** and turn on the integration. It stays in the list; no setup page opens automatically.
2020

21-
The switch permits the integration in your organization. It does not connect an account, grant document access, or start indexing. Teammates connect from **Integrations** in the main sidebar; only admins manage these switches.
21+
The switch saves immediately and permits the integration in your organization. It does not connect an account, grant document access, or start indexing. Teammates connect from **Integrations** in the main sidebar; only admins manage these switches.
2222

2323
</Step>
2424
<Step>
@@ -41,9 +41,9 @@ Open **Integrations** in the main sidebar and select **Connect** if prompted—e
4141
</Step>
4242
</Steps>
4343

44-
<Image className="mx-auto h-auto w-full max-w-xl" src="/static/search/integration-settings.jpg" alt="Organization Sources settings with provider switches, Set up or Manage actions, and Update configurations" width={800} height={674} />
44+
<Image className="mx-auto h-auto w-full max-w-xl" src="/static/search/integration-settings.jpg" alt="Organization Sources settings with provider switches and Set up or Manage actions" width={800} height={674} />
4545

46-
Source availability depends on the deployment and organization policy. An unavailable source needs operator configuration before setup can continue. If an existing provider asks for a configuration update, an admin selects **Update configurations** on this page, then affected teammates reconnect.
46+
Source availability depends on the deployment and organization policy. An unavailable source needs operator configuration before setup can continue. If an existing provider asks for a configuration update after a deployment change, an admin selects **More → Refresh connection settings** on this page, then affected teammates reconnect.
4747

4848
## Choose the right connection method
4949

@@ -100,7 +100,7 @@ For Gmail, one configuration is usually enough. **Add sync configuration** creat
100100
| Source tab | What you can do |
101101
| --- | --- |
102102
| **Documents** | Find indexed documents, inspect processing status, retry failed indexing, or exclude and restore documents. |
103-
| **Settings** | Edit the source's scope, filters, and supported indexing credentials. Save your changes before leaving. |
103+
| **Settings** | Edit the source's scope, filters, and supported indexing credentials. Select **Save** to apply changes or **Discard** to undo them. |
104104
| **Sync history** | Review run dates, document changes, and any sync or account errors. |
105105

106106
**Sync using** shows the method selected when the source was created. Create a new source to change that method. To replace a supported indexing credential, select its replacement and use **Change indexing account**.

apps/docs/content/docs/search/jira.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,6 @@ The deployment operator configures one shared Jira OAuth integration. Teammates
138138

139139
3. Under **Permissions**, add **Jira API**, then **Configure** its classic and granular scopes for Jira, Jira Service Management, and Assets. Separately add **User Identity API** with `read:me`. Sim requests `offline_access` in the authorization URL for refresh tokens. Configure the full `jira` scope list for your release in [Sim's OAuth configuration](https://github.com/simstudioai/sim/blob/staging/apps/sim/lib/oauth/oauth.ts); the Search read scopes above are only a subset of this shared integration's permissions.
140140
4. Under **Distribution**, enable sharing so teammates can authorize the app. Copy the client ID and secret from **Settings** into `JIRA_CLIENT_ID` and `JIRA_CLIENT_SECRET`, set the correct `NEXT_PUBLIC_APP_URL`, and restart Sim.
141-
5. Start a connection from **Integrations**. Confirm that Atlassian lists the intended site, then return to Sim. After changing the deployment's OAuth client or requested scopes, an organization admin selects **Settings → Sources → Update configurations**, then affected teammates reconnect.
141+
5. Start a connection from **Integrations**. Confirm that Atlassian lists the intended site, then return to Sim. After changing the deployment's OAuth client or requested scopes, an organization admin selects **Settings → Sources → More → Refresh connection settings**, then affected teammates reconnect.
142142

143143
For a local instance using `NEXT_PUBLIC_APP_URL=http://localhost:3000`, register `http://localhost:3000/api/auth/oauth2/callback/jira`. Use a separate development OAuth app when production callbacks must remain unchanged. After updating local client credentials or the app URL, restart Sim and begin a new connection from **Integrations**. If only the app owner can connect, check **Distribution**. See Atlassian's [OAuth configuration and sharing guide](https://developer.atlassian.com/cloud/jira/platform/oauth-2-3lo-apps/) and Sim's [deployment reference](/platform/self-hosting/integrations-oauth).
-22.8 KB
Loading

apps/sim/app/api/auth/oauth/credentials/route.test.ts

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ vi.mock('@/lib/workspaces/permissions/utils', () => permissionsMock)
3838

3939
vi.mock('@/lib/permission-groups/config-scope.server', () => permissionGroupScopeMock)
4040

41+
import { getCanonicalScopesForProvider, getMissingRequiredScopes } from '@/lib/oauth/utils'
4142
import { DEFAULT_PERMISSION_GROUP_CONFIG } from '@/lib/permission-groups/fields'
4243
import { GET } from '@/app/api/auth/oauth/credentials/route'
4344

@@ -146,6 +147,100 @@ describe('OAuth Credentials API Route', () => {
146147
await expect(response.json()).resolves.toEqual({ credentials: [] })
147148
})
148149

150+
describe.each(['list', 'detail'] as const)('OAuth grant scopes in %s responses', (mode) => {
151+
const workspaceId = '3f1c8a54-1c2e-4a1b-9d6e-2b7c5a9f0e11'
152+
153+
beforeEach(() => {
154+
hybridAuthMockFns.mockCheckSessionOrInternalAuth.mockReset().mockResolvedValue({
155+
success: true,
156+
userId: 'user-123',
157+
authType: 'session',
158+
})
159+
permissionsMockFns.mockCheckWorkspaceAccess.mockResolvedValue({
160+
exists: true,
161+
hasAccess: true,
162+
canWrite: true,
163+
canAdmin: true,
164+
})
165+
permissionGroupScopeMockFns.mockResolvePermissionGroupConfig.mockResolvedValue(
166+
DEFAULT_PERMISSION_GROUP_CONFIG
167+
)
168+
})
169+
170+
async function requestCredential(providerId: string, scope: string | null) {
171+
const row = {
172+
id: 'credential-1',
173+
workspaceId,
174+
type: 'oauth',
175+
displayName: 'Connected account',
176+
providerId,
177+
accountId: 'account-1',
178+
scope,
179+
updatedAt: new Date('2026-01-01T00:00:00Z'),
180+
accountProviderId: providerId,
181+
accountScope: scope,
182+
accountUpdatedAt: new Date('2026-01-01T00:00:00Z'),
183+
}
184+
if (mode === 'detail') {
185+
dbChainMockFns.limit.mockResolvedValueOnce([row])
186+
} else {
187+
dbChainMockFns.where.mockResolvedValueOnce([row]).mockResolvedValueOnce([])
188+
}
189+
const query =
190+
mode === 'detail'
191+
? '?credentialId=credential-1'
192+
: `?provider=${providerId}&workspaceId=${workspaceId}`
193+
const response = await GET(createMockRequestWithQuery('GET', query))
194+
expect(response.status).toBe(200)
195+
const data = await response.json()
196+
expect(data.credentials).toHaveLength(1)
197+
return data.credentials[0]
198+
}
199+
200+
it.each([null, '', ' \t\n '])(
201+
'does not synthesize a Confluence grant from missing scope metadata %j',
202+
async (scope) => {
203+
const credential = await requestCredential('confluence', scope)
204+
205+
expect(credential.scopes).toEqual([])
206+
expect(
207+
getMissingRequiredScopes(credential, getCanonicalScopesForProvider('confluence'))
208+
).toContain('read:group:confluence')
209+
}
210+
)
211+
212+
it('preserves the actual older Confluence grant and identifies missing group access', async () => {
213+
const requiredScopes = getCanonicalScopesForProvider('confluence')
214+
const previousGrant = requiredScopes.filter((scope) => scope !== 'read:group:confluence')
215+
const credential = await requestCredential('confluence', previousGrant.join(','))
216+
217+
expect(credential.scopes).toEqual(previousGrant)
218+
expect(getMissingRequiredScopes(credential, requiredScopes)).toEqual([
219+
'read:group:confluence',
220+
])
221+
})
222+
223+
it('preserves a complete Confluence grant without requesting another update', async () => {
224+
const grantedScopes = getCanonicalScopesForProvider('confluence')
225+
const credential = await requestCredential('confluence', grantedScopes.join(' '))
226+
227+
expect(credential.scopes).toEqual(grantedScopes)
228+
expect(getMissingRequiredScopes(credential, grantedScopes)).toEqual([])
229+
})
230+
231+
it.each([null, '', ' \t\n '])(
232+
'preserves the Box omitted-scope fallback for %j',
233+
async (scope) => {
234+
const credential = await requestCredential('box', scope)
235+
const requiredScopes = getCanonicalScopesForProvider('box')
236+
237+
expect(requiredScopes.length).toBeGreaterThan(0)
238+
expect(credential.scopes).toEqual(requiredScopes)
239+
expect(getMissingRequiredScopes(credential, requiredScopes)).toEqual([])
240+
}
241+
)
242+
})
243+
149244
/** The session/executor split documented on {@link integrationsWithheldFromSession} in the route. */
150245
describe('integrations.manage', () => {
151246
const INTEGRATIONS_WITHHELD = {

0 commit comments

Comments
 (0)