Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ Keep **Expire user authorization tokens** enabled so Sim receives the refresh to

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

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.
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.

</Step>
</Steps>
Expand Down
2 changes: 1 addition & 1 deletion apps/docs/content/docs/search/confluence.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,6 @@ Configure one shared Confluence OAuth integration for your deployment. This powe
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.
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.
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.
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.
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.

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).
2 changes: 1 addition & 1 deletion apps/docs/content/docs/search/github.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ This is an installation plus personal authorization flow. GitHub Search does not
| 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. |
| Authorization fails after installation | Return to Sim and start **Connect** there. Do not enable authorization during installation. |
| 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). |
| Update GitHub using Update configurations in organization settings before connecting this source | An organization admin must select **Settings → Sources → Update configurations**, then reconnect GitHub. |
| GitHub asks for a provider configuration update | An organization admin selects **Settings → Sources → More → Refresh connection settings**, then affected users reconnect GitHub. |
| 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. |
| Sync is incomplete | Review the source status. Very large Git trees, file size limits, and unreadable files can limit indexing. |
| Empty repository returns an error | Add an initial commit, then sync again. GitHub does not return a file tree for an uninitialized repository. |
Expand Down
2 changes: 1 addition & 1 deletion apps/docs/content/docs/search/gmail.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ Users do not need to create Google Cloud credentials. The deployment operator co
1. In [Google Cloud Console](https://console.cloud.google.com/), select your project. Open **APIs & Services → Library**, find **Gmail API**, and enable it.
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.
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).
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.
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.

```text
https://<your-sim-domain>/api/auth/oauth2/callback/google-email
Expand Down
2 changes: 1 addition & 1 deletion apps/docs/content/docs/search/google-calendar.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ The deployment operator configures Google OAuth once; teammates then use the nor
1. In [Google Cloud Console](https://console.cloud.google.com/), select your project. Open **APIs & Services → Library**, find **Google Calendar API**, and enable it.
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).
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).
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.
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.

```text
https://<your-sim-domain>/api/auth/oauth2/callback/google-calendar
Expand Down
2 changes: 1 addition & 1 deletion apps/docs/content/docs/search/google-drive.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ https://<your-sim-domain>/api/auth/oauth2/callback/google-drive

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.

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.
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.

The current Sim Drive OAuth connection uses these scopes:

Expand Down
8 changes: 4 additions & 4 deletions apps/docs/content/docs/search/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Search brings your connected sources into one place. An organization admin allow

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

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.
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.

</Step>
<Step>
Expand All @@ -41,9 +41,9 @@ Open **Integrations** in the main sidebar and select **Connect** if prompted—e
</Step>
</Steps>

<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} />
<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} />

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.
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.

## Choose the right connection method

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

**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**.
Expand Down
2 changes: 1 addition & 1 deletion apps/docs/content/docs/search/jira.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,6 @@ The deployment operator configures one shared Jira OAuth integration. Teammates

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.
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.
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.
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.

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).
Binary file modified apps/docs/public/static/search/integration-settings.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
95 changes: 95 additions & 0 deletions apps/sim/app/api/auth/oauth/credentials/route.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ vi.mock('@/lib/workspaces/permissions/utils', () => permissionsMock)

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

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

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

describe.each(['list', 'detail'] as const)('OAuth grant scopes in %s responses', (mode) => {
const workspaceId = '3f1c8a54-1c2e-4a1b-9d6e-2b7c5a9f0e11'

beforeEach(() => {
hybridAuthMockFns.mockCheckSessionOrInternalAuth.mockReset().mockResolvedValue({
success: true,
userId: 'user-123',
authType: 'session',
})
permissionsMockFns.mockCheckWorkspaceAccess.mockResolvedValue({
exists: true,
hasAccess: true,
canWrite: true,
canAdmin: true,
})
permissionGroupScopeMockFns.mockResolvePermissionGroupConfig.mockResolvedValue(
DEFAULT_PERMISSION_GROUP_CONFIG
)
})

async function requestCredential(providerId: string, scope: string | null) {
const row = {
id: 'credential-1',
workspaceId,
type: 'oauth',
displayName: 'Connected account',
providerId,
accountId: 'account-1',
scope,
updatedAt: new Date('2026-01-01T00:00:00Z'),
accountProviderId: providerId,
accountScope: scope,
accountUpdatedAt: new Date('2026-01-01T00:00:00Z'),
}
if (mode === 'detail') {
dbChainMockFns.limit.mockResolvedValueOnce([row])
} else {
dbChainMockFns.where.mockResolvedValueOnce([row]).mockResolvedValueOnce([])
}
const query =
mode === 'detail'
? '?credentialId=credential-1'
: `?provider=${providerId}&workspaceId=${workspaceId}`
const response = await GET(createMockRequestWithQuery('GET', query))
expect(response.status).toBe(200)
const data = await response.json()
expect(data.credentials).toHaveLength(1)
return data.credentials[0]
}

it.each([null, '', ' \t\n '])(
'does not synthesize a Confluence grant from missing scope metadata %j',
async (scope) => {
const credential = await requestCredential('confluence', scope)

expect(credential.scopes).toEqual([])
expect(
getMissingRequiredScopes(credential, getCanonicalScopesForProvider('confluence'))
).toContain('read:group:confluence')
}
)

it('preserves the actual older Confluence grant and identifies missing group access', async () => {
const requiredScopes = getCanonicalScopesForProvider('confluence')
const previousGrant = requiredScopes.filter((scope) => scope !== 'read:group:confluence')
const credential = await requestCredential('confluence', previousGrant.join(','))

expect(credential.scopes).toEqual(previousGrant)
expect(getMissingRequiredScopes(credential, requiredScopes)).toEqual([
'read:group:confluence',
])
})

it('preserves a complete Confluence grant without requesting another update', async () => {
const grantedScopes = getCanonicalScopesForProvider('confluence')
const credential = await requestCredential('confluence', grantedScopes.join(' '))

expect(credential.scopes).toEqual(grantedScopes)
expect(getMissingRequiredScopes(credential, grantedScopes)).toEqual([])
})

it.each([null, '', ' \t\n '])(
'preserves the Box omitted-scope fallback for %j',
async (scope) => {
const credential = await requestCredential('box', scope)
const requiredScopes = getCanonicalScopesForProvider('box')

expect(requiredScopes.length).toBeGreaterThan(0)
expect(credential.scopes).toEqual(requiredScopes)
expect(getMissingRequiredScopes(credential, requiredScopes)).toEqual([])
}
)
})

/** The session/executor split documented on {@link integrationsWithheldFromSession} in the route. */
describe('integrations.manage', () => {
const INTEGRATIONS_WITHHELD = {
Expand Down
12 changes: 7 additions & 5 deletions apps/sim/app/api/auth/oauth/credentials/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,15 @@ function toCredentialResponse(
credentialType: 'oauth' | 'service_account' = 'oauth'
) {
const storedScope = scope?.trim()
// Some providers (e.g. Box) don't return scopes in their token response,
// so the DB column stays empty. Fall back to the configured scopes for
// the provider so the credential-selector doesn't show a false
// "Additional permissions required" banner.
/**
* Confluence reports granted scopes, so absent metadata must prompt reauthorization.
* Preserve the existing fallback for providers that omit scopes, such as Box.
*/
const scopes = storedScope
? storedScope.split(/[\s,]+/).filter(Boolean)
: getCanonicalScopesForProvider(providerId)
: providerId === 'confluence'
? []
: getCanonicalScopesForProvider(providerId)
const [_, featureType = 'default'] = providerId.split('-')

return {
Expand Down
Loading
Loading